diff --git a/.gitignore b/.gitignore
index 40c3db7..01362f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,12 +5,14 @@
*.tar.gz
*.box
*.log
-data/minceraft/*.log
-data/minceraft/ultimmc.cfg
+data/minceraft/instances/1.21.4/.minecraft/authlib-injector.log
data/minceraft/instances/1.21.4/.minecraft/logs
-data/minceraft/instances/1.21.4/.minecraft/*.log
-data/minceraft/meta*/
-data/minceraft/assets/
+data/minecraft/*
+data/minceraft/*/
+!data/minecraft/instances
+!data/minecraft/ultimmc.cfg.def
+!data/minecraft/instances/
+!data/minecraft/run_mine.sh
output/
build/
xbps-cache
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0aa1017
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+PHONY=build iso ultimmc clean
+
+build: iso
+
+clean:
+ rm -rf output
+ cp -r data/minceraft data/minceraft_tmp
+ rm -rf data/minceraft
+ mkdir data/minceraft
+ cp data/minceraft_tmp/ultimmc.cfg.def data/minceraft
+ cp -r data/minceraft_tmp/instances data/minceraft
+ cp data/minceraft_tmp/run_mine.sh data/minceraft
+ rm -rf data/minceraft_tmp
+ rm -rf ultimmc/build
+
+ultimmc: data/minceraft/UltimMC
+
+data/minceraft/UltimMC:
+ mkdir -p ultimmc/build
+ cd ultimmc/build && cmake \
+ -DCMAKE_C_COMPILER=/usr/bin/gcc \
+ -DCMAKE_CXX_COMPILER=/usr/bin/g++ \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
+ -DCMAKE_INSTALL_PREFIX:PATH=../ \
+ -DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
+ -DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
+ -DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
+ -DLauncher_LAYOUT=lin-nodeps \
+ -DLauncher_BUILD_PLATFORM=lin64 \
+ -DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
+ -DLauncher_EMBED_SECRETS=On \
+ ..
+ cd ultimmc/build && make
+ cp -a ultimmc/build/. data/minceraft/
+ rm -rf ultimmc/build
+ chmod 777 data/minceraft -R
+ echo "CLOSE MINECRAFT WHEN ASSETS ARE LOADED!!"
+ cp data/minceraft/ultimmc.cfg.def data/minceraft/ultimmc.cfg
+ cd data/minceraft && ./UltimMC -o -n Steve -l 1.21.4
+ cp data/minceraft/ultimmc.cfg.def data/minceraft/ultimmc.cfg
+
+iso: data/minceraft/UltimMC
+ sudo ./mkmine.sh
diff --git a/README.md b/README.md
index 1e95b1d..73bc38b 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,34 @@
# minceraftOS
OS that uses Minceraft as Desktop Environment. \
-Now it only launches minceraft and does nothing.
-
-## Download iso file
-
-You can get iso file of latest version here: [Latest release](https://github.com/MeexReay/minceraftOS/releases/latest) \
-But if you want to get all latest unstable changes and fixes, you need to build iso file yourself: [How to make iso file](https://github.com/MeexReay/minceraftOS#how-to-make-iso-file)
+Now it only starts minceraft and do nothing.
## How to make iso file
You need to do this from Void Linux (because the script needs XBPS to build)!
-Load minceraft assets at first:
+At first, compile ultimmc and load minceraft assets:
```
-./data/minceraft/UltimMC -l 1.21.4 # close minecraft when it's loaded
-cp data/minceraft/ultimmc.cfg.def data/minceraft/ultimmc.cfg
+make ultimmc
+
+# it will start game to load assets,
+# you just need to close the window when it will open
```
Use `mkmine.sh` script to make ISO file. \
Result will be in `output/` directory. \
-Script compiles it only for x86_64, but I think it's not really hard to make it compile for any other architectures
+Script compiles it only for x86_64, but I think it's not really hard to make it compile for any other architecture
```
-sudo ./mkmine.sh # idk why it needs sudo, please pr if you know how to remove it
+sudo ./mkmine.sh
+sudo make iso # the same but also prepares launcher
+
+# idk why it needs sudo, please pr if you know how to remove it
```
+Finally, you can forget all above and use just `sudo make`
+
## How to burn it on disk
First link in google bro
diff --git a/data/minceraft/accounts.json b/data/minceraft/accounts.json
deleted file mode 100644
index 7932612..0000000
--- a/data/minceraft/accounts.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "accounts": [
- {
- "active": true,
- "entitlement": {
- "canPlayMinecraft": true,
- "ownsMinecraft": true
- },
- "profile": {
- "capes": [
- ],
- "id": "5627dd98e6be3c21b8a8e92344183641",
- "name": "Steve",
- "skin": {
- "id": "",
- "url": "",
- "variant": ""
- }
- },
- "type": "Local",
- "ygg": {
- "extra": {
- "clientToken": "08db5b7f52414da9acae79e23aa07e63",
- "userName": "Steve"
- },
- "iat": 1741787232
- }
- }
- ],
- "formatVersion": 3
-}
diff --git a/data/minceraft/bin/UltimMC b/data/minceraft/bin/UltimMC
deleted file mode 100755
index a39756e..0000000
Binary files a/data/minceraft/bin/UltimMC and /dev/null differ
diff --git a/data/minceraft/bin/jars/JavaCheck.jar b/data/minceraft/bin/jars/JavaCheck.jar
deleted file mode 100755
index 4566498..0000000
Binary files a/data/minceraft/bin/jars/JavaCheck.jar and /dev/null differ
diff --git a/data/minceraft/bin/jars/NewLaunch.jar b/data/minceraft/bin/jars/NewLaunch.jar
deleted file mode 100755
index ac2937f..0000000
Binary files a/data/minceraft/bin/jars/NewLaunch.jar and /dev/null differ
diff --git a/data/minceraft/bin/libLauncher_iconfix.so b/data/minceraft/bin/libLauncher_iconfix.so
deleted file mode 100755
index 4b89fcb..0000000
Binary files a/data/minceraft/bin/libLauncher_iconfix.so and /dev/null differ
diff --git a/data/minceraft/bin/libLauncher_nbt++.so b/data/minceraft/bin/libLauncher_nbt++.so
deleted file mode 100755
index 1b2300c..0000000
Binary files a/data/minceraft/bin/libLauncher_nbt++.so and /dev/null differ
diff --git a/data/minceraft/bin/libLauncher_quazip.so b/data/minceraft/bin/libLauncher_quazip.so
deleted file mode 100755
index ec2fc5a..0000000
Binary files a/data/minceraft/bin/libLauncher_quazip.so and /dev/null differ
diff --git a/data/minceraft/bin/libLauncher_rainbow.so b/data/minceraft/bin/libLauncher_rainbow.so
deleted file mode 100755
index 3f5835e..0000000
Binary files a/data/minceraft/bin/libLauncher_rainbow.so and /dev/null differ
diff --git a/data/minceraft/injectors/authlib-injector-1.2.5.jar b/data/minceraft/injectors/authlib-injector-1.2.5.jar
deleted file mode 100644
index dc50e5b..0000000
Binary files a/data/minceraft/injectors/authlib-injector-1.2.5.jar and /dev/null differ
diff --git a/data/minceraft/injectors/version.json b/data/minceraft/injectors/version.json
deleted file mode 100644
index e534e1f..0000000
--- a/data/minceraft/injectors/version.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "build_number": 53,
- "version": "1.2.5",
- "release_time": "2024-02-17T17:24:35Z",
- "download_url": "https://authlib-injector.yushi.moe/artifact/53/authlib-injector-1.2.5.jar",
- "checksums": {
- "sha256": "3bc9ebdc583b36abd2a65b626c4b9f35f21177fbf42a851606eaaea3fd42ee0f"
- }
-}
diff --git a/data/minceraft/instances/1.21.4/.minecraft/authlib-injector.log b/data/minceraft/instances/1.21.4/.minecraft/authlib-injector.log
index 012ca3b..da63eed 100755
--- a/data/minceraft/instances/1.21.4/.minecraft/authlib-injector.log
+++ b/data/minceraft/instances/1.21.4/.minecraft/authlib-injector.log
@@ -1,6 +1,6 @@
-Logging started at 2025-03-12T20:00:28.230448162Z
+Logging started at 2025-03-14T01:16:18.022394035Z
[authlib-injector] [INFO] Version: 1.2.5
-[authlib-injector] [INFO] Authentication server: http://127.0.0.1:43699
+[authlib-injector] [INFO] Authentication server: http://127.0.0.1:42059
[authlib-injector] [WARNING] You are using HTTP protocol, which is INSECURE! Please switch to HTTPS if possible.
[authlib-injector] [INFO] Transformed [net.minecraft.client.main.Main] with [Main Arguments Transformer]
[authlib-injector] [INFO] Transformed [com.mojang.authlib.properties.Property] with [Yggdrasil Public Key Transformer]
diff --git a/data/minceraft/instances/1.21.4/.minecraft/options.txt b/data/minceraft/instances/1.21.4/.minecraft/options.txt
old mode 100644
new mode 100755
diff --git a/data/minceraft/instances/1.21.4/instance.cfg b/data/minceraft/instances/1.21.4/instance.cfg
index 350ed48..1e07b61 100755
--- a/data/minceraft/instances/1.21.4/instance.cfg
+++ b/data/minceraft/instances/1.21.4/instance.cfg
@@ -48,8 +48,8 @@ UseNativeGLFW=false
UseNativeOpenAL=false
WrapperCommand=
iconKey=default
-lastLaunchTime=1741809628094
-lastTimePlayed=94
+lastLaunchTime=1741914977888
+lastTimePlayed=89
name=1.21.4
notes=
-totalTimePlayed=584
+totalTimePlayed=673
diff --git a/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar b/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar
deleted file mode 100755
index 0c5e9c1..0000000
Binary files a/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar b/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar
deleted file mode 100755
index 0cb7a37..0000000
Binary files a/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar b/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar
deleted file mode 100755
index 5b653d6..0000000
Binary files a/data/minceraft/libraries/com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar b/data/minceraft/libraries/com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar
deleted file mode 100755
index 53775a5..0000000
Binary files a/data/minceraft/libraries/com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar b/data/minceraft/libraries/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar
deleted file mode 100755
index edfda76..0000000
Binary files a/data/minceraft/libraries/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar b/data/minceraft/libraries/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar
deleted file mode 100755
index 18e59c8..0000000
Binary files a/data/minceraft/libraries/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar b/data/minceraft/libraries/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar
deleted file mode 100755
index d73ab80..0000000
Binary files a/data/minceraft/libraries/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar b/data/minceraft/libraries/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar
deleted file mode 100755
index 9a88e16..0000000
Binary files a/data/minceraft/libraries/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/ibm/icu/icu4j/76.1/icu4j-76.1.jar b/data/minceraft/libraries/com/ibm/icu/icu4j/76.1/icu4j-76.1.jar
deleted file mode 100755
index 17f5845..0000000
Binary files a/data/minceraft/libraries/com/ibm/icu/icu4j/76.1/icu4j-76.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar b/data/minceraft/libraries/com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar
deleted file mode 100755
index c2f9d39..0000000
Binary files a/data/minceraft/libraries/com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/authlib/6.0.57/authlib-6.0.57.jar b/data/minceraft/libraries/com/mojang/authlib/6.0.57/authlib-6.0.57.jar
deleted file mode 100755
index 8552698..0000000
Binary files a/data/minceraft/libraries/com/mojang/authlib/6.0.57/authlib-6.0.57.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/blocklist/1.0.10/blocklist-1.0.10.jar b/data/minceraft/libraries/com/mojang/blocklist/1.0.10/blocklist-1.0.10.jar
deleted file mode 100755
index 4b5c6a9..0000000
Binary files a/data/minceraft/libraries/com/mojang/blocklist/1.0.10/blocklist-1.0.10.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar b/data/minceraft/libraries/com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar
deleted file mode 100755
index fed2379..0000000
Binary files a/data/minceraft/libraries/com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar b/data/minceraft/libraries/com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar
deleted file mode 100755
index faa58c2..0000000
Binary files a/data/minceraft/libraries/com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29-natives-linux.jar b/data/minceraft/libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29-natives-linux.jar
deleted file mode 100755
index d8edd57..0000000
Binary files a/data/minceraft/libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar b/data/minceraft/libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar
deleted file mode 100755
index 012083b..0000000
Binary files a/data/minceraft/libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/logging/1.5.10/logging-1.5.10.jar b/data/minceraft/libraries/com/mojang/logging/1.5.10/logging-1.5.10.jar
deleted file mode 100755
index 4985959..0000000
Binary files a/data/minceraft/libraries/com/mojang/logging/1.5.10/logging-1.5.10.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/minecraft/1.21.4/minecraft-1.21.4-client.jar b/data/minceraft/libraries/com/mojang/minecraft/1.21.4/minecraft-1.21.4-client.jar
deleted file mode 100755
index 776646b..0000000
Binary files a/data/minceraft/libraries/com/mojang/minecraft/1.21.4/minecraft-1.21.4-client.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/patchy/2.2.10/patchy-2.2.10.jar b/data/minceraft/libraries/com/mojang/patchy/2.2.10/patchy-2.2.10.jar
deleted file mode 100755
index e9abf8e..0000000
Binary files a/data/minceraft/libraries/com/mojang/patchy/2.2.10/patchy-2.2.10.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/mojang/text2speech/1.17.9/text2speech-1.17.9.jar b/data/minceraft/libraries/com/mojang/text2speech/1.17.9/text2speech-1.17.9.jar
deleted file mode 100755
index 716ceb0..0000000
Binary files a/data/minceraft/libraries/com/mojang/text2speech/1.17.9/text2speech-1.17.9.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/nimbusds/content-type/2.3/content-type-2.3.jar b/data/minceraft/libraries/com/nimbusds/content-type/2.3/content-type-2.3.jar
deleted file mode 100755
index 7e88769..0000000
Binary files a/data/minceraft/libraries/com/nimbusds/content-type/2.3/content-type-2.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar b/data/minceraft/libraries/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar
deleted file mode 100755
index c089707..0000000
Binary files a/data/minceraft/libraries/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar b/data/minceraft/libraries/com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar
deleted file mode 100755
index 342eee7..0000000
Binary files a/data/minceraft/libraries/com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar and /dev/null differ
diff --git a/data/minceraft/libraries/com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar b/data/minceraft/libraries/com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar
deleted file mode 100755
index e372022..0000000
Binary files a/data/minceraft/libraries/com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar and /dev/null differ
diff --git a/data/minceraft/libraries/commons-codec/commons-codec/1.17.1/commons-codec-1.17.1.jar b/data/minceraft/libraries/commons-codec/commons-codec/1.17.1/commons-codec-1.17.1.jar
deleted file mode 100755
index 5023670..0000000
Binary files a/data/minceraft/libraries/commons-codec/commons-codec/1.17.1/commons-codec-1.17.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/commons-io/commons-io/2.17.0/commons-io-2.17.0.jar b/data/minceraft/libraries/commons-io/commons-io/2.17.0/commons-io-2.17.0.jar
deleted file mode 100755
index ad00ddc..0000000
Binary files a/data/minceraft/libraries/commons-io/commons-io/2.17.0/commons-io-2.17.0.jar and /dev/null differ
diff --git a/data/minceraft/libraries/commons-logging/commons-logging/1.3.4/commons-logging-1.3.4.jar b/data/minceraft/libraries/commons-logging/commons-logging/1.3.4/commons-logging-1.3.4.jar
deleted file mode 100755
index b6339bb..0000000
Binary files a/data/minceraft/libraries/commons-logging/commons-logging/1.3.4/commons-logging-1.3.4.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar
deleted file mode 100755
index 5372981..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar
deleted file mode 100755
index 342629f..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar
deleted file mode 100755
index 6b610ea..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar
deleted file mode 100755
index 30a7a35..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar
deleted file mode 100755
index 63d1b3e..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar
deleted file mode 100755
index 84e06bc..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar b/data/minceraft/libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar
deleted file mode 100755
index 5d5f754..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar b/data/minceraft/libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar
deleted file mode 100755
index ce021e5..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar
deleted file mode 100755
index 78db923..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar b/data/minceraft/libraries/io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar
deleted file mode 100755
index 3a64746..0000000
Binary files a/data/minceraft/libraries/io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar and /dev/null differ
diff --git a/data/minceraft/libraries/it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar b/data/minceraft/libraries/it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar
deleted file mode 100755
index 2e03f48..0000000
Binary files a/data/minceraft/libraries/it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar and /dev/null differ
diff --git a/data/minceraft/libraries/net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar b/data/minceraft/libraries/net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar
deleted file mode 100755
index 645b692..0000000
Binary files a/data/minceraft/libraries/net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar and /dev/null differ
diff --git a/data/minceraft/libraries/net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar b/data/minceraft/libraries/net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar
deleted file mode 100755
index a216935..0000000
Binary files a/data/minceraft/libraries/net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar and /dev/null differ
diff --git a/data/minceraft/libraries/net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar b/data/minceraft/libraries/net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar
deleted file mode 100755
index de9487c..0000000
Binary files a/data/minceraft/libraries/net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar b/data/minceraft/libraries/net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar
deleted file mode 100755
index 2842d99..0000000
Binary files a/data/minceraft/libraries/net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar b/data/minceraft/libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar
deleted file mode 100755
index 317b2b0..0000000
Binary files a/data/minceraft/libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/commons/commons-compress/1.27.1/commons-compress-1.27.1.jar b/data/minceraft/libraries/org/apache/commons/commons-compress/1.27.1/commons-compress-1.27.1.jar
deleted file mode 100755
index 1bea2d9..0000000
Binary files a/data/minceraft/libraries/org/apache/commons/commons-compress/1.27.1/commons-compress-1.27.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar b/data/minceraft/libraries/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar
deleted file mode 100755
index f6486b4..0000000
Binary files a/data/minceraft/libraries/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/httpcomponents/httpclient/4.5.14/httpclient-4.5.14.jar b/data/minceraft/libraries/org/apache/httpcomponents/httpclient/4.5.14/httpclient-4.5.14.jar
deleted file mode 100755
index 2bb7c07..0000000
Binary files a/data/minceraft/libraries/org/apache/httpcomponents/httpclient/4.5.14/httpclient-4.5.14.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/httpcomponents/httpcore/4.4.16/httpcore-4.4.16.jar b/data/minceraft/libraries/org/apache/httpcomponents/httpcore/4.4.16/httpcore-4.4.16.jar
deleted file mode 100755
index f0bdebe..0000000
Binary files a/data/minceraft/libraries/org/apache/httpcomponents/httpcore/4.4.16/httpcore-4.4.16.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar b/data/minceraft/libraries/org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar
deleted file mode 100755
index f9d5b43..0000000
Binary files a/data/minceraft/libraries/org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar b/data/minceraft/libraries/org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar
deleted file mode 100755
index ea8eefa..0000000
Binary files a/data/minceraft/libraries/org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar b/data/minceraft/libraries/org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar
deleted file mode 100755
index 0a914b9..0000000
Binary files a/data/minceraft/libraries/org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/jcraft/jorbis/0.0.17/jorbis-0.0.17.jar b/data/minceraft/libraries/org/jcraft/jorbis/0.0.17/jorbis-0.0.17.jar
deleted file mode 100755
index e58a6aa..0000000
Binary files a/data/minceraft/libraries/org/jcraft/jorbis/0.0.17/jorbis-0.0.17.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/joml/joml/1.10.8/joml-1.10.8.jar b/data/minceraft/libraries/org/joml/joml/1.10.8/joml-1.10.8.jar
deleted file mode 100755
index ddcad59..0000000
Binary files a/data/minceraft/libraries/org/joml/joml/1.10.8/joml-1.10.8.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3-natives-linux.jar
deleted file mode 100755
index caa2984..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3.jar
deleted file mode 100755
index 9fcdfa9..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3-natives-linux.jar
deleted file mode 100755
index 28db301..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3.jar
deleted file mode 100755
index 791fe06..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3-natives-linux.jar
deleted file mode 100755
index c72f93a..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3.jar
deleted file mode 100755
index 6881c26..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3-natives-linux.jar
deleted file mode 100755
index 46252b4..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3.jar
deleted file mode 100755
index 2a60334..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3-natives-linux.jar
deleted file mode 100755
index b4198d5..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3.jar
deleted file mode 100755
index 9905636..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3-natives-linux.jar
deleted file mode 100755
index b8ccae2..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3.jar
deleted file mode 100755
index ba65bed..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3-natives-linux.jar
deleted file mode 100755
index df5d573..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3.jar
deleted file mode 100755
index 7fbd1bf..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3-natives-linux.jar b/data/minceraft/libraries/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3-natives-linux.jar
deleted file mode 100755
index 68018d8..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3-natives-linux.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3.jar b/data/minceraft/libraries/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3.jar
deleted file mode 100755
index 2d1fcf9..0000000
Binary files a/data/minceraft/libraries/org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar b/data/minceraft/libraries/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar
deleted file mode 100755
index 89c644b..0000000
Binary files a/data/minceraft/libraries/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/ow2/asm/asm/9.6/asm-9.6.jar b/data/minceraft/libraries/org/ow2/asm/asm/9.6/asm-9.6.jar
deleted file mode 100755
index cc1c2cd..0000000
Binary files a/data/minceraft/libraries/org/ow2/asm/asm/9.6/asm-9.6.jar and /dev/null differ
diff --git a/data/minceraft/libraries/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar b/data/minceraft/libraries/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar
deleted file mode 100755
index cbb5448..0000000
Binary files a/data/minceraft/libraries/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar and /dev/null differ
diff --git a/data/minceraft/metacache b/data/minceraft/metacache
deleted file mode 100755
index 1146eb4..0000000
--- a/data/minceraft/metacache
+++ /dev/null
@@ -1,645 +0,0 @@
-{
- "entries": [
- {
- "base": "asset_indexes",
- "etag": "",
- "last_changed_timestamp": 1741809420953,
- "md5sum": "d5a9c66300c0d7eacd8293b62a5986a8",
- "path": "19.json",
- "remote_changed_timestamp": "Wed, 12 Mar 2025 12:46:52 GMT"
- },
- {
- "base": "injectors",
- "etag": "\"65d0ebf9-537d2\"",
- "last_changed_timestamp": 1741787699920,
- "md5sum": "c60d3899b711537e10be33c680ebd8ae",
- "path": "authlib-injector-1.2.5.jar",
- "remote_changed_timestamp": "Sat, 17 Feb 2024 17:25:13 GMT"
- },
- {
- "base": "injectors",
- "etag": "W/\"65d0ebf9-11f\"",
- "last_changed_timestamp": 1741787699583,
- "md5sum": "e206374eedab95eef76581f3e5c26867",
- "path": "version.json",
- "remote_changed_timestamp": "Sat, 17 Feb 2024 17:25:13 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC80A2DCB5773E",
- "last_changed_timestamp": 1741807485215,
- "md5sum": "7e982dafcf25c24b365b5088eaf8a0a6",
- "path": "com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar",
- "remote_changed_timestamp": "Thu, 30 May 2024 12:20:15 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC80A2DC6C8B4B",
- "last_changed_timestamp": 1741807485216,
- "md5sum": "e8f064827ddb8deb06e45d20988bcaa5",
- "path": "com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar",
- "remote_changed_timestamp": "Thu, 30 May 2024 12:20:15 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC80A2DC4C0872",
- "last_changed_timestamp": 1741807485222,
- "md5sum": "f26eab82fa1da09f8e8bc7e52fc82088",
- "path": "com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar",
- "remote_changed_timestamp": "Thu, 30 May 2024 12:20:14 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C56DAFC7C",
- "last_changed_timestamp": 1741807485225,
- "md5sum": "e4e15667573ea6a967d0171289d59e9c",
- "path": "com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:58 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC80A2DBDE58B1",
- "last_changed_timestamp": 1741807485225,
- "md5sum": "d62dbfa8789378457ada685e2f614846",
- "path": "com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar",
- "remote_changed_timestamp": "Thu, 30 May 2024 12:20:14 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C584D3738",
- "last_changed_timestamp": 1741807485227,
- "md5sum": "0c69b9199d3a4e6c34dc03619ff7feee",
- "path": "com/google/code/gson/gson/2.11.0/gson-2.11.0.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:06:01 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C58333CC5",
- "last_changed_timestamp": 1741807485227,
- "md5sum": "3f75955b49b6758fd6d1e1bd9bf777b3",
- "path": "com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:06:01 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C57E1334F",
- "last_changed_timestamp": 1741807485237,
- "md5sum": "7b7d80d99af4181db55b00dad50a91bb",
- "path": "com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:06:00 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C569016F6",
- "last_changed_timestamp": 1741807485290,
- "md5sum": "0621976c76a3b05b0622aef5a4c1d981",
- "path": "com/ibm/icu/icu4j/76.1/icu4j-76.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:58 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C59ABD8EF",
- "last_changed_timestamp": 1741807485294,
- "md5sum": "af37c5d9f4d839c78a103c866ea2bfe4",
- "path": "com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:06:03 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DCF8C600B13DA5",
- "last_changed_timestamp": 1741807485294,
- "md5sum": "f6c1f2b733258d1dc74533cbad0da8f6",
- "path": "com/mojang/authlib/6.0.57/authlib-6.0.57.jar",
- "remote_changed_timestamp": "Wed, 30 Oct 2024 09:34:07 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DAB7EC4634A6BF",
- "last_changed_timestamp": 1741807485294,
- "md5sum": "fc1420e3182dd32b4df9933f810ebebb",
- "path": "com/mojang/blocklist/1.0.10/blocklist-1.0.10.jar",
- "remote_changed_timestamp": "Thu, 27 Oct 2022 07:24:24 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DCB6E7544D8099",
- "last_changed_timestamp": 1741807485295,
- "md5sum": "a755b426eb7942bb74b46a95b02f1de4",
- "path": "com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar",
- "remote_changed_timestamp": "Wed, 07 Aug 2024 13:46:24 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC6E7281CD0C4C",
- "last_changed_timestamp": 1741807485297,
- "md5sum": "d932ac637b6d83e6c45a8f269fe81e3b",
- "path": "com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar",
- "remote_changed_timestamp": "Tue, 07 May 2024 08:48:46 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DCCDAC732329E6",
- "last_changed_timestamp": 1741807485298,
- "md5sum": "b8dc2f815666609419b80263d7a34969",
- "path": "com/mojang/jtracy/1.0.29/jtracy-1.0.29-natives-linux.jar",
- "remote_changed_timestamp": "Thu, 05 Sep 2024 13:12:52 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DCCDAC724C594D",
- "last_changed_timestamp": 1741807485298,
- "md5sum": "1c06cd97d006339f58085cdfd8065000",
- "path": "com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar",
- "remote_changed_timestamp": "Thu, 05 Sep 2024 13:12:51 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD04C20161983D",
- "last_changed_timestamp": 1741807485298,
- "md5sum": "0b7da24cea4e840b91c13ed08657a6d3",
- "path": "com/mojang/logging/1.5.10/logging-1.5.10.jar",
- "remote_changed_timestamp": "Thu, 14 Nov 2024 15:35:44 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD1384AF4AC9E1",
- "last_changed_timestamp": 1741807485397,
- "md5sum": "70e2838411853210dce14bdd30769458",
- "path": "com/mojang/minecraft/1.21.4/minecraft-1.21.4-client.jar",
- "remote_changed_timestamp": "Tue, 03 Dec 2024 10:24:35 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DAB7EC4DC28215",
- "last_changed_timestamp": 1741807485400,
- "md5sum": "ff905bf0aacf501149a13880a2d6742d",
- "path": "com/mojang/patchy/2.2.10/patchy-2.2.10.jar",
- "remote_changed_timestamp": "Thu, 27 Oct 2022 07:24:37 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DB5528647B683F",
- "last_changed_timestamp": 1741807485400,
- "md5sum": "f5b05e8db22e2e0668b786e11ac9d3ce",
- "path": "com/mojang/text2speech/1.17.9/text2speech-1.17.9.jar",
- "remote_changed_timestamp": "Mon, 15 May 2023 09:40:17 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC80A2DCECEAB3",
- "last_changed_timestamp": 1741807485400,
- "md5sum": "f0fc0d6be73e838863e2197c03a27c3f",
- "path": "com/nimbusds/content-type/2.3/content-type-2.3.jar",
- "remote_changed_timestamp": "Thu, 30 May 2024 12:20:15 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC80A2DD4E9A00",
- "last_changed_timestamp": 1741807485401,
- "md5sum": "31b8a4f76fdbf21f1d667f9d6618e0b2",
- "path": "com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar",
- "remote_changed_timestamp": "Thu, 30 May 2024 12:20:16 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C596A8593",
- "last_changed_timestamp": 1741807485403,
- "md5sum": "42ce81c8d034f163663d23e8bbc3638d",
- "path": "com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:06:03 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C58FA679B",
- "last_changed_timestamp": 1741807485406,
- "md5sum": "7ea0239e0e285c7625964893dcba95ec",
- "path": "com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:06:02 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C5466CBEB",
- "last_changed_timestamp": 1741807485407,
- "md5sum": "7b3438ab4c6d91e0066d410947e43f3e",
- "path": "commons-codec/commons-codec/1.17.1/commons-codec-1.17.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:54 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C542DACB3",
- "last_changed_timestamp": 1741807485409,
- "md5sum": "f6232d0e290d58bb93f74f67165bf91f",
- "path": "commons-io/commons-io/2.17.0/commons-io-2.17.0.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:54 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C53E22B96",
- "last_changed_timestamp": 1741807485409,
- "md5sum": "e7a1e7cb6a89241ed9bfec4c25b6c645",
- "path": "commons-logging/commons-logging/1.3.4/commons-logging-1.3.4.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:53 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C4EE1F06E",
- "last_changed_timestamp": 1741807485411,
- "md5sum": "c4ddfa85fddc7cdd84ef38c87c036010",
- "path": "io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:45 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C500F0B81",
- "last_changed_timestamp": 1741807485412,
- "md5sum": "5391594c6f5bbdd944e3e8bcecf3d9ea",
- "path": "io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:47 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C5054C6B8",
- "last_changed_timestamp": 1741807485414,
- "md5sum": "6241a4cfb9c478bbd7aa12512b90735d",
- "path": "io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:47 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C5120CE15",
- "last_changed_timestamp": 1741807485416,
- "md5sum": "2a752fae3646b70f7bd17a2265c788ed",
- "path": "io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:49 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C50A06EB6",
- "last_changed_timestamp": 1741807485416,
- "md5sum": "e133793fdcb3ea2846693f1de1d31906",
- "path": "io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:48 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C4FBF9735",
- "last_changed_timestamp": 1741807485417,
- "md5sum": "9cbf83e1fe1dcc8c92075196f6f0f88c",
- "path": "io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:47 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C4F17435B",
- "last_changed_timestamp": 1741807485417,
- "md5sum": "e76aa9d835b3dceb7004d644d6f4badc",
- "path": "io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:45 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C4F21711B",
- "last_changed_timestamp": 1741807485417,
- "md5sum": "1949f9a395ac49f303c99251c4710844",
- "path": "io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:45 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C50C9736A",
- "last_changed_timestamp": 1741807485418,
- "md5sum": "c5714e7bc9bbd800a52d8d6c145b19e2",
- "path": "io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:48 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C4F7070B4",
- "last_changed_timestamp": 1741807485419,
- "md5sum": "c2da3befce20eaf33fa8005e0797e03a",
- "path": "io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:46 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C565131DC",
- "last_changed_timestamp": 1741807485508,
- "md5sum": "da830fa5023a010d2c2af1484d13cefc",
- "path": "it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:58 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C555A2C31",
- "last_changed_timestamp": 1741807485513,
- "md5sum": "41d91e4a13428fb79c12024cb92a4091",
- "path": "net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:56 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C5594A96F",
- "last_changed_timestamp": 1741807485518,
- "md5sum": "cd756a719c1892e56d9c9d424e8983bb",
- "path": "net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:56 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C550676F3",
- "last_changed_timestamp": 1741807485518,
- "md5sum": "51e60dbf9ac51f6666f0077317990944",
- "path": "net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:55 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C549AC0D3",
- "last_changed_timestamp": 1741807485519,
- "md5sum": "88a65001b616c2e7796f9263ad97bbf1",
- "path": "net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:55 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DAB7EC4420E1F8",
- "last_changed_timestamp": 1741807485519,
- "md5sum": "eb0d9dffe9b0eddead68fe678be76c49",
- "path": "net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar",
- "remote_changed_timestamp": "Thu, 27 Oct 2022 07:24:20 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C52C9451A",
- "last_changed_timestamp": 1741807485523,
- "md5sum": "1db4bd87b0082044c6e7a6af0b977a3e",
- "path": "org/apache/commons/commons-compress/1.27.1/commons-compress-1.27.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:52 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C5261822A",
- "last_changed_timestamp": 1741807485525,
- "md5sum": "7730df72b7fdff4a3a32d89a314f826a",
- "path": "org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:51 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C530326C3",
- "last_changed_timestamp": 1741807485528,
- "md5sum": "2cb357c4b763f47e58af6cad47df6ba3",
- "path": "org/apache/httpcomponents/httpclient/4.5.14/httpclient-4.5.14.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:52 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DBA8624587D567",
- "last_changed_timestamp": 1741807485529,
- "md5sum": "28d2cd9bf8789fd2ec774fb88436ebd1",
- "path": "org/apache/httpcomponents/httpcore/4.4.16/httpcore-4.4.16.jar",
- "remote_changed_timestamp": "Tue, 29 Aug 2023 07:33:42 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C520A2785",
- "last_changed_timestamp": 1741807485531,
- "md5sum": "4dac8bc9a21f3503cd1a856503b6fea0",
- "path": "org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:50 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C519C77D6",
- "last_changed_timestamp": 1741807485537,
- "md5sum": "fd982d71f36250bc31528fc7e3d0807d",
- "path": "org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:50 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C51E6E8AE",
- "last_changed_timestamp": 1741807485538,
- "md5sum": "033362ddeac79ba54fdc33cc2004d445",
- "path": "org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:50 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC3A103D72187B",
- "last_changed_timestamp": 1741807485538,
- "md5sum": "4794379b6074b962bb4cab21bfdb8d9c",
- "path": "org/jcraft/jorbis/0.0.17/jorbis-0.0.17.jar",
- "remote_changed_timestamp": "Fri, 01 Mar 2024 16:54:20 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C513A7AC1",
- "last_changed_timestamp": 1741807485541,
- "md5sum": "58a4c6e7475f1121bfd390a819e62c98",
- "path": "org/joml/joml/1.10.8/joml-1.10.8.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:49 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C44385BF",
- "last_changed_timestamp": 1741807485543,
- "md5sum": "7da8d8e06e7315c6c877ae5e542407b6",
- "path": "org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:50 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C4FB9761",
- "last_changed_timestamp": 1741807485544,
- "md5sum": "4506c5f102d10aab0dae80214579352c",
- "path": "org/lwjgl/lwjgl-freetype/3.3.3/lwjgl-freetype-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:52 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C52734E1",
- "last_changed_timestamp": 1741807485545,
- "md5sum": "9b31da2f80cbbdc6d76b72c8ac2cf943",
- "path": "org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:52 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C5BF8E23",
- "last_changed_timestamp": 1741807485545,
- "md5sum": "41c1287c1219fd0b67e8d51879f4c812",
- "path": "org/lwjgl/lwjgl-glfw/3.3.3/lwjgl-glfw-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:53 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C6B5B12A",
- "last_changed_timestamp": 1741807485546,
- "md5sum": "a3867865fad43f2ccb6b20dea405b3c8",
- "path": "org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:55 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C69FBB55",
- "last_changed_timestamp": 1741807485546,
- "md5sum": "a8ffc7d8a0d54981f1d7b5c2a40acf01",
- "path": "org/lwjgl/lwjgl-jemalloc/3.3.3/lwjgl-jemalloc-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:54 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C329B354",
- "last_changed_timestamp": 1741807485547,
- "md5sum": "82bec56d8f88b000b29550c5946bada4",
- "path": "org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:49 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C30519A1",
- "last_changed_timestamp": 1741807485547,
- "md5sum": "3ae8606b16891af57eb08ed5a6f78ed8",
- "path": "org/lwjgl/lwjgl-openal/3.3.3/lwjgl-openal-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:48 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C68A13A2",
- "last_changed_timestamp": 1741807485548,
- "md5sum": "de05e5c258591d07d294a9c16096a383",
- "path": "org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:54 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C5FAB9C7",
- "last_changed_timestamp": 1741807485551,
- "md5sum": "d5a85fe9c675ff040197e2e2dd694fc1",
- "path": "org/lwjgl/lwjgl-opengl/3.3.3/lwjgl-opengl-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:53 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C7BFCE76",
- "last_changed_timestamp": 1741807485551,
- "md5sum": "23a604402c5e9527ecce65e66086308a",
- "path": "org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:56 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C74FDEE5",
- "last_changed_timestamp": 1741807485552,
- "md5sum": "11b824be2cd8532eb6ef063ada6a75bd",
- "path": "org/lwjgl/lwjgl-stb/3.3.3/lwjgl-stb-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:56 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C822EEA1",
- "last_changed_timestamp": 1741807485552,
- "md5sum": "36797f4097de3e127cefc1ed5aa09f02",
- "path": "org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:57 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C82D9B79",
- "last_changed_timestamp": 1741807485552,
- "md5sum": "c687eaba9debbc609df72ab45f4e1164",
- "path": "org/lwjgl/lwjgl-tinyfd/3.3.3/lwjgl-tinyfd-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:57 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C3F808CE",
- "last_changed_timestamp": 1741807485552,
- "md5sum": "be04104b73f6154ceb5fdfa651e07e84",
- "path": "org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3-natives-linux.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:50 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC4CD0C40748DB",
- "last_changed_timestamp": 1741807485555,
- "md5sum": "d89fce0be944d8cffd1f82c6546509bf",
- "path": "org/lwjgl/lwjgl/3.3.3/lwjgl-3.3.3.jar",
- "remote_changed_timestamp": "Mon, 25 Mar 2024 13:37:50 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DC1CC20CCAC2F0",
- "last_changed_timestamp": 1741807485557,
- "md5sum": "936a927700aa8fc3b75d21d7571171f6",
- "path": "org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar",
- "remote_changed_timestamp": "Wed, 24 Jan 2024 09:51:34 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C536B109B",
- "last_changed_timestamp": 1741807485557,
- "md5sum": "6f8bccf756f170d4185bb24c8c2d2020",
- "path": "org/ow2/asm/asm/9.6/asm-9.6.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:53 GMT"
- },
- {
- "base": "libraries",
- "etag": "0x8DD054C534B9E10",
- "last_changed_timestamp": 1741807485558,
- "md5sum": "c8de8f5d740584cb24b5652cfba8b3c4",
- "path": "org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar",
- "remote_changed_timestamp": "Fri, 15 Nov 2024 08:05:52 GMT"
- },
- {
- "base": "meta",
- "etag": "W/\"67d1dabf-6b3\"",
- "last_changed_timestamp": 1741809417677,
- "md5sum": "cf2ef84d8b941ed397e73ad3f99b1515",
- "path": "index.json",
- "remote_changed_timestamp": "Wed, 12 Mar 2025 19:04:31 GMT"
- },
- {
- "base": "meta",
- "etag": "W/\"67d1dabf-10be2\"",
- "last_changed_timestamp": 1741809418082,
- "md5sum": "b2cbeddaca7ad3c3219dfbe6cead381e",
- "path": "net.minecraft/1.21.4.json",
- "remote_changed_timestamp": "Wed, 12 Mar 2025 19:04:31 GMT"
- },
- {
- "base": "meta",
- "etag": "W/\"67d1b06d-4f79c\"",
- "last_changed_timestamp": 1741796189155,
- "md5sum": "947e142002eecb37abae388519809f17",
- "path": "net.minecraft/index.json",
- "remote_changed_timestamp": "Wed, 12 Mar 2025 16:03:57 GMT"
- },
- {
- "base": "root",
- "etag": "\"550e6bb5-356\"",
- "last_changed_timestamp": 1741787183638,
- "md5sum": "a8a8094545267c76ad51fe276b0e9e8f",
- "path": "notifications.json",
- "remote_changed_timestamp": "Sun, 22 Mar 2015 07:13:57 GMT"
- },
- {
- "base": "translations",
- "etag": "\"678d1f1a-3e3b\"",
- "last_changed_timestamp": 1741807485558,
- "md5sum": "e9a6cb5c7168458327f5ea6c5f0b6ebc",
- "path": "index_v2.json",
- "remote_changed_timestamp": "Sun, 19 Jan 2025 15:49:46 GMT"
- },
- {
- "base": "translations",
- "etag": "\"678d1f1a-22df3\"",
- "last_changed_timestamp": 1741807485559,
- "md5sum": "8dd856913c4c78006bc4c5ddafba720b",
- "path": "mmc_ru.qm",
- "remote_changed_timestamp": "Sun, 19 Jan 2025 15:49:46 GMT"
- }
- ],
- "version": "1"
-}
diff --git a/data/minceraft/themes/custom/theme.json b/data/minceraft/themes/custom/theme.json
deleted file mode 100644
index fc6cbd7..0000000
--- a/data/minceraft/themes/custom/theme.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "colors": {
- "AlternateBase": "#31363b",
- "Base": "#232629",
- "BrightText": "#ff0000",
- "Button": "#31363b",
- "ButtonText": "#ffffff",
- "Highlight": "#2a82da",
- "HighlightedText": "#000000",
- "Link": "#2a82da",
- "Text": "#ffffff",
- "ToolTipBase": "#ffffff",
- "ToolTipText": "#ffffff",
- "Window": "#31363b",
- "WindowText": "#ffffff",
- "fadeAmount": 0.5,
- "fadeColor": "#31363b"
- },
- "name": "Custom",
- "widgets": "Fusion"
-}
diff --git a/data/minceraft/themes/custom/themeStyle.css b/data/minceraft/themes/custom/themeStyle.css
deleted file mode 100644
index 9fce8ae..0000000
--- a/data/minceraft/themes/custom/themeStyle.css
+++ /dev/null
@@ -1 +0,0 @@
-QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }
\ No newline at end of file
diff --git a/data/minceraft/translations/index_v2.json b/data/minceraft/translations/index_v2.json
deleted file mode 100755
index dd69a47..0000000
--- a/data/minceraft/translations/index_v2.json
+++ /dev/null
@@ -1,454 +0,0 @@
-{
- "file_type" : "MMC-TRANSLATION-INDEX",
- "version" : 2,
- "languages" : {
- "ar" : {
- "file" : "eb6a15e60a23901ea6a8005d8e9125bed4922f3d.class",
- "sha1" : "eb6a15e60a23901ea6a8005d8e9125bed4922f3d",
- "size" : 126775,
- "translated" : 988,
- "fuzzy" : 0,
- "untranslated" : 105
- },
- "be" : {
- "file" : "339e0b746e7908755f426b0d1c350acb072079d8.class",
- "sha1" : "339e0b746e7908755f426b0d1c350acb072079d8",
- "size" : 140090,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "bg" : {
- "file" : "b6395a0c931c2e2a880224cee74d01fa969467c1.class",
- "sha1" : "b6395a0c931c2e2a880224cee74d01fa969467c1",
- "size" : 112989,
- "translated" : 816,
- "fuzzy" : 0,
- "untranslated" : 277
- },
- "ca" : {
- "file" : "57cdcee61737e6c0f7218899558e67fecea9e262.class",
- "sha1" : "57cdcee61737e6c0f7218899558e67fecea9e262",
- "size" : 141064,
- "translated" : 975,
- "fuzzy" : 0,
- "untranslated" : 118
- },
- "cs" : {
- "file" : "8b2ecbc4e2a439f27bdcaab5b2189c02cbcd079c.class",
- "sha1" : "8b2ecbc4e2a439f27bdcaab5b2189c02cbcd079c",
- "size" : 138537,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "cy" : {
- "file" : "5c65378e6b7933dbf45ed7dd2a0deb0836d45de9.class",
- "sha1" : "5c65378e6b7933dbf45ed7dd2a0deb0836d45de9",
- "size" : 76254,
- "translated" : 551,
- "fuzzy" : 0,
- "untranslated" : 542
- },
- "da" : {
- "file" : "6fd5896ee6ed59f6803a446a8445b2f4e236268b.class",
- "sha1" : "6fd5896ee6ed59f6803a446a8445b2f4e236268b",
- "size" : 66801,
- "translated" : 550,
- "fuzzy" : 0,
- "untranslated" : 543
- },
- "de" : {
- "file" : "144b5c6ceb391a6b2e196356344d7e9abca97b88.class",
- "sha1" : "144b5c6ceb391a6b2e196356344d7e9abca97b88",
- "size" : 147827,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "de_CH" : {
- "file" : "df553b69076153b1b9e0dcd045ebaf614a1703bc.class",
- "sha1" : "df553b69076153b1b9e0dcd045ebaf614a1703bc",
- "size" : 147899,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "el" : {
- "file" : "48fdfc6fef33430de278a80a6a47db38e21bb78f.class",
- "sha1" : "48fdfc6fef33430de278a80a6a47db38e21bb78f",
- "size" : 142770,
- "translated" : 986,
- "fuzzy" : 0,
- "untranslated" : 107
- },
- "en_GB" : {
- "file" : "b1e7c270b460947711de28fbc7e6eabf292db4e0.class",
- "sha1" : "b1e7c270b460947711de28fbc7e6eabf292db4e0",
- "size" : 136327,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "eo" : {
- "file" : "b3386dfd7d2c263598ebaf1950eb94e1cf20a5ab.class",
- "sha1" : "b3386dfd7d2c263598ebaf1950eb94e1cf20a5ab",
- "size" : 24371,
- "translated" : 218,
- "fuzzy" : 0,
- "untranslated" : 875
- },
- "es" : {
- "file" : "c98f678f9356e19ef6b8017b40a3c92de9643f07.class",
- "sha1" : "c98f678f9356e19ef6b8017b40a3c92de9643f07",
- "size" : 155344,
- "translated" : 1074,
- "fuzzy" : 0,
- "untranslated" : 19
- },
- "es_UY" : {
- "file" : "2e7156bb88c2bec8a0ed2c010aba81b822f1cd6d.class",
- "sha1" : "2e7156bb88c2bec8a0ed2c010aba81b822f1cd6d",
- "size" : 148293,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "et" : {
- "file" : "e14f350b7de44997805853f859a0451a16ac483f.class",
- "sha1" : "e14f350b7de44997805853f859a0451a16ac483f",
- "size" : 110023,
- "translated" : 881,
- "fuzzy" : 0,
- "untranslated" : 212
- },
- "fa" : {
- "file" : "756c328866b85302445ec1ae55e11b4c10831fda.class",
- "sha1" : "756c328866b85302445ec1ae55e11b4c10831fda",
- "size" : 135579,
- "translated" : 1020,
- "fuzzy" : 0,
- "untranslated" : 73
- },
- "fi" : {
- "file" : "bdc7421f620fced85d84fd05ce7a2ef9eb89c3e1.class",
- "sha1" : "bdc7421f620fced85d84fd05ce7a2ef9eb89c3e1",
- "size" : 141197,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "fr" : {
- "file" : "c3172e77e068e615c3bf4a447361f4b7ef0da764.class",
- "sha1" : "c3172e77e068e615c3bf4a447361f4b7ef0da764",
- "size" : 164857,
- "translated" : 1093,
- "fuzzy" : 0,
- "untranslated" : 0
- },
- "gl" : {
- "file" : "495c4cc380a80fa25163a2a2622f2643414831f0.class",
- "sha1" : "495c4cc380a80fa25163a2a2622f2643414831f0",
- "size" : 156017,
- "translated" : 1093,
- "fuzzy" : 0,
- "untranslated" : 0
- },
- "he" : {
- "file" : "5c4fac508c9c85f001980d11c6a6b64d26c4c510.class",
- "sha1" : "5c4fac508c9c85f001980d11c6a6b64d26c4c510",
- "size" : 127071,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "hu" : {
- "file" : "37bd219c4a50c12ba70a0108f5fb5153731ee3a0.class",
- "sha1" : "37bd219c4a50c12ba70a0108f5fb5153731ee3a0",
- "size" : 144644,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "hy" : {
- "file" : "2b6b042c110594ec1e8af5c77ba9e91d34030c3e.class",
- "sha1" : "2b6b042c110594ec1e8af5c77ba9e91d34030c3e",
- "size" : 6603,
- "translated" : 102,
- "fuzzy" : 0,
- "untranslated" : 991
- },
- "id" : {
- "file" : "a6e0bf75acff59c31c49265b5414170f50e0e2e5.class",
- "sha1" : "a6e0bf75acff59c31c49265b5414170f50e0e2e5",
- "size" : 135268,
- "translated" : 984,
- "fuzzy" : 0,
- "untranslated" : 109
- },
- "is" : {
- "file" : "061661de40508f2b5c044bbf7df314a2897768a4.class",
- "sha1" : "061661de40508f2b5c044bbf7df314a2897768a4",
- "size" : 11020,
- "translated" : 145,
- "fuzzy" : 0,
- "untranslated" : 948
- },
- "it" : {
- "file" : "f91c8470d31c6485417df6840c2638a21a80f8b1.class",
- "sha1" : "f91c8470d31c6485417df6840c2638a21a80f8b1",
- "size" : 153828,
- "translated" : 1061,
- "fuzzy" : 0,
- "untranslated" : 32
- },
- "ja" : {
- "file" : "c8cb1d373557a58e5e85c5fc628f40233660dcf3.class",
- "sha1" : "c8cb1d373557a58e5e85c5fc628f40233660dcf3",
- "size" : 110002,
- "translated" : 1016,
- "fuzzy" : 0,
- "untranslated" : 77
- },
- "ja_KANJI" : {
- "file" : "509c74b64179f0bac0d07c794b3f2ac73d8c8b00.class",
- "sha1" : "509c74b64179f0bac0d07c794b3f2ac73d8c8b00",
- "size" : 107852,
- "translated" : 997,
- "fuzzy" : 0,
- "untranslated" : 96
- },
- "jv" : {
- "file" : "4884fd9af6890647b7af1aefa57f38cca49ad899.class",
- "sha1" : "4884fd9af6890647b7af1aefa57f38cca49ad899",
- "size" : 16,
- "translated" : 0,
- "fuzzy" : 0,
- "untranslated" : 1030
- },
- "ka" : {
- "file" : "f50883cad9dd7abbadba01a4303cfd1014ae2b21.class",
- "sha1" : "f50883cad9dd7abbadba01a4303cfd1014ae2b21",
- "size" : 34461,
- "translated" : 253,
- "fuzzy" : 0,
- "untranslated" : 840
- },
- "kn" : {
- "file" : "2aa2d42c51f9cf024e3777f0dde4270388fd22ae.class",
- "sha1" : "2aa2d42c51f9cf024e3777f0dde4270388fd22ae",
- "size" : 23,
- "translated" : 0,
- "fuzzy" : 0,
- "untranslated" : 1030
- },
- "ko" : {
- "file" : "5e5506a3c2ea90e1c396b54f070a2620f20fbe30.class",
- "sha1" : "5e5506a3c2ea90e1c396b54f070a2620f20fbe30",
- "size" : 112422,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "lb" : {
- "file" : "e588e4755a1e407314bf3753572744514e04bea6.class",
- "sha1" : "e588e4755a1e407314bf3753572744514e04bea6",
- "size" : 21524,
- "translated" : 152,
- "fuzzy" : 0,
- "untranslated" : 941
- },
- "lt" : {
- "file" : "969a032f2aca58c6cd8def04aed39763e923077b.class",
- "sha1" : "969a032f2aca58c6cd8def04aed39763e923077b",
- "size" : 81459,
- "translated" : 701,
- "fuzzy" : 0,
- "untranslated" : 392
- },
- "lv" : {
- "file" : "4453f6a649d5a079b116aa742bac2207a796860c.class",
- "sha1" : "4453f6a649d5a079b116aa742bac2207a796860c",
- "size" : 139731,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "mk" : {
- "file" : "5bd1feff346ff9e945079856554d174075d5f5c7.class",
- "sha1" : "5bd1feff346ff9e945079856554d174075d5f5c7",
- "size" : 13284,
- "translated" : 86,
- "fuzzy" : 0,
- "untranslated" : 1007
- },
- "mn" : {
- "file" : "ba4455b0bfcc24d23499587637aebc9ce283000e.class",
- "sha1" : "ba4455b0bfcc24d23499587637aebc9ce283000e",
- "size" : 145103,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "ms" : {
- "file" : "0a2440c5ba866d5813b33fbaae04568dea18dfe8.class",
- "sha1" : "0a2440c5ba866d5813b33fbaae04568dea18dfe8",
- "size" : 128928,
- "translated" : 958,
- "fuzzy" : 0,
- "untranslated" : 135
- },
- "nb" : {
- "file" : "c5c8b5642165dcb28604bd40d7f2e6a508e51725.class",
- "sha1" : "c5c8b5642165dcb28604bd40d7f2e6a508e51725",
- "size" : 65006,
- "translated" : 607,
- "fuzzy" : 0,
- "untranslated" : 486
- },
- "nl" : {
- "file" : "3fced84194ef345022dddee84738bb8c742012c5.class",
- "sha1" : "3fced84194ef345022dddee84738bb8c742012c5",
- "size" : 145861,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "nn" : {
- "file" : "9b568b72e01e6e3a805be3fcac81a8d2a81487e8.class",
- "sha1" : "9b568b72e01e6e3a805be3fcac81a8d2a81487e8",
- "size" : 137945,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "pl" : {
- "file" : "a3060323d787011872c3d8aae9f2f6660a40cf9c.class",
- "sha1" : "a3060323d787011872c3d8aae9f2f6660a40cf9c",
- "size" : 143293,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "pt_PT" : {
- "file" : "cba7ae8c6b93047045ab92a42651d5e9bdd654da.class",
- "sha1" : "cba7ae8c6b93047045ab92a42651d5e9bdd654da",
- "size" : 144795,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "pt_BR" : {
- "file" : "f7c9ce421a50ce1f07e33c521bf63021eaabf39c.class",
- "sha1" : "f7c9ce421a50ce1f07e33c521bf63021eaabf39c",
- "size" : 141123,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "ro" : {
- "file" : "937ae3c194c0857ed9b804a229efce92fa4c5674.class",
- "sha1" : "937ae3c194c0857ed9b804a229efce92fa4c5674",
- "size" : 144892,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "ru" : {
- "file" : "6ef2160cd14f59f23bda3df995da9bdc7e28cbba.class",
- "sha1" : "6ef2160cd14f59f23bda3df995da9bdc7e28cbba",
- "size" : 142835,
- "translated" : 1031,
- "fuzzy" : 0,
- "untranslated" : 62
- },
- "si" : {
- "file" : "b156f906a02decf93cf1f988984c7b5a43409590.class",
- "sha1" : "b156f906a02decf93cf1f988984c7b5a43409590",
- "size" : 454,
- "translated" : 7,
- "fuzzy" : 0,
- "untranslated" : 1086
- },
- "sk" : {
- "file" : "f1d32f055feac63a4c9e773c3e66ab7b4ae35626.class",
- "sha1" : "f1d32f055feac63a4c9e773c3e66ab7b4ae35626",
- "size" : 139643,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "sl" : {
- "file" : "8c66381fe0bf8e858a41bac30d28240c3c7d2d9a.class",
- "sha1" : "8c66381fe0bf8e858a41bac30d28240c3c7d2d9a",
- "size" : 83079,
- "translated" : 599,
- "fuzzy" : 0,
- "untranslated" : 494
- },
- "sr" : {
- "file" : "e28850462023c55c1ec87117f342039ff9251e6a.class",
- "sha1" : "e28850462023c55c1ec87117f342039ff9251e6a",
- "size" : 55187,
- "translated" : 450,
- "fuzzy" : 0,
- "untranslated" : 643
- },
- "sv" : {
- "file" : "99ab207f3d99a59b8173a2ee5eedb8cdb79e9b1a.class",
- "sha1" : "99ab207f3d99a59b8173a2ee5eedb8cdb79e9b1a",
- "size" : 134617,
- "translated" : 990,
- "fuzzy" : 0,
- "untranslated" : 103
- },
- "th" : {
- "file" : "805e8d895166a8942afc90fed6b693dee1a8b134.class",
- "sha1" : "805e8d895166a8942afc90fed6b693dee1a8b134",
- "size" : 48926,
- "translated" : 405,
- "fuzzy" : 0,
- "untranslated" : 688
- },
- "tr" : {
- "file" : "7454355d341c712932b707d70faec9d1f7ca3e7b.class",
- "sha1" : "7454355d341c712932b707d70faec9d1f7ca3e7b",
- "size" : 139854,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "uk" : {
- "file" : "1ff78912f28968ee7149818c5fcb20d9ad123aee.class",
- "sha1" : "1ff78912f28968ee7149818c5fcb20d9ad123aee",
- "size" : 143599,
- "translated" : 1038,
- "fuzzy" : 0,
- "untranslated" : 55
- },
- "vi" : {
- "file" : "95cced97bbf4706976d133113aaebce1d8d6dd0e.class",
- "sha1" : "95cced97bbf4706976d133113aaebce1d8d6dd0e",
- "size" : 139812,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "zh" : {
- "file" : "1764ce984b322bf8ab2f88f93c47db62261df4a3.class",
- "sha1" : "1764ce984b322bf8ab2f88f93c47db62261df4a3",
- "size" : 101420,
- "translated" : 1024,
- "fuzzy" : 0,
- "untranslated" : 69
- },
- "zh_TW" : {
- "file" : "53db715fc238e97bee26c4b2e537076324063979.class",
- "sha1" : "53db715fc238e97bee26c4b2e537076324063979",
- "size" : 108891,
- "translated" : 1092,
- "fuzzy" : 0,
- "untranslated" : 1
- }
- }
-}
diff --git a/data/minceraft/translations/mmc_ru.qm b/data/minceraft/translations/mmc_ru.qm
deleted file mode 100755
index 140e845..0000000
Binary files a/data/minceraft/translations/mmc_ru.qm and /dev/null differ
diff --git a/data/minceraft/ultimmc.cfg b/data/minceraft/ultimmc.cfg
deleted file mode 100755
index 0cfee9a..0000000
--- a/data/minceraft/ultimmc.cfg
+++ /dev/null
@@ -1,51 +0,0 @@
-Analytics=true
-AutoCloseConsole=false
-AutoUpdate=true
-CentralModsDir=mods
-ConsoleFont=DejaVu Sans Mono
-ConsoleFontSize=11
-ConsoleMaxLines=100000
-ConsoleOverflowStop=true
-ConsoleWindowGeometry=AdnQywADAAAAAAUCAAAAMAAAB30AAAStAAAAAAAAAAD////+/////gAAAAACAAAAB4AAAAUCAAAAMAAAB30AAASt
-ConsoleWindowState=AAAA/wAAAAD9AAAAAAAAAnwAAAR+AAAABAAAAAQAAAAIAAAACPwAAAAA
-IconTheme=multimc
-IconsDir=icons
-InstSortMode=Name
-InstanceDir=instances
-JProfilerPath=
-JVisualVMPath=
-JavaPath=java
-JsonEditor=
-JvmArgs=
-Language=ru
-LastHostname=minceraftos
-LastUsedGroupForNewInstance=
-LaunchMaximized=false
-MCEditPath=
-MainWindowGeometry=AdnQywADAAAAAAKCAAAAMAAABP0AAAStAAAAAAAAABQAAAPVAAACYwAAAAACAAAAB4AAAAKCAAAAMAAABP0AAASt
-MainWindowState=AAAA/wAAAAD9AAAAAAAAAZoAAAQiAAAABAAAAAQAAAAIAAAACPwAAAADAAAAAQAAAAEAAAAeAGkAbgBzAHQAYQBuAGMAZQBUAG8AbwBsAEIAYQByAwAAAAD/////AAAAAAAAAAAAAAACAAAAAQAAABYAbQBhAGkAbgBUAG8AbwBsAEIAYQByAQAAAAD/////AAAAAAAAAAAAAAADAAAAAQAAABYAbgBlAHcAcwBUAG8AbwBsAEIAYQByAQAAAAD/////AAAAAAAAAAA=
-MaxMemAlloc=1024
-MinMemAlloc=512
-MinecraftWinHeight=480
-MinecraftWinWidth=854
-NewInstanceGeometry=AdnQywADAAAAAAJTAAABhQAABSwAAANBAAACUwAAAYUAAAUsAAADQQAAAAAAAAAAB4AAAAJTAAABhQAABSwAAANB
-PagedGeometry=AdnQywADAAAAAAIuAAAA5QAABVAAAAPhAAACLgAAAOUAAAVQAAAD4QAAAAAAAAAAB4AAAAIuAAAA5QAABVAAAAPh
-PasteEEAPIKey=multimc
-PostExitCommand=
-PreLaunchCommand=
-ProxyAddr=127.0.0.1
-ProxyPass=
-ProxyPort=8080
-ProxyType=None
-ProxyUser=
-RecordGameTime=true
-SelectedInstance=1.21.4
-ShowConsole=false
-ShowConsoleOnError=true
-ShowGameTime=true
-ShowGameTimeHours=false
-ShowGlobalGameTime=true
-ShownNotifications=
-UseNativeGLFW=false
-UseNativeOpenAL=false
-WrapperCommand=
diff --git a/data/minceraft/ultimmc.cfg.def b/data/minceraft/ultimmc.cfg.def
old mode 100644
new mode 100755
diff --git a/ultimmc/.gitattributes b/ultimmc/.gitattributes
new file mode 100644
index 0000000..c9c0d50
--- /dev/null
+++ b/ultimmc/.gitattributes
@@ -0,0 +1,2 @@
+*.pem -crlf
+**/testdata/** -text -diff
diff --git a/ultimmc/.github/ISSUE_TEMPLATE/bug_report.yml b/ultimmc/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..72986e9
--- /dev/null
+++ b/ultimmc/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,51 @@
+name: Bug Report
+description: File a bug report
+labels: [bug, needs-triage]
+body:
+- type: markdown
+ attributes:
+ value: |
+ If you need help with running Minecraft, please visit us [on our Discord](https://discord.gg/multimc) before making a bug report.
+
+ Before submitting a bug report, please make sure you have read this *entire* form, and that:
+ * You have read the [FAQ](https://github.com/MultiMC/Launcher/wiki/FAQ) and it has not answered your question
+ * Your bug is not caused by Minecraft or any mods you have installed.
+ * Your issue has not been reported before, [make sure to use the search function!](https://github.com/MultiMC/Launcher/issues)
+
+ **Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
+- type: dropdown
+ attributes:
+ label: Operating System
+ description: If you know this bug occurs on multiple operating systems, select all you have tested.
+ multiple: true
+ options:
+ - Windows
+ - macOS
+ - Linux
+ - Other
+- type: textarea
+ attributes:
+ label: Description of bug
+ description: What did you expect to happen, what happened, and why is it incorrect?
+ placeholder: The cat button should show a cat, but it showed a dog instead!
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Steps to reproduce
+ description: A bulleted list, or an exported instance if relevant.
+ placeholder: "* Press the cat button"
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Suspected cause
+ description: If you know what could be causing this bug, describe it here.
+ validations:
+ required: false
+- type: checkboxes
+ attributes:
+ label: This issue is unique
+ options:
+ - label: I have searched the issue tracker and did not find an issue describing my bug.
+ required: true
diff --git a/ultimmc/.github/ISSUE_TEMPLATE/config.yml b/ultimmc/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..0086358
--- /dev/null
+++ b/ultimmc/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: true
diff --git a/ultimmc/.github/ISSUE_TEMPLATE/suggestion.yml b/ultimmc/.github/ISSUE_TEMPLATE/suggestion.yml
new file mode 100644
index 0000000..88bf66c
--- /dev/null
+++ b/ultimmc/.github/ISSUE_TEMPLATE/suggestion.yml
@@ -0,0 +1,38 @@
+name: Suggestion
+description: Make a suggestion
+labels: [idea, needs-triage]
+body:
+- type: markdown
+ attributes:
+ value: |
+ ### Use this form to suggest a feature for MultiMC.
+- type: input
+ attributes:
+ label: Role
+ description: In what way do you use MultiMC that needs this feature?
+ placeholder: I play modded Minecraft.
+ validations:
+ required: true
+- type: input
+ attributes:
+ label: Suggestion
+ description: What do you want MultiMC to do?
+ placeholder: I want the cat button to meow.
+ validations:
+ required: true
+- type: input
+ attributes:
+ label: Benefit
+ description: Why do you need MultiMC to do this?
+ placeholder: so that I can always hear a cat when I need to.
+ validations:
+ required: true
+- type: checkboxes
+ attributes:
+ label: This suggestion is unique
+ options:
+ - label: I have searched the issue tracker and did not find an issue describing my suggestion, especially not one that has been rejected.
+ required: true
+- type: textarea
+ attributes:
+ label: You may use the editor below to elaborate further.
diff --git a/ultimmc/.github/workflows/dispatch.yml b/ultimmc/.github/workflows/dispatch.yml
new file mode 100644
index 0000000..097524c
--- /dev/null
+++ b/ultimmc/.github/workflows/dispatch.yml
@@ -0,0 +1,20 @@
+name: Dispatcher
+on:
+ push:
+ branches: ['6']
+jobs:
+ dispatch:
+ name: Dispatch
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ steps:
+ - name: Extract branch name
+ shell: bash
+ run: echo "branch=$(echo ${GITHUB_REF#refs/heads/})" >>$GITHUB_OUTPUT
+ id: extract_branch
+ - name: Dispatch to workflows
+ run: |
+ curl -H "Accept: application/vnd.github.everest-preview+json" \
+ -H "Authorization: token ${{ secrets.DISPATCH_TOKEN }}" \
+ --request POST \
+ --data '{"event_type": "push_to_main_repo", "client_payload": { "branch": "${{ steps.extract_branch.outputs.branch }}" }}' https://api.github.com/repos/MultiMC/Build/dispatches
diff --git a/ultimmc/.github/workflows/main.yml b/ultimmc/.github/workflows/main.yml
new file mode 100644
index 0000000..6f45374
--- /dev/null
+++ b/ultimmc/.github/workflows/main.yml
@@ -0,0 +1,286 @@
+name: CI
+
+on:
+ push:
+ branches: [ develop ]
+ pull_request:
+ branches: [ develop ]
+ workflow_dispatch:
+ schedule:
+ - cron: "0 0 1 * *"
+
+jobs:
+ build-linux:
+ name: build-linux
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@main
+ with:
+ submodules: 'recursive'
+
+ - name: Install Dependencies
+ run: |
+ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
+ sudo add-apt-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ bionic main'
+ sudo add-apt-repository 'deb http://dk.archive.ubuntu.com/ubuntu/ bionic universe'
+ sudo apt update
+ sudo apt install libgl1-mesa-dev qttools5-dev g++-5 gcc-5
+
+ - name: Build
+ run: |
+ export JAVA_HOME=$JAVA_HOME_8_X64
+ mkdir build
+ cd build
+ cmake \
+ -DCMAKE_C_COMPILER=/usr/bin/gcc-5 \
+ -DCMAKE_CXX_COMPILER=/usr/bin/g++-5 \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
+ -DCMAKE_INSTALL_PREFIX:PATH=/home/runner/UltimMC/UltimMC \
+ -DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
+ -DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
+ -DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
+ -DLauncher_LAYOUT=lin-nodeps \
+ -DLauncher_BUILD_PLATFORM=lin64 \
+ -DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
+ -DLauncher_EMBED_SECRETS=On \
+ $GITHUB_WORKSPACE
+
+ - name: Compile
+ run: |
+ cd build
+ make -j$(nproc)
+
+ - name: Test
+ run: |
+ cd build
+ make test
+ cmake -E remove_directory "/home/runner/UltimMC/UltimMC"
+
+ - name: Install
+ run: |
+ cd build
+ make install
+ chmod +x /home/runner/UltimMC/UltimMC/UltimMC
+ chmod +x /home/runner/UltimMC/UltimMC/bin/UltimMC
+
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@main
+ with:
+ name: mmc-cracked-lin64
+ path: /home/runner/UltimMC
+
+ build-windows:
+ name: build-windows
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@main
+ with:
+ submodules: 'recursive'
+
+ - name: Cache Qt
+ uses: actions/cache@main
+ id: qt-cached
+ with:
+ path: "D:/Qt"
+ key: ${{ runner.os }}-qt56-installed-d
+
+ - name: Cache Qt Installer
+ uses: actions/cache@main
+ if: steps.qt-cached.outputs.cache-hit != 'true'
+ id: installer-cached
+ with:
+ path: "installer.exe"
+ key: ${{ runner.os }}-qt56-installer
+
+ - name: Create QtAccount File
+ if: steps.qt-cached.outputs.cache-hit != 'true'
+ run: |
+ mkdir C:/Users/runneradmin/AppData/Roaming/Qt/
+ curl https://gist.github.com/Neptune650/1086e0a3126be6a66580b71afcf8bd99/raw/797d8b90edf07ce88f265b38a573cc6b1fb45bfb/qtaccount.txt --output C:/Users/runneradmin/AppData/Roaming/Qt/qtaccount.ini
+
+ - name: Download Qt Installer
+ if: steps.installer-cached.outputs.cache-hit != 'true' && steps.qt-cached.outputs.cache-hit != 'true'
+ run: curl https://download.qt.io/new_archive/qt/5.6/5.6.3/qt-opensource-windows-x86-mingw492-5.6.3.exe --output installer.exe
+
+ - name: Download Qt non-Interactive Script
+ if: steps.qt-cached.outputs.cache-hit != 'true'
+ run: curl https://gist.githubusercontent.com/Neptune650/aa6c051abc17e7d9d609add7f6dfd16a/raw/074dedb7525c0ffc010b39871615b008c2efbcd6/qt-installer-noninteractive.qs --output nonInteractive.qs
+
+ - name: Install Qt
+ if: steps.qt-cached.outputs.cache-hit != 'true'
+ shell: cmd
+ run: installer.exe -v --script nonInteractive.qs --silent
+
+ - name: Setup CMake
+ run: |
+ curl -L https://github.com/Kitware/CMake/releases/download/v3.30.4/cmake-3.30.4-windows-i386.zip -o cmake.zip
+ unzip cmake.zip
+
+ - name: Setup JDK
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: '8'
+ architecture: x86
+
+ - name: Setup zlib
+ run: |
+ mkdir zlib
+ cd zlib
+ C:\msys64\usr\bin\wget.exe -O zlib.zip https://downloads.sourceforge.net/project/gnuwin32/zlib/1.2.3/zlib-1.2.3-bin.zip
+ C:\msys64\usr\bin\wget.exe -O zliblibs.zip https://downloads.sourceforge.net/project/gnuwin32/zlib/1.2.3/zlib-1.2.3-lib.zip
+ unzip zlib.zip
+ unzip zliblibs.zip
+
+ - name: Setup OpenSSL
+ run: |
+ mkdir OpenSSL
+ cd OpenSSL
+ curl -L https://files.catbox.moe/ctwswu.dll -o libeay32.dll
+ curl -L https://files.catbox.moe/ie9e77.dll -o ssleay32.dll
+
+ - name: Build
+ shell: cmd
+ if: steps.build-cached.outputs.cache-hit != 'true'
+ run: |
+ for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
+ set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
+ set PATH=%CD%\zlib;%CD%\zlib\bin;%CD%\zlib\lib;%CD%\zlib\include;%PATH%
+ set PATH=%CD%\OpenSSL;%PATH%
+ set PATH=%PATHCM%;%PATH%
+ mkdir build
+ cd build
+ cmake ^
+ -DCMAKE_C_COMPILER=gcc ^
+ -DCMAKE_CXX_COMPILER=g++ ^
+ -DCMAKE_BUILD_TYPE=Release ^
+ -DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json ^
+ -DCMAKE_INSTALL_PREFIX:PATH="D:/UltimMC/UltimMC" ^
+ -DCMAKE_PREFIX_PATH="D:\Qt\5.6.3\mingw49_32" ^
+ -DQt5_DIR="D:\Qt\5.6.3\mingw49_32" ^
+ -DLauncher_UPDATER_BASE=https://files.multimc.org/update/ ^
+ -DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ ^
+ -DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 ^
+ -DLauncher_LAYOUT=win-bundle ^
+ -DLauncher_BUILD_PLATFORM=win32 ^
+ -DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues ^
+ -DLauncher_EMBED_SECRETS=On ^
+ -G "MinGW Makefiles" ^
+ ..
+
+ - name: Compile
+ shell: cmd
+ run: |
+ for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
+ set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
+ set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
+ set PATH=%CD%\OpenSSL;%PATH%
+ set PATH=%PATHCM%;%PATH%
+ set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
+ cd build
+ mingw32-make -j%NUMBER_OF_PROCESSORS%
+
+ - name: Test
+ shell: cmd
+ run: |
+ for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
+ set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
+ set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
+ set PATH=%CD%\OpenSSL;%PATH%
+ set PATH=%PATHCM%;%PATH%
+ set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
+ cd build
+ mingw32-make test
+ cmake -E remove_directory "D:/UltimMC/UltimMC"
+
+ - name: Install
+ shell: cmd
+ run: |
+ for /f "tokens=*" %%n in ('powershell -NoLogo -Command "$(ls $pwd\cmake-*-windows-i386\bin).Fullname"') do @(set PATHCM=%%n)
+ set PATH=D:\Qt\5.6.3\mingw49_32\bin;D:\Qt\Tools\mingw492_32\bin;
+ set PATH=%CD%\zlib;%CD%\zlib\bin;%PATH%
+ set PATH=%CD%\OpenSSL;%PATH%
+ set PATH=%PATHCM%;%PATH%
+ set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;%PATH%
+ cd build
+ mingw32-make install
+
+ - name: Copy OpenSSL
+ shell: cmd
+ run: |
+ cp OpenSSL/ssleay32.dll D:/UltimMC/UltimMC/ssleay32.dll
+ cp OpenSSL/libeay32.dll D:/UltimMC/UltimMC/libeay32.dll
+
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@main
+ with:
+ name: mmc-cracked-win32
+ path: "D:/UltimMC"
+
+ build-mac:
+ name: build-mac
+ runs-on: macos-13
+
+ steps:
+ - uses: actions/checkout@main
+ with:
+ submodules: 'recursive'
+
+ - name: Cache Dependencies
+ uses: actions/cache@main
+ with:
+ path: /Users/runner/Library/Caches/Homebrew
+ key: ${{ runner.os }}-deps-cache
+
+ - name: Install Dependencies
+ run: |
+ brew cleanup
+ brew install qt@5
+
+ - name: Build
+ run: |
+ mkdir build
+ cd build
+ cmake \
+ -DCMAKE_C_COMPILER=/usr/bin/clang \
+ -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLauncher_NOTIFICATION_URL:STRING=https://files.multimc.org/notifications.json \
+ -DCMAKE_INSTALL_PREFIX:PATH="/Users/runner/work/UltimMC/build/dist" \
+ -DCMAKE_PREFIX_PATH="$(brew --prefix qt@5)/lib/cmake" \
+ -DQt5_DIR="$(brew --prefix qt@5)" \
+ -DLauncher_UPDATER_BASE=https://files.multimc.org/update/ \
+ -DLauncher_PASTE_EE_API_KEY:STRING=utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ \
+ -DLauncher_ANALYTICS_ID:STRING=UA-87731965-2 \
+ -DLauncher_LAYOUT=mac-bundle \
+ -DLauncher_BUILD_PLATFORM=osx64-5.15.2 \
+ -DLauncher_BUG_TRACKER_URL=https://github.com/UltimMC/Launcher/issues \
+ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 \
+ -DLauncher_EMBED_SECRETS=On \
+ $GITHUB_WORKSPACE
+
+ - name: Compile
+ run: |
+ cd build
+ make -j$(sysctl -n hw.logicalcpu)
+
+ - name: Test
+ run: |
+ cd build
+ make test
+ cmake -E remove_directory "/Users/runner/work/UltimMC/build/dist"
+
+ - name: Install
+ run: |
+ cd build
+ make install
+ chmod +x /Users/runner/work/UltimMC/build/dist/UltimMC.app/Contents/MacOS/UltimMC
+
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@main
+ with:
+ name: mmc-cracked-osx64
+ path: /Users/runner/work/UltimMC/build/dist
diff --git a/ultimmc/.gitignore b/ultimmc/.gitignore
new file mode 100644
index 0000000..f1bd42a
--- /dev/null
+++ b/ultimmc/.gitignore
@@ -0,0 +1,39 @@
+Thumbs.db
+*.kdev4
+.user
+.directory
+resources/CMakeFiles
+*~
+*.swp
+html/
+
+# Project Files
+*.pro.user
+CMakeLists.txt.user
+CMakeLists.txt.user.*
+/.project
+/.settings
+/.idea
+cmake-build-*/
+Debug
+.cache
+
+# Build dirs
+build
+/build-*
+
+# Install dirs
+install
+/install-*
+
+# Ctags File
+tags
+
+# YouCompleteMe config stuff.
+.ycm_extra_conf.*
+
+#OSX Stuff
+.DS_Store
+
+branding/
+run/
diff --git a/ultimmc/.gitmodules b/ultimmc/.gitmodules
new file mode 100644
index 0000000..04a561c
--- /dev/null
+++ b/ultimmc/.gitmodules
@@ -0,0 +1,8 @@
+[submodule "depends/libnbtplusplus"]
+ path = libraries/libnbtplusplus
+ url = https://github.com/MultiMC/libnbtplusplus.git
+ pushurl = git@github.com:MultiMC/libnbtplusplus.git
+[submodule "libraries/quazip"]
+ path = libraries/quazip
+ url = https://github.com/MultiMC/quazip.git
+ pushurl = git@github.com:MultiMC/quazip.git
diff --git a/ultimmc/CMakeLists.txt b/ultimmc/CMakeLists.txt
new file mode 100644
index 0000000..e9ac52b
--- /dev/null
+++ b/ultimmc/CMakeLists.txt
@@ -0,0 +1,290 @@
+cmake_minimum_required(VERSION 3.1)
+
+if(WIN32)
+ # In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
+ cmake_policy(SET CMP0020 OLD)
+endif()
+
+project(Launcher)
+enable_testing()
+
+string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
+if(IS_IN_SOURCE_BUILD)
+ message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
+endif()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR
+ CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*"
+ )
+ message(FATAL_ERROR "Building the Launcher is not supported in Linux-on-Windows distributions.")
+ endif()
+endif()
+
+option(Launcher_RUN_CLANG_TIDY "Run clang-tidy with the compiler." OFF)
+if(Launcher_RUN_CLANG_TIDY)
+ find_program(CLANG_TIDY_COMMAND NAMES clang-tidy)
+ if(NOT CLANG_TIDY_COMMAND)
+ message(WARNING "CMake_RUN_CLANG_TIDY is ON but clang-tidy is not found!")
+ set(DO_CLANG_TIDY "" CACHE STRING "" FORCE)
+ else()
+ set(CLANG_TIDY_CHECKS "-modernize-use-trailing-return-type,-readability-magic-numbers,-modernize-avoid-c-arrays")
+ set(DO_CLANG_TIDY "${CLANG_TIDY_COMMAND};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/src/*'")
+ endif()
+ # TODO: make this per-target, differentiate between libraries and main codebase
+ set(CMAKE_CXX_CLANG_TIDY ${DO_CLANG_TIDY})
+endif()
+
+
+##################################### Set CMake options #####################################
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
+
+# Output all executables and shared libs in the main build folder, not in subfolders.
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+if(UNIX)
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+endif()
+set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
+
+######## Set compiler flags ########
+set(CMAKE_CXX_STANDARD_REQUIRED true)
+set(CMAKE_C_STANDARD_REQUIRED true)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_C_STANDARD 11)
+include(GenerateExportHeader)
+set(CMAKE_CXX_FLAGS " -Wall -pedantic -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
+set(CMAKE_CXX_FLAGS_RELEASE " -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
+if(UNIX AND APPLE)
+ set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
+endif()
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror -Werror=return-type -O0")
+
+# Fix build with Qt 5.13
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
+
+##################################### Set Application options #####################################
+
+######## Set URLs ########
+set(Launcher_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetch Launcher's news RSS feed from.")
+
+######## Set version numbers ########
+set(Launcher_VERSION_MAJOR 0)
+set(Launcher_VERSION_MINOR 7)
+set(Launcher_VERSION_HOTFIX 0)
+
+# Build number
+set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
+
+# Build platform.
+set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
+
+# Channel list URL
+set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
+
+# Notification URL
+set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
+
+# The metadata server
+set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
+
+# paste.ee API key
+set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
+
+# Imgur API Client ID
+set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
+
+# Google analytics ID
+set(Launcher_ANALYTICS_ID "UA-87731965-2" CACHE STRING "ID you can get from Google analytics")
+
+# Bug tracker URL
+set(Launcher_BUG_TRACKER_URL "" CACHE STRING "URL for the bug tracker.")
+
+# Discord URL
+set(Launcher_DISCORD_URL "" CACHE STRING "URL for the Discord guild.")
+
+# Subreddit URL
+set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
+
+# Use the secrets library or a public stub?
+option(Launcher_EMBED_SECRETS "Determines whether to embed secrets. Secrets are separate and non-public." OFF)
+
+# API Keys
+# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
+# of these platforms, please change these API keys beforehand.
+# Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause
+# breakage to thousands of users.
+# If you don't plan to use these features of this software, you can just remove these values.
+
+# By using this key in your builds you accept the terms of use laid down in
+# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
+set(Launcher_MSA_CLIENT_ID "f4404707-7bbe-4e40-80ba-85fb2bb825a1" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
+
+#### Check the current Git commit and branch
+include(GetGitRevisionDescription)
+get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
+
+message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
+message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
+
+set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
+
+#### Custom target to just print the version.
+add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
+add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
+
+################################ 3rd Party Libs ################################
+
+# Find the required Qt parts
+find_package(Qt5Core REQUIRED)
+find_package(Qt5Widgets REQUIRED)
+find_package(Qt5Concurrent REQUIRED)
+find_package(Qt5Network REQUIRED)
+find_package(Qt5Test REQUIRED)
+find_package(Qt5Xml REQUIRED)
+
+# The Qt5 cmake files don't provide its install paths, so ask qmake.
+include(QMakeQuery)
+query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
+query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
+query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
+query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
+query_qmake(QT_HOST_DATA QT_DATA_DIR)
+set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
+
+if (Qt5_POSITION_INDEPENDENT_CODE)
+ SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
+endif()
+
+####################################### Secrets #######################################
+
+if(Launcher_EMBED_SECRETS)
+ add_subdirectory(secrets)
+else()
+ add_subdirectory(notsecrets)
+endif()
+
+####################################### Install layout #######################################
+
+# How to install the build results
+set(Launcher_LAYOUT "auto" CACHE STRING "The layout for the launcher installation (auto, win-bundle, lin-nodeps, mac-bundle)")
+set_property(CACHE Launcher_LAYOUT PROPERTY STRINGS auto win-bundle lin-nodeps mac-bundle)
+
+if(Launcher_LAYOUT STREQUAL "auto")
+ if(UNIX AND APPLE)
+ set(Launcher_LAYOUT_REAL "mac-bundle")
+ elseif(UNIX)
+ set(Launcher_LAYOUT_REAL "lin-nodeps")
+ elseif(WIN32)
+ set(Launcher_LAYOUT_REAL "win-bundle")
+ else()
+ message(FATAL_ERROR "Cannot choose a sensible install layout for your platform.")
+ endif()
+else()
+ set(Launcher_LAYOUT_REAL ${Launcher_LAYOUT})
+endif()
+
+if(Launcher_LAYOUT_REAL STREQUAL "mac-bundle")
+ set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
+ set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
+ set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
+ set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
+ set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
+
+ set(BUNDLE_DEST_DIR ".")
+
+ # Apps to bundle
+ set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
+
+ # Mac bundle settings
+ set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
+ set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: Minecraft launcher and management utility.")
+ set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.${Launcher_Name}")
+ set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
+ set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
+ set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
+ set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
+ set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2023 ${Launcher_Copyright}")
+
+ # directories to look for dependencies
+ set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+
+ # install as bundle
+ set(INSTALL_BUNDLE "full")
+
+ # Add the icon
+ install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
+
+elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
+ set(BINARY_DEST_DIR "bin")
+ set(LIBRARY_DEST_DIR "bin")
+ set(PLUGIN_DEST_DIR "plugins")
+ set(BUNDLE_DEST_DIR ".")
+ set(RESOURCES_DEST_DIR ".")
+ set(JARS_DEST_DIR "bin/jars")
+
+ # install as bundle with no dependencies included
+ set(INSTALL_BUNDLE "nodeps")
+
+ # Set RPATH
+ SET(Launcher_BINARY_RPATH "$ORIGIN/")
+
+ # Install basic runner script
+ configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
+ install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
+
+elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
+ set(BINARY_DEST_DIR ".")
+ set(LIBRARY_DEST_DIR ".")
+ set(PLUGIN_DEST_DIR ".")
+ set(BUNDLE_DEST_DIR ".")
+ set(RESOURCES_DEST_DIR ".")
+ set(JARS_DEST_DIR "jars")
+
+ # Apps to bundle
+ set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.exe")
+
+ # directories to look for dependencies
+ set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+
+ # install as bundle
+ set(INSTALL_BUNDLE "full")
+else()
+ message(FATAL_ERROR "No sensible install layout set.")
+endif()
+
+################################ Included Libs ################################
+
+include(ExternalProject)
+set_directory_properties(PROPERTIES EP_BASE External)
+
+option(NBT_BUILD_SHARED "Build NBT shared library" ON)
+option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
+option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
+set(NBT_NAME Launcher_nbt++)
+set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
+add_subdirectory(libraries/libnbtplusplus)
+
+add_subdirectory(libraries/ganalytics) # google analytics library
+add_subdirectory(libraries/systeminfo) # system information library
+add_subdirectory(libraries/hoedown) # markdown parser
+add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
+add_subdirectory(libraries/javacheck) # java compatibility checker
+add_subdirectory(libraries/xz-embedded) # xz compression
+add_subdirectory(libraries/quazip) # zip manipulation library
+add_subdirectory(libraries/rainbow) # Qt extension for colors
+add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
+add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
+add_subdirectory(libraries/classparser) # google analytics library
+add_subdirectory(libraries/optional-bare)
+add_subdirectory(libraries/tomlc99) # toml parser
+add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
+
+############################### Built Artifacts ###############################
+
+add_subdirectory(buildconfig)
+
+# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
+add_subdirectory(launcher)
diff --git a/ultimmc/COPYING.md b/ultimmc/COPYING.md
new file mode 100644
index 0000000..0cbe6ed
--- /dev/null
+++ b/ultimmc/COPYING.md
@@ -0,0 +1,367 @@
+# MultiMC
+
+Portions are licensed under Apache 2.0 License:
+
+ Copyright 2012-2021 MultiMC Contributors
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+Portions are licensed under MS-PL:
+
+ This license governs use of the accompanying software. If you use the
+ software, you accept this license. If you do not accept the license,
+ do not use the software.
+
+ 1. Definitions
+ The terms "reproduce," "reproduction," "derivative works," and
+ "distribution" have the same meaning here as under U.S. copyright law.
+
+ A "contribution" is the original software, or any additions or
+ changes to the software.
+
+ A "contributor" is any person that distributes its contribution
+ under this license.
+
+ "Licensed patents" are a contributor's patent claims that read
+ directly on its contribution.
+
+ 2. Grant of Rights
+
+ (A) Copyright Grant- Subject to the terms of this license,
+ including the license conditions and limitations in section 3,
+ each contributor grants you a non-exclusive, worldwide, royalty-free
+ copyright license to reproduce its contribution, prepare derivative
+ works of its contribution, and distribute its contribution or any
+ derivative works that you create.
+
+ (B) Patent Grant- Subject to the terms of this license, including
+ the license conditions and limitations in section 3, each contributor
+ grants you a non-exclusive, worldwide, royalty-free license under its
+ licensed patents to make, have made, use, sell, offer for sale, import,
+ and/or otherwise dispose of its contribution in the software or derivative
+ works of the contribution in the software.
+
+ 3. Conditions and Limitations
+
+ (A) No Trademark License- This license does not grant you rights to
+ use any contributors' name, logo, or trademarks.
+
+ (B) If you bring a patent claim against any contributor over patents
+ that you claim are infringed by the software, your patent license
+ from such contributor to the software ends automatically.
+
+ (C) If you distribute any portion of the software, you must retain all
+ copyright, patent, trademark, and attribution notices that are present
+ in the software.
+
+ (D) If you distribute any portion of the software in source code form,
+ you may do so only under this license by including a complete copy of
+ this license with your distribution. If you distribute any portion of
+ the software in compiled or object code form, you may only do so under
+ a license that complies with this license.
+
+ (E) The software is licensed "as-is." You bear the risk of using it.
+ The contributors give no express warranties, guarantees or conditions.
+ You may have additional consumer rights under your local laws which
+ this license cannot change. To the extent permitted under your local
+ laws, the contributors exclude the implied warranties of merchantability,
+ fitness for a particular purpose and non-infringement.
+
+# MinGW runtime (Windows)
+
+ Copyright (c) 2012 MinGW.org project
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice, this permission notice and the below disclaimer
+ shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+# Qt 5
+
+ Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+ Contact: http://www.qt-project.org/legal
+
+ Licensed under LGPL v2.1
+
+# libnbt++
+
+ libnbt++ - A library for the Minecraft Named Binary Tag format.
+ Copyright (C) 2013, 2015 ljfa-ag
+
+ libnbt++ is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ libnbt++ is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with libnbt++. If not, see .
+
+# rainbow (KGuiAddons)
+
+ Copyright (C) 2007 Matthew Woehlke
+ Copyright (C) 2007 Olaf Schmidt
+ Copyright (C) 2007 Thomas Zander
+ Copyright (C) 2007 Zack Rusin
+ Copyright (C) 2015 Petr Mrazek
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+# Hoedown
+
+ Copyright (c) 2008, Natacha Porté
+ Copyright (c) 2011, Vicent Martí
+ Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Batch icon set
+
+ You are free to use Batch (the "icon set") or any part thereof (the "icons")
+ in any personal, open-source or commercial work without obligation of payment
+ (monetary or otherwise) or attribution. Do not sell the icon set, host
+ the icon set or rent the icon set (either in existing or modified form).
+
+ While attribution is optional, it is always appreciated.
+
+ Intellectual property rights are not transferred with the download of the icons.
+
+ EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT
+ BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,
+ PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
+ EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+# Material Design Icons
+
+ Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
+ with Reserved Font Name Material Design Icons.
+ Copyright (c) 2014, Google (http://www.google.com/design/)
+ uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE
+
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
+ This license is copied below, and is also available with a FAQ at:
+ http://scripts.sil.org/OFL
+
+# Quazip
+
+ Copyright (C) 2005-2011 Sergey A. Tachenov
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ See COPYING file for the full LGPL text.
+
+ Original ZIP package is copyrighted by Gilles Vollant, see
+ quazip/(un)zip.h files for details, basically it's zlib license.
+
+# xz-minidec
+
+ XZ decompressor
+
+ Authors: Lasse Collin
+ Igor Pavlov
+
+ This file has been put into the public domain.
+ You can do whatever you want with this file.
+
+# ColumnResizer
+
+ Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted (subject to the limitations in the
+ disclaimer below) provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the
+ distribution.
+
+ * The name of the contributors may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+ NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# lionshead
+
+ Code has been taken from https://github.com/natefoo/lionshead and loosely
+ translated to C++ laced with Qt.
+
+ MIT License
+
+ Copyright (c) 2017 Nate Coraor
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+# optional-bare
+
+ Code from https://github.com/martinmoene/optional-bare/
+
+ Boost Software License - Version 1.0 - August 17th, 2003
+
+ Permission is hereby granted, free of charge, to any person or organization
+ obtaining a copy of the software and accompanying documentation covered by
+ this license (the "Software") to use, reproduce, display, distribute,
+ execute, and transmit the Software, and to prepare derivative works of the
+ Software, and to permit third-parties to whom the Software is furnished to
+ do so, all subject to the following:
+
+ The copyright notices in the Software and this entire statement, including
+ the above license grant, this restriction and the following disclaimer,
+ must be included in all copies of the Software, in whole or in part, and
+ all derivative works of the Software, unless such copies or derivative
+ works are solely in the form of machine-executable object code generated by
+ a source language processor.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+# tomlc99
+
+ MIT License
+
+ Copyright (c) 2017 CK Tan
+ https://github.com/cktan/tomlc99
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+# O2 (Katabasis fork)
+
+ Copyright (c) 2012, Akos Polster
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ultimmc/README.md b/ultimmc/README.md
new file mode 100644
index 0000000..22fba04
--- /dev/null
+++ b/ultimmc/README.md
@@ -0,0 +1,61 @@
+
+
+
+
+
UltimMC is a custom launcher for Minecraft which allows you to manage multiple instances and use offline ("cracked") accounts while keeping as close as possible to the original.
+
+
+
+> [!IMPORTANT]
+> This project is a **fork** of MultiMC.
+> This software is provided without any warranty, so please don't contact the main
+> MultiMC developers in case anything goes wrong using this launcher.
+> Nonetheless, feel free to create an issue within this repository
+> in case you face an issue specific to UltimMC.
+
+## Downloading
+
+- All the available downloads can be found [here](https://nightly.link/UltimMC/Launcher/workflows/main/develop). These builds are directly taken from our [GitHub Actions](https://github.com/UltimMC/Launcher/actions).
+
+Direct downloads for specific platforms can be found below.
+
+- *[Windows \(32-bit and 64-bit\)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-win32.zip)*.
+
+- *[Linux (64-bit)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-lin64.zip)*.
+
+- *[macOS (10.14 and newer)](https://nightly.link/UltimMC/Launcher/workflows/main/develop/mmc-cracked-osx64.zip)*.
+
+> [!NOTE]
+> In the case you're using macOS then another additional step you might need to do
+> is to make `UltimMC` an executable by running the command `chmod +x UltimMC.app/Contents/MacOS/UltimMC` in the terminal.
+
+There's additionally a [.deb package](https://nightly.link/UltimMC/ultimmc-deb/workflows/ci/master/UltimMC.zip) for Debian/Ubuntu distributions.
+
+And an AUR package as [ultimmc-bin](https://aur.archlinux.org/packages/ultimmc-bin). [](https://aur.archlinux.org/packages/ultimmc-bin)
+
+## Installing and Using
+
+1. Pick the correct download for your system.
+2. Uncompress it in your desired directory.
+3. Launch `UltimMC`.
+4. Go to account settings.
+6. A. Pick "Add Local" and you will be requested to use the username you desire, this can be anything.
+7. B. Pick "Add Ely.by" and add your Ely.by account by putting your email and password.
+8. Save it.
+9. Now enjoy the Launcher.
+
+## Updating
+
+To update the launcher replace all replaceable files and folders with the newer ones from any of the links listed above.
+
+A better update system is in the works.
+
+## Forking
+
+This project now includes our MSA API key in order to have functional Microsoft authentication within the launcher.
+
+This means you're accepting the:
+
+- [Microsoft Identity Platform Terms of Use](https://learn.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
+
+We humbly ask that in case you wish to fork UltimMC, please either remove the key by setting it empty (`""`) or by setting your own.
diff --git a/ultimmc/buildconfig/BuildConfig.cpp.in b/ultimmc/buildconfig/BuildConfig.cpp.in
new file mode 100644
index 0000000..6fa4979
--- /dev/null
+++ b/ultimmc/buildconfig/BuildConfig.cpp.in
@@ -0,0 +1,72 @@
+#include "BuildConfig.h"
+#include
+
+const Config BuildConfig;
+
+Config::Config()
+{
+ // Name and copyright
+ LAUNCHER_NAME = "@Launcher_Name@";
+ LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
+ LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
+ LAUNCHER_DOMAIN = "@Launcher_Domain@";
+ LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
+ LAUNCHER_GIT = "@Launcher_Git@";
+
+ USER_AGENT = "@Launcher_UserAgent@";
+ USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
+
+ // Version information
+ VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
+ VERSION_MINOR = @Launcher_VERSION_MINOR@;
+ VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
+ VERSION_BUILD = @Launcher_VERSION_BUILD@;
+
+ BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
+ UPDATER_BASE = "@Launcher_UPDATER_BASE@";
+ ANALYTICS_ID = "@Launcher_ANALYTICS_ID@";
+ NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
+ FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";
+
+ GIT_COMMIT = "@Launcher_GIT_COMMIT@";
+ GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
+ if(GIT_REFSPEC.startsWith("refs/heads/") && !UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0)
+ {
+ VERSION_CHANNEL = GIT_REFSPEC;
+ VERSION_CHANNEL.remove("refs/heads/");
+ UPDATER_ENABLED = true;
+ }
+ else
+ {
+ VERSION_CHANNEL = QObject::tr("custom");
+ }
+
+ VERSION_STR = "@Launcher_VERSION_STRING@";
+ NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
+ PASTE_EE_KEY = "@Launcher_PASTE_EE_API_KEY@";
+ IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
+ MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
+ META_URL = "@Launcher_META_URL@";
+
+ BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
+ DISCORD_URL = "@Launcher_DISCORD_URL@";
+ SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
+}
+
+QString Config::printableVersionString() const
+{
+ QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
+
+ // If the build is not a main release, append the channel
+ if(VERSION_CHANNEL != "develop")
+ {
+ vstr += "-" + VERSION_CHANNEL;
+ }
+
+ // if a build number is set, also add it to the end
+ if(VERSION_BUILD >= 0)
+ {
+ vstr += "-" + QString::number(VERSION_BUILD);
+ }
+ return vstr;
+}
diff --git a/ultimmc/buildconfig/BuildConfig.h b/ultimmc/buildconfig/BuildConfig.h
new file mode 100644
index 0000000..589aafe
--- /dev/null
+++ b/ultimmc/buildconfig/BuildConfig.h
@@ -0,0 +1,121 @@
+#pragma once
+#include
+
+/**
+ * \brief The Config class holds all the build-time information passed from the build system.
+ */
+class Config
+{
+public:
+ Config();
+ QString LAUNCHER_NAME;
+ QString LAUNCHER_DISPLAYNAME;
+ QString LAUNCHER_COPYRIGHT;
+ QString LAUNCHER_DOMAIN;
+ QString LAUNCHER_CONFIGFILE;
+ QString LAUNCHER_GIT;
+
+ /// The major version number.
+ int VERSION_MAJOR;
+ /// The minor version number.
+ int VERSION_MINOR;
+ /// The hotfix number.
+ int VERSION_HOTFIX;
+ /// The build number.
+ int VERSION_BUILD;
+
+ /**
+ * The version channel
+ * This is used by the updater to determine what channel the current version came from.
+ */
+ QString VERSION_CHANNEL;
+
+ bool UPDATER_ENABLED = false;
+
+ /// A short string identifying this build's platform. For example, "lin64" or "win32".
+ QString BUILD_PLATFORM;
+
+ /// URL for the updater's channel
+ QString UPDATER_BASE;
+
+
+ /// User-Agent to use.
+ QString USER_AGENT;
+
+ /// User-Agent to use for uncached requests.
+ QString USER_AGENT_UNCACHED;
+
+
+ /// Google analytics ID
+ QString ANALYTICS_ID;
+
+ /// URL for notifications
+ QString NOTIFICATION_URL;
+
+ /// Used for matching notifications
+ QString FULL_VERSION_STR;
+
+ /// The git commit hash of this build
+ QString GIT_COMMIT;
+
+ /// The git refspec of this build
+ QString GIT_REFSPEC;
+
+ /// This is printed on start to standard output
+ QString VERSION_STR;
+
+ /**
+ * This is used to fetch the news RSS feed.
+ * It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
+ */
+ QString NEWS_RSS_URL;
+
+ /**
+ * API key you can get from paste.ee when you register an account
+ */
+ QString PASTE_EE_KEY;
+
+ /**
+ * Client ID you can get from Imgur when you register an application
+ */
+ QString IMGUR_CLIENT_ID;
+
+ /**
+ * Client ID you can get from Microsoft Identity Platform when you register an application
+ */
+ QString MSA_CLIENT_ID;
+
+ /**
+ * Metadata repository URL prefix
+ */
+ QString META_URL;
+
+ QString BUG_TRACKER_URL;
+ QString DISCORD_URL;
+ QString SUBREDDIT_URL;
+
+ QString RESOURCE_BASE = "https://resources.download.minecraft.net/";
+ QString LIBRARY_BASE = "https://libraries.minecraft.net/";
+ QString AUTH_BASE = "https://authserver.mojang.com/";
+ QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
+ QString FMLLIBS_BASE_URL = "https://files.multimc.org/fmllibs/";
+ QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/";
+
+ QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";
+
+ QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
+
+ QString TECHNIC_API_BASE_URL = "https://api.technicpack.net/";
+ /**
+ * The build that is reported to the Technic API.
+ */
+ QString TECHNIC_API_BUILD = "multimc";
+
+ /**
+ * \brief Converts the Version to a string.
+ * \return The version number in string format (major.minor.revision.build).
+ */
+ QString printableVersionString() const;
+};
+
+extern const Config BuildConfig;
diff --git a/ultimmc/buildconfig/CMakeLists.txt b/ultimmc/buildconfig/CMakeLists.txt
new file mode 100644
index 0000000..de4fd35
--- /dev/null
+++ b/ultimmc/buildconfig/CMakeLists.txt
@@ -0,0 +1,11 @@
+######## Configure the file with build properties ########
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/BuildConfig.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp")
+
+add_library(BuildConfig STATIC
+ BuildConfig.h
+ ${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
+)
+
+target_link_libraries(BuildConfig Qt5::Core)
+target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
diff --git a/ultimmc/cmake/BundleUtilities.cmake b/ultimmc/cmake/BundleUtilities.cmake
new file mode 100644
index 0000000..e3f50b9
--- /dev/null
+++ b/ultimmc/cmake/BundleUtilities.cmake
@@ -0,0 +1,786 @@
+# - Functions to help assemble a standalone bundle application.
+# A collection of CMake utility functions useful for dealing with .app
+# bundles on the Mac and bundle-like directories on any OS.
+#
+# The following functions are provided by this module:
+# fixup_bundle
+# copy_and_fixup_bundle
+# verify_app
+# get_bundle_main_executable
+# get_dotapp_dir
+# get_bundle_and_executable
+# get_bundle_all_executables
+# get_item_key
+# clear_bundle_keys
+# set_bundle_key_values
+# get_bundle_keys
+# copy_resolved_item_into_bundle
+# copy_resolved_framework_into_bundle
+# fixup_bundle_item
+# verify_bundle_prerequisites
+# verify_bundle_symlinks
+# Requires CMake 2.6 or greater because it uses function, break and
+# PARENT_SCOPE. Also depends on GetPrerequisites.cmake.
+#
+# FIXUP_BUNDLE( )
+# Fix up a bundle in-place and make it standalone, such that it can be
+# drag-n-drop copied to another machine and run on that machine as long as all
+# of the system libraries are compatible.
+#
+# If you pass plugins to fixup_bundle as the libs parameter, you should install
+# them or copy them into the bundle before calling fixup_bundle. The "libs"
+# parameter is a list of libraries that must be fixed up, but that cannot be
+# determined by otool output analysis. (i.e., plugins)
+#
+# Gather all the keys for all the executables and libraries in a bundle, and
+# then, for each key, copy each prerequisite into the bundle. Then fix each one
+# up according to its own list of prerequisites.
+#
+# Then clear all the keys and call verify_app on the final bundle to ensure
+# that it is truly standalone.
+#
+# COPY_AND_FIXUP_BUNDLE( )
+# Makes a copy of the bundle at location and then fixes up the
+# new copied bundle in-place at ...
+#
+# VERIFY_APP()
+# Verifies that an application appears valid based on running analysis
+# tools on it. Calls "message(FATAL_ERROR" if the application is not verified.
+#
+# GET_BUNDLE_MAIN_EXECUTABLE( )
+# The result will be the full path name of the bundle's main executable file
+# or an "error:" prefixed string if it could not be determined.
+#
+# GET_DOTAPP_DIR( )
+# Returns the nearest parent dir whose name ends with ".app" given the full
+# path to an executable. If there is no such parent dir, then simply return
+# the dir containing the executable.
+#
+# The returned directory may or may not exist.
+#
+# GET_BUNDLE_AND_EXECUTABLE( )
+# Takes either a ".app" directory name or the name of an executable
+# nested inside a ".app" directory and returns the path to the ".app"
+# directory in and the path to its main executable in
+#
+#
+# GET_BUNDLE_ALL_EXECUTABLES( )
+# Scans the given bundle recursively for all executable files and accumulates
+# them into a variable.
+#
+# GET_ITEM_KEY(- )
+# Given a file (item) name, generate a key that should be unique considering
+# the set of libraries that need copying or fixing up to make a bundle
+# standalone. This is essentially the file name including extension with "."
+# replaced by "_"
+#
+# This key is used as a prefix for CMake variables so that we can associate a
+# set of variables with a given item based on its key.
+#
+# CLEAR_BUNDLE_KEYS()
+# Loop over the list of keys, clearing all the variables associated with each
+# key. After the loop, clear the list of keys itself.
+#
+# Caller of get_bundle_keys should call clear_bundle_keys when done with list
+# of keys.
+#
+# SET_BUNDLE_KEY_VALUES(
-
+# )
+# Add a key to the list (if necessary) for the given item. If added,
+# also set all the variables associated with that key.
+#
+# GET_BUNDLE_KEYS( )
+# Loop over all the executable and library files within the bundle (and given
+# as extra ) and accumulate a list of keys representing them. Set
+# values associated with each key such that we can loop over all of them and
+# copy prerequisite libs into the bundle and then do appropriate
+# install_name_tool fixups.
+#
+# COPY_RESOLVED_ITEM_INTO_BUNDLE( )
+# Copy a resolved item into the bundle if necessary. Copy is not necessary if
+# the resolved_item is "the same as" the resolved_embedded_item.
+#
+# COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE( )
+# Copy a resolved framework into the bundle if necessary. Copy is not necessary
+# if the resolved_item is "the same as" the resolved_embedded_item.
+#
+# By default, BU_COPY_FULL_FRAMEWORK_CONTENTS is not set. If you want full
+# frameworks embedded in your bundles, set BU_COPY_FULL_FRAMEWORK_CONTENTS to
+# ON before calling fixup_bundle. By default,
+# COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE copies the framework dylib itself plus
+# the framework Resources directory.
+#
+# FIXUP_BUNDLE_ITEM( )
+# Get the direct/non-system prerequisites of the resolved embedded item. For
+# each prerequisite, change the way it is referenced to the value of the
+# _EMBEDDED_ITEM keyed variable for that prerequisite. (Most likely changing to
+# an "@executable_path" style reference.)
+#
+# This function requires that the resolved_embedded_item be "inside" the bundle
+# already. In other words, if you pass plugins to fixup_bundle as the libs
+# parameter, you should install them or copy them into the bundle before
+# calling fixup_bundle. The "libs" parameter is a list of libraries that must
+# be fixed up, but that cannot be determined by otool output analysis. (i.e.,
+# plugins)
+#
+# Also, change the id of the item being fixed up to its own _EMBEDDED_ITEM
+# value.
+#
+# Accumulate changes in a local variable and make *one* call to
+# install_name_tool at the end of the function with all the changes at once.
+#
+# If the BU_CHMOD_BUNDLE_ITEMS variable is set then bundle items will be
+# marked writable before install_name_tool tries to change them.
+#
+# VERIFY_BUNDLE_PREREQUISITES( )
+# Verifies that the sum of all prerequisites of all files inside the bundle
+# are contained within the bundle or are "system" libraries, presumed to exist
+# everywhere.
+#
+# VERIFY_BUNDLE_SYMLINKS( )
+# Verifies that any symlinks found in the bundle point to other files that are
+# already also in the bundle... Anything that points to an external file causes
+# this function to fail the verification.
+
+#=============================================================================
+# Copyright 2008-2009 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+# The functions defined in this file depend on the get_prerequisites function
+# (and possibly others) found in:
+#
+get_filename_component(BundleUtilities_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
+include("${BundleUtilities_cmake_dir}/GetPrerequisites.cmake")
+
+
+function(get_bundle_main_executable bundle result_var)
+ set(result "error: '${bundle}/Contents/Info.plist' file does not exist")
+
+ if(EXISTS "${bundle}/Contents/Info.plist")
+ set(result "error: no CFBundleExecutable in '${bundle}/Contents/Info.plist' file")
+ set(line_is_main_executable 0)
+ set(bundle_executable "")
+
+ # Read Info.plist as a list of lines:
+ #
+ set(eol_char "E")
+ file(READ "${bundle}/Contents/Info.plist" info_plist)
+ string(REGEX REPLACE ";" "\\\\;" info_plist "${info_plist}")
+ string(REGEX REPLACE "\n" "${eol_char};" info_plist "${info_plist}")
+
+ # Scan the lines for "CFBundleExecutable" - the line after that
+ # is the name of the main executable.
+ #
+ foreach(line ${info_plist})
+ if(line_is_main_executable)
+ string(REGEX REPLACE "^.*(.*).*$" "\\1" bundle_executable "${line}")
+ break()
+ endif()
+
+ if(line MATCHES "^.*CFBundleExecutable.*$")
+ set(line_is_main_executable 1)
+ endif()
+ endforeach()
+
+ if(NOT "${bundle_executable}" STREQUAL "")
+ if(EXISTS "${bundle}/Contents/MacOS/${bundle_executable}")
+ set(result "${bundle}/Contents/MacOS/${bundle_executable}")
+ else()
+
+ # Ultimate goal:
+ # If not in "Contents/MacOS" then scan the bundle for matching files. If
+ # there is only one executable file that matches, then use it, otherwise
+ # it's an error...
+ #
+ #file(GLOB_RECURSE file_list "${bundle}/${bundle_executable}")
+
+ # But for now, pragmatically, it's an error. Expect the main executable
+ # for the bundle to be in Contents/MacOS, it's an error if it's not:
+ #
+ set(result "error: '${bundle}/Contents/MacOS/${bundle_executable}' does not exist")
+ endif()
+ endif()
+ else()
+ #
+ # More inclusive technique... (This one would work on Windows and Linux
+ # too, if a developer followed the typical Mac bundle naming convention...)
+ #
+ # If there is no Info.plist file, try to find an executable with the same
+ # base name as the .app directory:
+ #
+ endif()
+
+ set(${result_var} "${result}" PARENT_SCOPE)
+endfunction()
+
+
+function(get_dotapp_dir exe dotapp_dir_var)
+ set(s "${exe}")
+
+ if(s MATCHES "^.*/.*\\.app/.*$")
+ # If there is a ".app" parent directory,
+ # ascend until we hit it:
+ # (typical of a Mac bundle executable)
+ #
+ set(done 0)
+ while(NOT ${done})
+ get_filename_component(snamewe "${s}" NAME_WE)
+ get_filename_component(sname "${s}" NAME)
+ get_filename_component(sdir "${s}" PATH)
+ set(s "${sdir}")
+ if(sname MATCHES "\\.app$")
+ set(done 1)
+ set(dotapp_dir "${sdir}/${sname}")
+ endif()
+ endwhile()
+ else()
+ # Otherwise use a directory containing the exe
+ # (typical of a non-bundle executable on Mac, Windows or Linux)
+ #
+ is_file_executable("${s}" is_executable)
+ if(is_executable)
+ get_filename_component(sdir "${s}" PATH)
+ set(dotapp_dir "${sdir}")
+ else()
+ set(dotapp_dir "${s}")
+ endif()
+ endif()
+
+
+ set(${dotapp_dir_var} "${dotapp_dir}" PARENT_SCOPE)
+endfunction()
+
+
+function(get_bundle_and_executable app bundle_var executable_var valid_var)
+ set(valid 0)
+
+ if(EXISTS "${app}")
+ # Is it a directory ending in .app?
+ if(IS_DIRECTORY "${app}")
+ if(app MATCHES "\\.app$")
+ get_bundle_main_executable("${app}" executable)
+ if(EXISTS "${app}" AND EXISTS "${executable}")
+ set(${bundle_var} "${app}" PARENT_SCOPE)
+ set(${executable_var} "${executable}" PARENT_SCOPE)
+ set(valid 1)
+ #message(STATUS "info: handled .app directory case...")
+ else()
+ message(STATUS "warning: *NOT* handled - .app directory case...")
+ endif()
+ else()
+ message(STATUS "warning: *NOT* handled - directory but not .app case...")
+ endif()
+ else()
+ # Is it an executable file?
+ is_file_executable("${app}" is_executable)
+ if(is_executable)
+ get_dotapp_dir("${app}" dotapp_dir)
+ if(EXISTS "${dotapp_dir}")
+ set(${bundle_var} "${dotapp_dir}" PARENT_SCOPE)
+ set(${executable_var} "${app}" PARENT_SCOPE)
+ set(valid 1)
+ #message(STATUS "info: handled executable file in .app dir case...")
+ else()
+ get_filename_component(app_dir "${app}" PATH)
+ set(${bundle_var} "${app_dir}" PARENT_SCOPE)
+ set(${executable_var} "${app}" PARENT_SCOPE)
+ set(valid 1)
+ #message(STATUS "info: handled executable file in any dir case...")
+ endif()
+ else()
+ message(STATUS "warning: *NOT* handled - not .app dir, not executable file...")
+ endif()
+ endif()
+ else()
+ message(STATUS "warning: *NOT* handled - directory/file ${app} does not exist...")
+ endif()
+
+ if(NOT valid)
+ set(${bundle_var} "error: not a bundle" PARENT_SCOPE)
+ set(${executable_var} "error: not a bundle" PARENT_SCOPE)
+ endif()
+
+ set(${valid_var} ${valid} PARENT_SCOPE)
+endfunction()
+
+
+function(get_bundle_all_executables bundle exes_var)
+ set(exes "")
+
+ file(GLOB_RECURSE file_list "${bundle}/*")
+ foreach(f ${file_list})
+ is_file_executable("${f}" is_executable)
+ if(is_executable)
+ set(exes ${exes} "${f}")
+ endif()
+ endforeach()
+
+ set(${exes_var} "${exes}" PARENT_SCOPE)
+endfunction()
+
+
+function(get_item_key item key_var)
+ get_filename_component(item_name "${item}" NAME)
+ if(WIN32)
+ string(TOLOWER "${item_name}" item_name)
+ endif()
+ string(REGEX REPLACE "\\." "_" ${key_var} "${item_name}")
+ set(${key_var} ${${key_var}} PARENT_SCOPE)
+endfunction()
+
+
+function(clear_bundle_keys keys_var)
+ foreach(key ${${keys_var}})
+ set(${key}_ITEM PARENT_SCOPE)
+ set(${key}_RESOLVED_ITEM PARENT_SCOPE)
+ set(${key}_DEFAULT_EMBEDDED_PATH PARENT_SCOPE)
+ set(${key}_EMBEDDED_ITEM PARENT_SCOPE)
+ set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE)
+ set(${key}_COPYFLAG PARENT_SCOPE)
+ endforeach()
+ set(${keys_var} PARENT_SCOPE)
+endfunction()
+
+
+function(set_bundle_key_values keys_var context item exepath dirs copyflag)
+ get_filename_component(item_name "${item}" NAME)
+
+ get_item_key("${item}" key)
+
+ list(LENGTH ${keys_var} length_before)
+ gp_append_unique(${keys_var} "${key}")
+ list(LENGTH ${keys_var} length_after)
+
+ if(NOT length_before EQUAL length_after)
+ gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item)
+
+ gp_item_default_embedded_path("${item}" default_embedded_path)
+
+ if(item MATCHES "[^/]+\\.framework/")
+ # For frameworks, construct the name under the embedded path from the
+ # opening "${item_name}.framework/" to the closing "/${item_name}":
+ #
+ string(REGEX REPLACE "^.*(${item_name}.framework/.*/?${item_name}).*$" "${default_embedded_path}/\\1" embedded_item "${item}")
+ else()
+ # For other items, just use the same name as the original, but in the
+ # embedded path:
+ #
+ set(embedded_item "${default_embedded_path}/${item_name}")
+ endif()
+
+ # Replace @executable_path and resolve ".." references:
+ #
+ string(REPLACE "@executable_path" "${exepath}" resolved_embedded_item "${embedded_item}")
+ get_filename_component(resolved_embedded_item "${resolved_embedded_item}" ABSOLUTE)
+
+ # *But* -- if we are not copying, then force resolved_embedded_item to be
+ # the same as resolved_item. In the case of multiple executables in the
+ # original bundle, using the default_embedded_path results in looking for
+ # the resolved executable next to the main bundle executable. This is here
+ # so that exes in the other sibling directories (like "bin") get fixed up
+ # properly...
+ #
+ if(NOT copyflag)
+ set(resolved_embedded_item "${resolved_item}")
+ endif()
+
+ set(${keys_var} ${${keys_var}} PARENT_SCOPE)
+ set(${key}_ITEM "${item}" PARENT_SCOPE)
+ set(${key}_RESOLVED_ITEM "${resolved_item}" PARENT_SCOPE)
+ set(${key}_DEFAULT_EMBEDDED_PATH "${default_embedded_path}" PARENT_SCOPE)
+ set(${key}_EMBEDDED_ITEM "${embedded_item}" PARENT_SCOPE)
+ set(${key}_RESOLVED_EMBEDDED_ITEM "${resolved_embedded_item}" PARENT_SCOPE)
+ set(${key}_COPYFLAG "${copyflag}" PARENT_SCOPE)
+ else()
+ #message("warning: item key '${key}' already in the list, subsequent references assumed identical to first")
+ endif()
+endfunction()
+
+
+function(get_bundle_keys app libs dirs keys_var)
+ set(${keys_var} PARENT_SCOPE)
+
+ get_bundle_and_executable("${app}" bundle executable valid)
+ if(valid)
+ # Always use the exepath of the main bundle executable for @executable_path
+ # replacements:
+ #
+ get_filename_component(exepath "${executable}" PATH)
+
+ # But do fixups on all executables in the bundle:
+ #
+ get_bundle_all_executables("${bundle}" exes)
+
+ # For each extra lib, accumulate a key as well and then also accumulate
+ # any of its prerequisites. (Extra libs are typically dynamically loaded
+ # plugins: libraries that are prerequisites for full runtime functionality
+ # but that do not show up in otool -L output...)
+ #
+ foreach(lib ${libs})
+ set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0)
+
+ set(prereqs "")
+ get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}")
+ foreach(pr ${prereqs})
+ set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1)
+ endforeach()
+ endforeach()
+
+ # For each executable found in the bundle, accumulate keys as we go.
+ # The list of keys should be complete when all prerequisites of all
+ # binaries in the bundle have been analyzed.
+ #
+ foreach(exe ${exes})
+ # Add the exe itself to the keys:
+ #
+ set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0)
+
+ # Add each prerequisite to the keys:
+ #
+ set(prereqs "")
+ get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}")
+ foreach(pr ${prereqs})
+ set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1)
+ endforeach()
+ endforeach()
+
+ # Propagate values to caller's scope:
+ #
+ set(${keys_var} ${${keys_var}} PARENT_SCOPE)
+ foreach(key ${${keys_var}})
+ set(${key}_ITEM "${${key}_ITEM}" PARENT_SCOPE)
+ set(${key}_RESOLVED_ITEM "${${key}_RESOLVED_ITEM}" PARENT_SCOPE)
+ set(${key}_DEFAULT_EMBEDDED_PATH "${${key}_DEFAULT_EMBEDDED_PATH}" PARENT_SCOPE)
+ set(${key}_EMBEDDED_ITEM "${${key}_EMBEDDED_ITEM}" PARENT_SCOPE)
+ set(${key}_RESOLVED_EMBEDDED_ITEM "${${key}_RESOLVED_EMBEDDED_ITEM}" PARENT_SCOPE)
+ set(${key}_COPYFLAG "${${key}_COPYFLAG}" PARENT_SCOPE)
+ endforeach()
+ endif()
+endfunction()
+
+
+function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
+ if(WIN32)
+ # ignore case on Windows
+ string(TOLOWER "${resolved_item}" resolved_item_compare)
+ string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
+ else()
+ set(resolved_item_compare "${resolved_item}")
+ set(resolved_embedded_item_compare "${resolved_embedded_item}")
+ endif()
+
+ if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
+ message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
+ else()
+ #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
+ if(UNIX AND NOT APPLE)
+ file(RPATH_REMOVE FILE "${resolved_embedded_item}")
+ endif()
+ endif()
+
+endfunction()
+
+
+function(copy_resolved_framework_into_bundle resolved_item resolved_embedded_item)
+ if(WIN32)
+ # ignore case on Windows
+ string(TOLOWER "${resolved_item}" resolved_item_compare)
+ string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
+ else()
+ set(resolved_item_compare "${resolved_item}")
+ set(resolved_embedded_item_compare "${resolved_embedded_item}")
+ endif()
+
+ if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
+ message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
+ else()
+ if(BU_COPY_FULL_FRAMEWORK_CONTENTS)
+ # Full Framework (everything):
+ get_filename_component(resolved_dir "${resolved_item}" PATH)
+ get_filename_component(resolved_dir "${resolved_dir}/../.." ABSOLUTE)
+ get_filename_component(resolved_embedded_dir "${resolved_embedded_item}" PATH)
+ get_filename_component(resolved_embedded_dir "${resolved_embedded_dir}/../.." ABSOLUTE)
+ #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_dir}' '${resolved_embedded_dir}'")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_dir}" "${resolved_embedded_dir}")
+ else()
+ # Framework lib itself:
+ #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
+
+ # Plus Resources, if they exist:
+ string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}")
+ string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}")
+ if(EXISTS "${resolved_resources}")
+ #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_resources}' '${resolved_embedded_resources}'")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_resources}" "${resolved_embedded_resources}")
+ endif()
+ endif()
+ if(UNIX AND NOT APPLE)
+ file(RPATH_REMOVE FILE "${resolved_embedded_item}")
+ endif()
+ endif()
+
+endfunction()
+
+
+function(fixup_bundle_item resolved_embedded_item exepath dirs)
+ # This item's key is "ikey":
+ #
+ get_item_key("${resolved_embedded_item}" ikey)
+
+ # Ensure the item is "inside the .app bundle" -- it should not be fixed up if
+ # it is not in the .app bundle... Otherwise, we'll modify files in the build
+ # tree, or in other varied locations around the file system, with our call to
+ # install_name_tool. Make sure that doesn't happen here:
+ #
+ get_dotapp_dir("${exepath}" exe_dotapp_dir)
+ string(LENGTH "${exe_dotapp_dir}/" exe_dotapp_dir_length)
+ string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length)
+ set(path_too_short 0)
+ set(is_embedded 0)
+ if(${resolved_embedded_item_length} LESS ${exe_dotapp_dir_length})
+ set(path_too_short 1)
+ endif()
+ if(NOT path_too_short)
+ string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring)
+ if("${exe_dotapp_dir}/" STREQUAL "${item_substring}")
+ set(is_embedded 1)
+ endif()
+ endif()
+ if(NOT is_embedded)
+ message(" exe_dotapp_dir/='${exe_dotapp_dir}/'")
+ message(" item_substring='${item_substring}'")
+ message(" resolved_embedded_item='${resolved_embedded_item}'")
+ message("")
+ message("Install or copy the item into the bundle before calling fixup_bundle.")
+ message("Or maybe there's a typo or incorrect path in one of the args to fixup_bundle?")
+ message("")
+ message(FATAL_ERROR "cannot fixup an item that is not in the bundle...")
+ endif()
+
+ set(prereqs "")
+ get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}")
+
+ set(changes "")
+
+ foreach(pr ${prereqs})
+ # Each referenced item's key is "rkey" in the loop:
+ #
+ get_item_key("${pr}" rkey)
+
+ if(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
+ set(changes ${changes} "-change" "${pr}" "${${rkey}_EMBEDDED_ITEM}")
+ else()
+ message("warning: unexpected reference to '${pr}'")
+ endif()
+ endforeach()
+
+ if(BU_CHMOD_BUNDLE_ITEMS)
+ execute_process(COMMAND chmod u+w "${resolved_embedded_item}")
+ endif()
+
+ # Change this item's id and all of its references in one call
+ # to install_name_tool:
+ #
+ execute_process(COMMAND install_name_tool
+ ${changes} -id "${${ikey}_EMBEDDED_ITEM}" "${resolved_embedded_item}"
+ )
+endfunction()
+
+
+function(fixup_bundle app libs dirs)
+ message(STATUS "fixup_bundle")
+ message(STATUS " app='${app}'")
+ message(STATUS " libs='${libs}'")
+ message(STATUS " dirs='${dirs}'")
+
+ get_bundle_and_executable("${app}" bundle executable valid)
+ if(valid)
+ get_filename_component(exepath "${executable}" PATH)
+
+ message(STATUS "fixup_bundle: preparing...")
+ get_bundle_keys("${app}" "${libs}" "${dirs}" keys)
+
+ message(STATUS "fixup_bundle: copying...")
+ list(LENGTH keys n)
+ math(EXPR n ${n}*2)
+
+ set(i 0)
+ foreach(key ${keys})
+ math(EXPR i ${i}+1)
+ if(${${key}_COPYFLAG})
+ message(STATUS "${i}/${n}: copying '${${key}_RESOLVED_ITEM}'")
+ else()
+ message(STATUS "${i}/${n}: *NOT* copying '${${key}_RESOLVED_ITEM}'")
+ endif()
+
+ set(show_status 0)
+ if(show_status)
+ message(STATUS "key='${key}'")
+ message(STATUS "item='${${key}_ITEM}'")
+ message(STATUS "resolved_item='${${key}_RESOLVED_ITEM}'")
+ message(STATUS "default_embedded_path='${${key}_DEFAULT_EMBEDDED_PATH}'")
+ message(STATUS "embedded_item='${${key}_EMBEDDED_ITEM}'")
+ message(STATUS "resolved_embedded_item='${${key}_RESOLVED_EMBEDDED_ITEM}'")
+ message(STATUS "copyflag='${${key}_COPYFLAG}'")
+ message(STATUS "")
+ endif()
+
+ if(${${key}_COPYFLAG})
+ set(item "${${key}_ITEM}")
+ if(item MATCHES "[^/]+\\.framework/")
+ copy_resolved_framework_into_bundle("${${key}_RESOLVED_ITEM}"
+ "${${key}_RESOLVED_EMBEDDED_ITEM}")
+ else()
+ copy_resolved_item_into_bundle("${${key}_RESOLVED_ITEM}"
+ "${${key}_RESOLVED_EMBEDDED_ITEM}")
+ endif()
+ endif()
+ endforeach()
+
+ message(STATUS "fixup_bundle: fixing...")
+ foreach(key ${keys})
+ math(EXPR i ${i}+1)
+ if(APPLE)
+ message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
+ fixup_bundle_item("${${key}_RESOLVED_EMBEDDED_ITEM}" "${exepath}" "${dirs}")
+ else()
+ message(STATUS "${i}/${n}: fix-up not required on this platform '${${key}_RESOLVED_EMBEDDED_ITEM}'")
+ endif()
+ endforeach()
+
+ message(STATUS "fixup_bundle: cleaning up...")
+ clear_bundle_keys(keys)
+
+ message(STATUS "fixup_bundle: verifying...")
+ verify_app("${app}")
+ else()
+ message(SEND_ERROR "error: fixup_bundle: not a valid bundle")
+ endif()
+
+ message(STATUS "fixup_bundle: done")
+endfunction()
+
+
+function(copy_and_fixup_bundle src dst libs dirs)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${src}" "${dst}")
+ fixup_bundle("${dst}" "${libs}" "${dirs}")
+endfunction()
+
+
+function(verify_bundle_prerequisites bundle result_var info_var)
+ set(result 1)
+ set(info "")
+ set(count 0)
+
+ get_bundle_main_executable("${bundle}" main_bundle_exe)
+
+ file(GLOB_RECURSE file_list "${bundle}/*")
+ foreach(f ${file_list})
+ is_file_executable("${f}" is_executable)
+ if(is_executable)
+ get_filename_component(exepath "${f}" PATH)
+ math(EXPR count "${count} + 1")
+
+ message(STATUS "executable file ${count}: ${f}")
+
+ set(prereqs "")
+ get_prerequisites("${f}" prereqs 1 1 "${exepath}" "")
+
+ # On the Mac,
+ # "embedded" and "system" prerequisites are fine... anything else means
+ # the bundle's prerequisites are not verified (i.e., the bundle is not
+ # really "standalone")
+ #
+ # On Windows (and others? Linux/Unix/...?)
+ # "local" and "system" prereqs are fine...
+ #
+ set(external_prereqs "")
+
+ foreach(p ${prereqs})
+ set(p_type "")
+ gp_file_type("${f}" "${p}" p_type)
+
+ if(APPLE)
+ if(NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
+ set(external_prereqs ${external_prereqs} "${p}")
+ endif()
+ else()
+ if(NOT "${p_type}" STREQUAL "local" AND NOT "${p_type}" STREQUAL "system")
+ set(external_prereqs ${external_prereqs} "${p}")
+ endif()
+ endif()
+ endforeach()
+
+ if(external_prereqs)
+ # Found non-system/somehow-unacceptable prerequisites:
+ set(result 0)
+ set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
+ endif()
+ endif()
+ endforeach()
+
+ if(result)
+ set(info "Verified ${count} executable files in '${bundle}'")
+ endif()
+
+ set(${result_var} "${result}" PARENT_SCOPE)
+ set(${info_var} "${info}" PARENT_SCOPE)
+endfunction()
+
+
+function(verify_bundle_symlinks bundle result_var info_var)
+ set(result 1)
+ set(info "")
+ set(count 0)
+
+ # TODO: implement this function for real...
+ # Right now, it is just a stub that verifies unconditionally...
+
+ set(${result_var} "${result}" PARENT_SCOPE)
+ set(${info_var} "${info}" PARENT_SCOPE)
+endfunction()
+
+
+function(verify_app app)
+ set(verified 0)
+ set(info "")
+
+ get_bundle_and_executable("${app}" bundle executable valid)
+
+ message(STATUS "===========================================================================")
+ message(STATUS "Analyzing app='${app}'")
+ message(STATUS "bundle='${bundle}'")
+ message(STATUS "executable='${executable}'")
+ message(STATUS "valid='${valid}'")
+
+ # Verify that the bundle does not have any "external" prerequisites:
+ #
+ verify_bundle_prerequisites("${bundle}" verified info)
+ message(STATUS "verified='${verified}'")
+ message(STATUS "info='${info}'")
+ message(STATUS "")
+
+ if(verified)
+ # Verify that the bundle does not have any symlinks to external files:
+ #
+ verify_bundle_symlinks("${bundle}" verified info)
+ message(STATUS "verified='${verified}'")
+ message(STATUS "info='${info}'")
+ message(STATUS "")
+ endif()
+
+ if(NOT verified)
+ message(FATAL_ERROR "error: verify_app failed")
+ endif()
+endfunction()
diff --git a/ultimmc/cmake/GetGitRevisionDescription.cmake b/ultimmc/cmake/GetGitRevisionDescription.cmake
new file mode 100644
index 0000000..39c2707
--- /dev/null
+++ b/ultimmc/cmake/GetGitRevisionDescription.cmake
@@ -0,0 +1,130 @@
+# - Returns a version string from Git
+#
+# These functions force a re-configure on each git commit so that you can
+# trust the values of the variables in your build system.
+#
+# get_git_head_revision( [ ...])
+#
+# Returns the refspec and sha hash of the current head revision
+#
+# git_describe( [ ...])
+#
+# Returns the results of git describe on the source tree, and adjusting
+# the output so that it tests false if an error occurs.
+#
+# git_get_exact_tag( [ ...])
+#
+# Returns the results of git describe --exact-match on the source tree,
+# and adjusting the output so that it tests false if there was no exact
+# matching tag.
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+if(__get_git_revision_description)
+ return()
+endif()
+set(__get_git_revision_description YES)
+
+# We must run the following at "include" time, not at function call time,
+# to find the path to this module rather than the path to a calling list file
+get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
+
+function(get_git_head_revision _refspecvar _hashvar)
+ set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+ while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
+ set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
+ get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
+ if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
+ # We have reached the root directory, we are not in git
+ set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+ set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+ set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+ endwhile()
+ # check if this is a submodule
+ if(NOT IS_DIRECTORY ${GIT_DIR})
+ file(READ ${GIT_DIR} submodule)
+ string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
+ get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
+ get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
+ endif()
+ set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
+ if(NOT EXISTS "${GIT_DATA}")
+ file(MAKE_DIRECTORY "${GIT_DATA}")
+ endif()
+
+ if(NOT EXISTS "${GIT_DIR}/HEAD")
+ return()
+ endif()
+ set(HEAD_FILE "${GIT_DATA}/HEAD")
+ configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
+
+ configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
+ "${GIT_DATA}/grabRef.cmake"
+ @ONLY)
+ include("${GIT_DATA}/grabRef.cmake")
+
+ set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
+ set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
+endfunction()
+
+function(git_describe _var)
+ if(NOT GIT_FOUND)
+ find_package(Git QUIET)
+ endif()
+ get_git_head_revision(refspec hash)
+ if(NOT GIT_FOUND)
+ set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+ if(NOT hash)
+ set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+
+ # TODO sanitize
+ #if((${ARGN}" MATCHES "&&") OR
+ # (ARGN MATCHES "||") OR
+ # (ARGN MATCHES "\\;"))
+ # message("Please report the following error to the project!")
+ # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
+ #endif()
+
+ #message(STATUS "Arguments to execute_process: ${ARGN}")
+
+ execute_process(COMMAND
+ "${GIT_EXECUTABLE}"
+ describe
+ ${hash}
+ ${ARGN}
+ WORKING_DIRECTORY
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ RESULT_VARIABLE
+ res
+ OUTPUT_VARIABLE
+ out
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(NOT res EQUAL 0)
+ set(out "${out}-${res}-NOTFOUND")
+ endif()
+
+ set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
+
+function(git_get_exact_tag _var)
+ git_describe(out --exact-match ${ARGN})
+ set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
diff --git a/ultimmc/cmake/GetGitRevisionDescription.cmake.in b/ultimmc/cmake/GetGitRevisionDescription.cmake.in
new file mode 100644
index 0000000..04db9a8
--- /dev/null
+++ b/ultimmc/cmake/GetGitRevisionDescription.cmake.in
@@ -0,0 +1,41 @@
+#
+# Internal file for GetGitRevisionDescription.cmake
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(HEAD_HASH)
+
+file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
+
+string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+if(HEAD_CONTENTS MATCHES "ref")
+ # named branch
+ string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
+ if(EXISTS "@GIT_DIR@/${HEAD_REF}")
+ configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+ else()
+ configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
+ file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
+ if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
+ set(HEAD_HASH "${CMAKE_MATCH_1}")
+ endif()
+ endif()
+else()
+ # detached HEAD
+ configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+endif()
+
+if(NOT HEAD_HASH)
+ file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+ string(STRIP "${HEAD_HASH}" HEAD_HASH)
+endif()
diff --git a/ultimmc/cmake/GetPrerequisites.cmake b/ultimmc/cmake/GetPrerequisites.cmake
new file mode 100644
index 0000000..39c2cc6
--- /dev/null
+++ b/ultimmc/cmake/GetPrerequisites.cmake
@@ -0,0 +1,902 @@
+# - Functions to analyze and list executable file prerequisites.
+# This module provides functions to list the .dll, .dylib or .so
+# files that an executable or shared library file depends on. (Its
+# prerequisites.)
+#
+# It uses various tools to obtain the list of required shared library files:
+# dumpbin (Windows)
+# objdump (MinGW on Windows)
+# ldd (Linux/Unix)
+# otool (Mac OSX)
+# The following functions are provided by this module:
+# get_prerequisites
+# list_prerequisites
+# list_prerequisites_by_glob
+# gp_append_unique
+# is_file_executable
+# gp_item_default_embedded_path
+# (projects can override with gp_item_default_embedded_path_override)
+# gp_resolve_item
+# (projects can override with gp_resolve_item_override)
+# gp_resolved_file_type
+# (projects can override with gp_resolved_file_type_override)
+# gp_file_type
+# Requires CMake 2.6 or greater because it uses function, break, return and
+# PARENT_SCOPE.
+#
+# GET_PREREQUISITES(
+# )
+# Get the list of shared library files required by . The list in
+# the variable named should be empty on first entry to
+# this function. On exit, will contain the list of
+# required shared library files.
+#
+# is the full path to an executable file. is the
+# name of a CMake variable to contain the results. must be 0
+# or 1 indicating whether to include or exclude "system" prerequisites. If
+# is set to 1 all prerequisites will be found recursively, if set to
+# 0 only direct prerequisites are listed. is the path to the top
+# level executable used for @executable_path replacment on the Mac. is
+# a list of paths where libraries might be found: these paths are searched
+# first when a target without any path info is given. Then standard system
+# locations are also searched: PATH, Framework locations, /usr/lib...
+#
+# LIST_PREREQUISITES( [ [ []]])
+# Print a message listing the prerequisites of .
+#
+# is the name of a shared library or executable target or the full
+# path to a shared library or executable file. If is set to 1 all
+# prerequisites will be found recursively, if set to 0 only direct
+# prerequisites are listed. must be 0 or 1 indicating whether
+# to include or exclude "system" prerequisites. With set to 0 only
+# the full path names of the prerequisites are printed, set to 1 extra
+# informatin will be displayed.
+#
+# LIST_PREREQUISITES_BY_GLOB( )
+# Print the prerequisites of shared library and executable files matching a
+# globbing pattern. is GLOB or GLOB_RECURSE and is a
+# globbing expression used with "file(GLOB" or "file(GLOB_RECURSE" to retrieve
+# a list of matching files. If a matching file is executable, its prerequisites
+# are listed.
+#
+# Any additional (optional) arguments provided are passed along as the
+# optional arguments to the list_prerequisites calls.
+#
+# GP_APPEND_UNIQUE( )
+# Append to the list variable only if the value is not
+# already in the list.
+#
+# IS_FILE_EXECUTABLE( )
+# Return 1 in if is a binary executable, 0 otherwise.
+#
+# GP_ITEM_DEFAULT_EMBEDDED_PATH(
- )
+# Return the path that others should refer to the item by when the item
+# is embedded inside a bundle.
+#
+# Override on a per-project basis by providing a project-specific
+# gp_item_default_embedded_path_override function.
+#
+# GP_RESOLVE_ITEM(
- )
+# Resolve an item into an existing full path file.
+#
+# Override on a per-project basis by providing a project-specific
+# gp_resolve_item_override function.
+#
+# GP_RESOLVED_FILE_TYPE( )
+# Return the type of with respect to . String
+# describing type of prerequisite is returned in variable named .
+#
+# Use and if necessary to resolve non-absolute
+# values -- but only for non-embedded items.
+#
+# Possible types are:
+# system
+# local
+# embedded
+# other
+# Override on a per-project basis by providing a project-specific
+# gp_resolved_file_type_override function.
+#
+# GP_FILE_TYPE( )
+# Return the type of with respect to . String
+# describing type of prerequisite is returned in variable named .
+#
+# Possible types are:
+# system
+# local
+# embedded
+# other
+
+#=============================================================================
+# Copyright 2008-2009 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+function(gp_append_unique list_var value)
+ set(contains 0)
+
+ foreach(item ${${list_var}})
+ if("${item}" STREQUAL "${value}")
+ set(contains 1)
+ break()
+ endif()
+ endforeach()
+
+ if(NOT contains)
+ set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+
+function(is_file_executable file result_var)
+ #
+ # A file is not executable until proven otherwise:
+ #
+ set(${result_var} 0 PARENT_SCOPE)
+
+ get_filename_component(file_full "${file}" ABSOLUTE)
+ string(TOLOWER "${file_full}" file_full_lower)
+
+ # If file name ends in .exe on Windows, *assume* executable:
+ #
+ if(WIN32 AND NOT UNIX)
+ if("${file_full_lower}" MATCHES "\\.exe$")
+ set(${result_var} 1 PARENT_SCOPE)
+ return()
+ endif()
+
+ # A clause could be added here that uses output or return value of dumpbin
+ # to determine ${result_var}. In 99%+? practical cases, the exe name
+ # match will be sufficient...
+ #
+ endif()
+
+ # Use the information returned from the Unix shell command "file" to
+ # determine if ${file_full} should be considered an executable file...
+ #
+ # If the file command's output contains "executable" and does *not* contain
+ # "text" then it is likely an executable suitable for prerequisite analysis
+ # via the get_prerequisites macro.
+ #
+ if(UNIX)
+ if(NOT file_cmd)
+ find_program(file_cmd "file")
+ mark_as_advanced(file_cmd)
+ endif()
+
+ if(file_cmd)
+ execute_process(COMMAND "${file_cmd}" "${file_full}"
+ OUTPUT_VARIABLE file_ov
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ # Replace the name of the file in the output with a placeholder token
+ # (the string " _file_full_ ") so that just in case the path name of
+ # the file contains the word "text" or "executable" we are not fooled
+ # into thinking "the wrong thing" because the file name matches the
+ # other 'file' command output we are looking for...
+ #
+ string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}")
+ string(TOLOWER "${file_ov}" file_ov)
+
+ #message(STATUS "file_ov='${file_ov}'")
+ if("${file_ov}" MATCHES "executable")
+ #message(STATUS "executable!")
+ if("${file_ov}" MATCHES "text")
+ #message(STATUS "but text, so *not* a binary executable!")
+ else()
+ set(${result_var} 1 PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ # Also detect position independent executables on Linux,
+ # where "file" gives "shared object ... (uses shared libraries)"
+ if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)")
+ set(${result_var} 1 PARENT_SCOPE)
+ return()
+ endif()
+
+ # "file" version 5.22 does not print "(used shared libraries)"
+ # but uses "interpreter"
+ if("${file_ov}" MATCHES "shared object.*interpreter")
+ set(${result_var} 1 PARENT_SCOPE)
+ return()
+ endif()
+
+ else()
+ message(STATUS "warning: No 'file' command, skipping execute_process...")
+ endif()
+ endif()
+endfunction()
+
+
+function(gp_item_default_embedded_path item default_embedded_path_var)
+
+ # On Windows and Linux, "embed" prerequisites in the same directory
+ # as the executable by default:
+ #
+ set(path "@executable_path")
+ set(overridden 0)
+
+ # On the Mac, relative to the executable depending on the type
+ # of the thing we are embedding:
+ #
+ if(APPLE)
+ #
+ # The assumption here is that all executables in the bundle will be
+ # in same-level-directories inside the bundle. The parent directory
+ # of an executable inside the bundle should be MacOS or a sibling of
+ # MacOS and all embedded paths returned from here will begin with
+ # "@executable_path/../" and will work from all executables in all
+ # such same-level-directories inside the bundle.
+ #
+
+ # By default, embed things right next to the main bundle executable:
+ #
+ set(path "@executable_path/../../Contents/MacOS")
+
+ # Embed .dylibs right next to the main bundle executable:
+ #
+ if(item MATCHES "\\.dylib$")
+ set(path "@executable_path/../MacOS")
+ set(overridden 1)
+ endif()
+
+ # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS):
+ #
+ if(NOT overridden)
+ if(item MATCHES "[^/]+\\.framework/")
+ set(path "@executable_path/../Frameworks")
+ set(overridden 1)
+ endif()
+ endif()
+ endif()
+
+ # Provide a hook so that projects can override the default embedded location
+ # of any given library by whatever logic they choose:
+ #
+ if(COMMAND gp_item_default_embedded_path_override)
+ gp_item_default_embedded_path_override("${item}" path)
+ endif()
+
+ set(${default_embedded_path_var} "${path}" PARENT_SCOPE)
+endfunction()
+
+
+function(gp_resolve_item context item exepath dirs resolved_item_var)
+ set(resolved 0)
+ set(resolved_item "${item}")
+
+ # Is it already resolved?
+ #
+ if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
+ set(resolved 1)
+ endif()
+
+ if(NOT resolved)
+ if(item MATCHES "@executable_path")
+ #
+ # @executable_path references are assumed relative to exepath
+ #
+ string(REPLACE "@executable_path" "${exepath}" ri "${item}")
+ get_filename_component(ri "${ri}" ABSOLUTE)
+
+ if(EXISTS "${ri}")
+ #message(STATUS "info: embedded item exists (${ri})")
+ set(resolved 1)
+ set(resolved_item "${ri}")
+ else()
+ message(STATUS "warning: embedded item does not exist '${ri}'")
+ endif()
+ endif()
+ endif()
+
+ if(NOT resolved)
+ if(item MATCHES "@loader_path")
+ #
+ # @loader_path references are assumed relative to the
+ # PATH of the given "context" (presumably another library)
+ #
+ get_filename_component(contextpath "${context}" PATH)
+ string(REPLACE "@loader_path" "${contextpath}" ri "${item}")
+ get_filename_component(ri "${ri}" ABSOLUTE)
+
+ if(EXISTS "${ri}")
+ #message(STATUS "info: embedded item exists (${ri})")
+ set(resolved 1)
+ set(resolved_item "${ri}")
+ else()
+ message(STATUS "warning: embedded item does not exist '${ri}'")
+ endif()
+ endif()
+ endif()
+
+ if(NOT resolved)
+ if(item MATCHES "@rpath")
+ #
+ # @rpath references are relative to the paths built into the binaries with -rpath
+ # We handle this case like we do for other Unixes
+ #
+ string(REPLACE "@rpath/" "" norpath_item "${item}")
+
+ set(ri "ri-NOTFOUND")
+ find_file(ri "${norpath_item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
+ if(ri)
+ #message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
+ set(resolved 1)
+ set(resolved_item "${ri}")
+ set(ri "ri-NOTFOUND")
+ endif()
+
+ endif()
+ endif()
+
+ if(NOT resolved)
+ set(ri "ri-NOTFOUND")
+ find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
+ find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
+ if(ri)
+ #message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
+ set(resolved 1)
+ set(resolved_item "${ri}")
+ set(ri "ri-NOTFOUND")
+ endif()
+ endif()
+
+ if(NOT resolved)
+ if(item MATCHES "[^/]+\\.framework/")
+ set(fw "fw-NOTFOUND")
+ find_file(fw "${item}"
+ "~/Library/Frameworks"
+ "/Library/Frameworks"
+ "/System/Library/Frameworks"
+ )
+ if(fw)
+ #message(STATUS "info: 'find_file' found framework (${fw})")
+ set(resolved 1)
+ set(resolved_item "${fw}")
+ set(fw "fw-NOTFOUND")
+ endif()
+ endif()
+ endif()
+
+ # Using find_program on Windows will find dll files that are in the PATH.
+ # (Converting simple file names into full path names if found.)
+ #
+ if(WIN32 AND NOT UNIX)
+ if(NOT resolved)
+ set(ri "ri-NOTFOUND")
+ find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH)
+ find_program(ri "${item}" PATHS "${exepath};${dirs}")
+ if(ri)
+ #message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
+ set(resolved 1)
+ set(resolved_item "${ri}")
+ set(ri "ri-NOTFOUND")
+ endif()
+ endif()
+ endif()
+
+ # Provide a hook so that projects can override item resolution
+ # by whatever logic they choose:
+ #
+ if(COMMAND gp_resolve_item_override)
+ gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved)
+ endif()
+
+ if(NOT resolved)
+ message(STATUS "
+warning: cannot resolve item '${item}'
+
+ possible problems:
+ need more directories?
+ need to use InstallRequiredSystemLibraries?
+ run in install tree instead of build tree?
+")
+# message(STATUS "
+#******************************************************************************
+#warning: cannot resolve item '${item}'
+#
+# possible problems:
+# need more directories?
+# need to use InstallRequiredSystemLibraries?
+# run in install tree instead of build tree?
+#
+# context='${context}'
+# item='${item}'
+# exepath='${exepath}'
+# dirs='${dirs}'
+# resolved_item_var='${resolved_item_var}'
+#******************************************************************************
+#")
+ endif()
+
+ set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
+endfunction()
+
+
+function(gp_resolved_file_type original_file file exepath dirs type_var)
+ #message(STATUS "**")
+
+ if(NOT IS_ABSOLUTE "${original_file}")
+ message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file")
+ endif()
+
+ set(is_embedded 0)
+ set(is_local 0)
+ set(is_system 0)
+
+ set(resolved_file "${file}")
+
+ if("${file}" MATCHES "^@(executable|loader)_path")
+ set(is_embedded 1)
+ endif()
+
+ if(NOT is_embedded)
+ if(NOT IS_ABSOLUTE "${file}")
+ gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file)
+ endif()
+
+ string(TOLOWER "${original_file}" original_lower)
+ string(TOLOWER "${resolved_file}" lower)
+
+ if(UNIX)
+ if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
+ set(is_system 1)
+ endif()
+ endif()
+
+ if(APPLE)
+ if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)")
+ set(is_system 1)
+ endif()
+ endif()
+
+ if(WIN32)
+ string(TOLOWER "$ENV{SystemRoot}" sysroot)
+ string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}")
+
+ string(TOLOWER "$ENV{windir}" windir)
+ string(REGEX REPLACE "\\\\" "/" windir "${windir}")
+
+ if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
+ set(is_system 1)
+ endif()
+
+ if(UNIX)
+ # if cygwin, we can get the properly formed windows paths from cygpath
+ find_program(CYGPATH_EXECUTABLE cygpath)
+
+ if(CYGPATH_EXECUTABLE)
+ execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W
+ OUTPUT_VARIABLE env_windir
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S
+ OUTPUT_VARIABLE env_sysdir
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ string(TOLOWER "${env_windir}" windir)
+ string(TOLOWER "${env_sysdir}" sysroot)
+
+ if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
+ set(is_system 1)
+ endif()
+ endif()
+ endif()
+ endif()
+
+ if(NOT is_system)
+ get_filename_component(original_path "${original_lower}" PATH)
+ get_filename_component(path "${lower}" PATH)
+ if("${original_path}" STREQUAL "${path}")
+ set(is_local 1)
+ else()
+ string(LENGTH "${original_path}/" original_length)
+ string(LENGTH "${lower}" path_length)
+ if(${path_length} GREATER ${original_length})
+ string(SUBSTRING "${lower}" 0 ${original_length} path)
+ if("${original_path}/" STREQUAL "${path}")
+ set(is_embedded 1)
+ endif()
+ endif()
+ endif()
+ endif()
+ endif()
+
+ # Return type string based on computed booleans:
+ #
+ set(type "other")
+
+ if(is_system)
+ set(type "system")
+ elseif(is_embedded)
+ set(type "embedded")
+ elseif(is_local)
+ set(type "local")
+ endif()
+
+ #message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'")
+ #message(STATUS " type: '${type}'")
+
+ if(NOT is_embedded)
+ if(NOT IS_ABSOLUTE "${resolved_file}")
+ if(lower MATCHES "^msvc[^/]+dll" AND is_system)
+ message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'")
+ else()
+ message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect")
+ endif()
+ endif()
+ endif()
+
+ # Provide a hook so that projects can override the decision on whether a
+ # library belongs to the system or not by whatever logic they choose:
+ #
+ if(COMMAND gp_resolved_file_type_override)
+ gp_resolved_file_type_override("${resolved_file}" type)
+ endif()
+
+ set(${type_var} "${type}" PARENT_SCOPE)
+
+ #message(STATUS "**")
+endfunction()
+
+
+function(gp_file_type original_file file type_var)
+ if(NOT IS_ABSOLUTE "${original_file}")
+ message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file")
+ endif()
+
+ get_filename_component(exepath "${original_file}" PATH)
+
+ set(type "")
+ gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type)
+
+ set(${type_var} "${type}" PARENT_SCOPE)
+endfunction()
+
+
+function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
+ set(verbose 0)
+ set(eol_char "E")
+
+ if(NOT IS_ABSOLUTE "${target}")
+ message("warning: target '${target}' is not absolute...")
+ endif()
+
+ if(NOT EXISTS "${target}")
+ message("warning: target '${target}' does not exist...")
+ endif()
+
+ set(gp_cmd_paths ${gp_cmd_paths}
+ "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"
+ "C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"
+ "C:/Program Files/Microsoft Visual Studio 8/VC/BIN"
+ "C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"
+ "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"
+ "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"
+ "/usr/local/bin"
+ "/usr/bin"
+ )
+
+ #
+ #
+ # Try to choose the right tool by default. Caller can set gp_tool prior to
+ # calling this function to force using a different tool.
+ #
+ if("${gp_tool}" STREQUAL "")
+ set(gp_tool "ldd")
+
+ if(APPLE)
+ set(gp_tool "otool")
+ endif()
+
+ if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har!
+ find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
+ if(gp_dumpbin)
+ set(gp_tool "dumpbin")
+ else() # Try harder. Maybe we're on MinGW
+ set(gp_tool "objdump")
+ endif()
+ endif()
+ endif()
+
+ find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths})
+
+ if(NOT gp_cmd)
+ message(FATAL_ERROR "FATAL ERROR: could not find '${gp_tool}' - cannot analyze prerequisites!")
+ return()
+ endif()
+
+ set(gp_tool_known 0)
+
+ if("${gp_tool}" STREQUAL "ldd")
+ set(gp_cmd_args "")
+ set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$")
+ set(gp_regex_error "not found${eol_char}$")
+ set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
+ set(gp_regex_cmp_count 1)
+ set(gp_tool_known 1)
+ endif()
+
+ if("${gp_tool}" STREQUAL "otool")
+ set(gp_cmd_args "-L")
+ set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
+ set(gp_regex_error "")
+ set(gp_regex_fallback "")
+ set(gp_regex_cmp_count 3)
+ set(gp_tool_known 1)
+ endif()
+
+ if("${gp_tool}" STREQUAL "dumpbin")
+ set(gp_cmd_args "/dependents")
+ set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$")
+ set(gp_regex_error "")
+ set(gp_regex_fallback "")
+ set(gp_regex_cmp_count 1)
+ set(gp_tool_known 1)
+ endif()
+
+ if("${gp_tool}" STREQUAL "objdump")
+ set(gp_cmd_args "-p")
+ set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
+ set(gp_regex_error "")
+ set(gp_regex_fallback "")
+ set(gp_regex_cmp_count 1)
+ set(gp_tool_known 1)
+ endif()
+
+ if(NOT gp_tool_known)
+ message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...")
+ message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'")
+ message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.")
+ return()
+ endif()
+
+
+ if("${gp_tool}" STREQUAL "dumpbin")
+ # When running dumpbin, it also needs the "Common7/IDE" directory in the
+ # PATH. It will already be in the PATH if being run from a Visual Studio
+ # command prompt. Add it to the PATH here in case we are running from a
+ # different command prompt.
+ #
+ get_filename_component(gp_cmd_dir "${gp_cmd}" PATH)
+ get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE)
+ # Use cmake paths as a user may have a PATH element ending with a backslash.
+ # This will escape the list delimiter and create havoc!
+ if(EXISTS "${gp_cmd_dlls_dir}")
+ # only add to the path if it is not already in the path
+ set(gp_found_cmd_dlls_dir 0)
+ file(TO_CMAKE_PATH "$ENV{PATH}" env_path)
+ foreach(gp_env_path_element ${env_path})
+ if("${gp_env_path_element}" STREQUAL "${gp_cmd_dlls_dir}")
+ set(gp_found_cmd_dlls_dir 1)
+ endif()
+ endforeach()
+
+ if(NOT gp_found_cmd_dlls_dir)
+ file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir)
+ set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}")
+ endif()
+ endif()
+ endif()
+ #
+ #
+
+ if("${gp_tool}" STREQUAL "ldd")
+ set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
+ foreach(dir ${exepath} ${dirs})
+ set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}")
+ endforeach()
+ endif()
+
+
+ # Track new prerequisites at each new level of recursion. Start with an
+ # empty list at each level:
+ #
+ set(unseen_prereqs)
+
+ # Run gp_cmd on the target:
+ #
+ execute_process(
+ COMMAND ${gp_cmd} ${gp_cmd_args} ${target}
+ OUTPUT_VARIABLE gp_cmd_ov
+ )
+
+ if("${gp_tool}" STREQUAL "ldd")
+ set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
+ endif()
+
+ if(verbose)
+ message(STATUS "")
+ message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
+ message(STATUS "")
+ endif()
+
+ get_filename_component(target_dir "${target}" PATH)
+
+ # Convert to a list of lines:
+ #
+ string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}")
+ string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}")
+
+ # check for install id and remove it from list, since otool -L can include a
+ # reference to itself
+ set(gp_install_id)
+ if("${gp_tool}" STREQUAL "otool")
+ execute_process(
+ COMMAND otool -D ${target}
+ OUTPUT_VARIABLE gp_install_id_ov
+ )
+ # second line is install name
+ string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}")
+ if(gp_install_id)
+ # trim
+ string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}")
+ #message("INSTALL ID is \"${gp_install_id}\"")
+ endif()
+ endif()
+
+ # Analyze each line for file names that match the regular expression:
+ #
+ foreach(candidate ${candidates})
+ if("${candidate}" MATCHES "${gp_regex}")
+
+ # Extract information from each candidate:
+ if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}")
+ string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}")
+ else()
+ string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}")
+ endif()
+
+ if(gp_regex_cmp_count GREATER 1)
+ string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}")
+ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
+ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
+ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
+ endif()
+
+ if(gp_regex_cmp_count GREATER 2)
+ string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}")
+ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
+ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
+ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
+ endif()
+
+ # Use the raw_item as the list entries returned by this function. Use the
+ # gp_resolve_item function to resolve it to an actual full path file if
+ # necessary.
+ #
+ set(item "${raw_item}")
+
+ # Add each item unless it is excluded:
+ #
+ set(add_item 1)
+
+ if("${item}" STREQUAL "${gp_install_id}")
+ set(add_item 0)
+ endif()
+
+ if(add_item AND ${exclude_system})
+ set(type "")
+ gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type)
+
+ if("${type}" STREQUAL "system")
+ set(add_item 0)
+ endif()
+ endif()
+
+ if(add_item)
+ list(LENGTH ${prerequisites_var} list_length_before_append)
+ gp_append_unique(${prerequisites_var} "${item}")
+ list(LENGTH ${prerequisites_var} list_length_after_append)
+
+ if(${recurse})
+ # If item was really added, this is the first time we have seen it.
+ # Add it to unseen_prereqs so that we can recursively add *its*
+ # prerequisites...
+ #
+ # But first: resolve its name to an absolute full path name such
+ # that the analysis tools can simply accept it as input.
+ #
+ if(NOT list_length_before_append EQUAL list_length_after_append)
+ gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item)
+ set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
+ endif()
+ endif()
+ endif()
+ else()
+ if(verbose)
+ message(STATUS "ignoring non-matching line: '${candidate}'")
+ endif()
+ endif()
+ endforeach()
+
+ list(LENGTH ${prerequisites_var} prerequisites_var_length)
+ if(prerequisites_var_length GREATER 0)
+ list(SORT ${prerequisites_var})
+ endif()
+ if(${recurse})
+ set(more_inputs ${unseen_prereqs})
+ foreach(input ${more_inputs})
+ get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}")
+ endforeach()
+ endif()
+
+ set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE)
+endfunction()
+
+
+function(list_prerequisites target)
+ if("${ARGV1}" STREQUAL "")
+ set(all 1)
+ else()
+ set(all "${ARGV1}")
+ endif()
+
+ if("${ARGV2}" STREQUAL "")
+ set(exclude_system 0)
+ else()
+ set(exclude_system "${ARGV2}")
+ endif()
+
+ if("${ARGV3}" STREQUAL "")
+ set(verbose 0)
+ else()
+ set(verbose "${ARGV3}")
+ endif()
+
+ set(count 0)
+ set(count_str "")
+ set(print_count "${verbose}")
+ set(print_prerequisite_type "${verbose}")
+ set(print_target "${verbose}")
+ set(type_str "")
+
+ get_filename_component(exepath "${target}" PATH)
+
+ set(prereqs "")
+ get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "")
+
+ if(print_target)
+ message(STATUS "File '${target}' depends on:")
+ endif()
+
+ foreach(d ${prereqs})
+ math(EXPR count "${count} + 1")
+
+ if(print_count)
+ set(count_str "${count}. ")
+ endif()
+
+ if(print_prerequisite_type)
+ gp_file_type("${target}" "${d}" type)
+ set(type_str " (${type})")
+ endif()
+
+ message(STATUS "${count_str}${d}${type_str}")
+ endforeach()
+endfunction()
+
+
+function(list_prerequisites_by_glob glob_arg glob_exp)
+ message(STATUS "=============================================================================")
+ message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'")
+ message(STATUS "")
+ file(${glob_arg} file_list ${glob_exp})
+ foreach(f ${file_list})
+ is_file_executable("${f}" is_f_executable)
+ if(is_f_executable)
+ message(STATUS "=============================================================================")
+ list_prerequisites("${f}" ${ARGN})
+ message(STATUS "")
+ endif()
+ endforeach()
+endfunction()
diff --git a/ultimmc/cmake/GitFunctions.cmake b/ultimmc/cmake/GitFunctions.cmake
new file mode 100644
index 0000000..a055b5d
--- /dev/null
+++ b/ultimmc/cmake/GitFunctions.cmake
@@ -0,0 +1,37 @@
+if(__GITFUNCTIONS_CMAKE__)
+ return()
+endif()
+set(__GITFUNCTIONS_CMAKE__ TRUE)
+
+find_package(Git QUIET)
+
+include(CMakeParseArguments)
+
+if(GIT_FOUND)
+ function(git_run)
+ set(oneValueArgs OUTPUT_VAR DEFAULT)
+ set(multiValueArgs COMMAND)
+ cmake_parse_arguments(GIT_RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ execute_process(COMMAND ${GIT_EXECUTABLE} ${GIT_RUN_COMMAND}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ RESULT_VARIABLE GIT_RESULTVAR
+ OUTPUT_VARIABLE GIT_OUTVAR
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ if(GIT_RESULTVAR EQUAL 0)
+ set(${GIT_RUN_OUTPUT_VAR} "${GIT_OUTVAR}" PARENT_SCOPE)
+ else()
+ set(${GIT_RUN_OUTPUT_VAR} ${GIT_RUN_DEFAULT})
+ message(STATUS "Failed to run Git: ${GIT_OUTVAR}")
+ endif()
+ endfunction()
+else()
+ function(git_run)
+ set(oneValueArgs OUTPUT_VAR DEFAULT)
+ set(multiValueArgs COMMAND)
+ cmake_parse_arguments(GIT_RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+ set(${GIT_RUN_OUTPUT_VAR} ${GIT_RUN_DEFAULT})
+ endfunction(git_run)
+endif()
diff --git a/ultimmc/cmake/MacOSXBundleInfo.plist.in b/ultimmc/cmake/MacOSXBundleInfo.plist.in
new file mode 100644
index 0000000..3302244
--- /dev/null
+++ b/ultimmc/cmake/MacOSXBundleInfo.plist.in
@@ -0,0 +1,42 @@
+
+
+
+
+ NSPrincipalClass
+ NSApplication
+ NSHighResolutionCapable
+ True
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${MACOSX_BUNDLE_EXECUTABLE_NAME}
+ CFBundleGetInfoString
+ ${MACOSX_BUNDLE_INFO_STRING}
+ CFBundleIconFile
+ ${MACOSX_BUNDLE_ICON_FILE}
+ CFBundleIdentifier
+ ${MACOSX_BUNDLE_GUI_IDENTIFIER}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleLongVersionString
+ ${MACOSX_BUNDLE_LONG_VERSION_STRING}
+ CFBundleName
+ ${MACOSX_BUNDLE_BUNDLE_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ ${MACOSX_BUNDLE_SHORT_VERSION_STRING}
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ ${MACOSX_BUNDLE_BUNDLE_VERSION}
+ CSResourcesFileMapped
+
+ LSRequiresCarbon
+
+ NSHumanReadableCopyright
+ ${MACOSX_BUNDLE_COPYRIGHT}
+ NSMicrophoneUsageDescription
+ MultiMC does not need access to your microphone for itself. Likely it's requesting it on behalf of a Minecraft mod you installed, which needs to access your microphone in order to function properly.
+
+
diff --git a/ultimmc/cmake/QMakeQuery.cmake b/ultimmc/cmake/QMakeQuery.cmake
new file mode 100644
index 0000000..bf0fe96
--- /dev/null
+++ b/ultimmc/cmake/QMakeQuery.cmake
@@ -0,0 +1,14 @@
+if(__QMAKEQUERY_CMAKE__)
+ return()
+endif()
+set(__QMAKEQUERY_CMAKE__ TRUE)
+
+get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
+
+function(QUERY_QMAKE VAR RESULT)
+ exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
+ if(NOT return_code)
+ file(TO_CMAKE_PATH "${output}" output)
+ set(${RESULT} ${output} PARENT_SCOPE)
+ endif(NOT return_code)
+endfunction(QUERY_QMAKE)
diff --git a/ultimmc/cmake/UnitTest.cmake b/ultimmc/cmake/UnitTest.cmake
new file mode 100644
index 0000000..9f2bc26
--- /dev/null
+++ b/ultimmc/cmake/UnitTest.cmake
@@ -0,0 +1,48 @@
+find_package(Qt5Test REQUIRED)
+
+set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR})
+
+message(${TEST_RESOURCE_PATH})
+
+function(add_unit_test name)
+ set(options "")
+ set(oneValueArgs DATA)
+ set(multiValueArgs SOURCES LIBS)
+
+ cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+ if(WIN32)
+ add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
+ else()
+ add_executable(${name}_test ${OPT_SOURCES})
+ endif()
+
+ if(NOT "${OPT_DATA}" STREQUAL "")
+ set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
+ set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
+ message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
+ string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
+ if(UNIX)
+ # on unix we get the third / from the filename
+ set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
+ else()
+ # we don't on windows, so we have to add it ourselves
+ set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
+ endif()
+ if(NOT TARGET "${DATA_TARGET_NAME}")
+ add_custom_target(${DATA_TARGET_NAME})
+ add_dependencies(${name}_test ${DATA_TARGET_NAME})
+ add_custom_command(
+ TARGET ${DATA_TARGET_NAME}
+ COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ endif()
+ endif()
+
+ target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
+
+ target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
+
+ add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endfunction()
diff --git a/ultimmc/cmake/UnitTest/TestUtil.h b/ultimmc/cmake/UnitTest/TestUtil.h
new file mode 100644
index 0000000..ebe3c66
--- /dev/null
+++ b/ultimmc/cmake/UnitTest/TestUtil.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#define expandstr(s) expandstr2(s)
+#define expandstr2(s) #s
+
+class TestsInternal
+{
+public:
+ static QByteArray readFile(const QString &fileName)
+ {
+ QFile f(fileName);
+ f.open(QFile::ReadOnly);
+ return f.readAll();
+ }
+ static QString readFileUtf8(const QString &fileName)
+ {
+ return QString::fromUtf8(readFile(fileName));
+ }
+};
+
+#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
+#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))
+
diff --git a/ultimmc/cmake/UnitTest/generate_test_data.cmake b/ultimmc/cmake/UnitTest/generate_test_data.cmake
new file mode 100644
index 0000000..d0bd4ab
--- /dev/null
+++ b/ultimmc/cmake/UnitTest/generate_test_data.cmake
@@ -0,0 +1,23 @@
+# Copy files from source directory to destination directory, substituting any
+# variables. Create destination directory if it does not exist.
+
+function(configure_files srcDir destDir)
+ make_directory(${destDir})
+
+ file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*)
+ foreach(templateFile ${templateFiles})
+ set(srcTemplatePath ${srcDir}/${templateFile})
+ if(NOT IS_DIRECTORY ${srcTemplatePath})
+ configure_file(
+ ${srcTemplatePath}
+ ${destDir}/${templateFile}
+ @ONLY
+ NEWLINE_STYLE LF
+ )
+ else()
+ configure_files("${srcTemplatePath}" "${destDir}/${templateFile}")
+ endif()
+ endforeach()
+endfunction()
+
+configure_files(${SOURCE} ${DESTINATION})
\ No newline at end of file
diff --git a/ultimmc/cmake/UnitTest/test.manifest b/ultimmc/cmake/UnitTest/test.manifest
new file mode 100644
index 0000000..dc5f9d8
--- /dev/null
+++ b/ultimmc/cmake/UnitTest/test.manifest
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Custom Minecraft launcher for managing multiple installs.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ultimmc/cmake/UnitTest/test.rc b/ultimmc/cmake/UnitTest/test.rc
new file mode 100644
index 0000000..9fe4147
--- /dev/null
+++ b/ultimmc/cmake/UnitTest/test.rc
@@ -0,0 +1,28 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include
+
+1 RT_MANIFEST "test.manifest"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 1,0,0,0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "FileDescription", "Testcase"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "ProductName", "Launcher Testcase"
+ VALUE "ProductVersion", "5"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04b0 // Unicode
+ END
+END
diff --git a/ultimmc/cmake/UseJava.cmake b/ultimmc/cmake/UseJava.cmake
new file mode 100644
index 0000000..1a5ef10
--- /dev/null
+++ b/ultimmc/cmake/UseJava.cmake
@@ -0,0 +1,881 @@
+# - Use Module for Java
+# This file provides functions for Java. It is assumed that FindJava.cmake
+# has already been loaded. See FindJava.cmake for information on how to
+# load Java into your CMake project.
+#
+# add_jar(TARGET_NAME SRC1 SRC2 .. SRCN RCS1 RCS2 .. RCSN)
+#
+# This command creates a .jar. It compiles the given source
+# files (SRC) and adds the given resource files (RCS) to the jar file.
+# If only resource files are given then just a jar file is created.
+#
+# Additional instructions:
+# To add compile flags to the target you can set these flags with
+# the following variable:
+#
+# set(CMAKE_JAVA_COMPILE_FLAGS -nowarn)
+#
+# To add a path or a jar file to the class path you can do this
+# with the CMAKE_JAVA_INCLUDE_PATH variable.
+#
+# set(CMAKE_JAVA_INCLUDE_PATH /usr/share/java/shibboleet.jar)
+#
+# To use a different output name for the target you can set it with:
+#
+# set(CMAKE_JAVA_TARGET_OUTPUT_NAME shibboleet.jar)
+# add_jar(foobar foobar.java)
+#
+# To use a different output directory than CMAKE_CURRENT_BINARY_DIR
+# you can set it with:
+#
+# set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/bin)
+#
+# To define an entry point in your jar you can set it with:
+#
+# set(CMAKE_JAVA_JAR_ENTRY_POINT com/examples/MyProject/Main)
+#
+# To add a VERSION to the target output name you can set it using
+# CMAKE_JAVA_TARGET_VERSION. This will create a jar file with the name
+# shibboleet-1.0.0.jar and will create a symlink shibboleet.jar
+# pointing to the jar with the version information.
+#
+# set(CMAKE_JAVA_TARGET_VERSION 1.2.0)
+# add_jar(shibboleet shibbotleet.java)
+#
+# If the target is a JNI library, utilize the following commands to
+# create a JNI symbolic link:
+#
+# set(CMAKE_JNI_TARGET TRUE)
+# set(CMAKE_JAVA_TARGET_VERSION 1.2.0)
+# add_jar(shibboleet shibbotleet.java)
+# install_jar(shibboleet ${LIB_INSTALL_DIR}/shibboleet)
+# install_jni_symlink(shibboleet ${JAVA_LIB_INSTALL_DIR})
+#
+# If a single target needs to produce more than one jar from its
+# java source code, to prevent the accumulation of duplicate class
+# files in subsequent jars, set/reset CMAKE_JAR_CLASSES_PREFIX prior
+# to calling the add_jar() function:
+#
+# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/foo)
+# add_jar(foo foo.java)
+#
+# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/bar)
+# add_jar(bar bar.java)
+#
+# Target Properties:
+# The add_jar() functions sets some target properties. You can get these
+# properties with the
+# get_property(TARGET PROPERTY )
+# command.
+#
+# INSTALL_FILES The files which should be installed. This is used by
+# install_jar().
+# JNI_SYMLINK The JNI symlink which should be installed.
+# This is used by install_jni_symlink().
+# JAR_FILE The location of the jar file so that you can include
+# it.
+# CLASS_DIR The directory where the class files can be found. For
+# example to use them with javah.
+#
+# find_jar(
+# name | NAMES name1 [name2 ...]
+# [PATHS path1 [path2 ... ENV var]]
+# [VERSIONS version1 [version2]]
+# [DOC "cache documentation string"]
+# )
+#
+# This command is used to find a full path to the named jar. A cache
+# entry named by is created to stor the result of this command. If
+# the full path to a jar is found the result is stored in the variable
+# and the search will not repeated unless the variable is cleared. If
+# nothing is found, the result will be -NOTFOUND, and the search
+# will be attempted again next time find_jar is invoked with the same
+# variable.
+# The name of the full path to a file that is searched for is specified
+# by the names listed after NAMES argument. Additional search locations
+# can be specified after the PATHS argument. If you require special a
+# version of a jar file you can specify it with the VERSIONS argument.
+# The argument after DOC will be used for the documentation string in
+# the cache.
+#
+# install_jar(TARGET_NAME DESTINATION)
+#
+# This command installs the TARGET_NAME files to the given DESTINATION.
+# It should be called in the same scope as add_jar() or it will fail.
+#
+# install_jni_symlink(TARGET_NAME DESTINATION)
+#
+# This command installs the TARGET_NAME JNI symlinks to the given
+# DESTINATION. It should be called in the same scope as add_jar()
+# or it will fail.
+#
+# create_javadoc(
+# PACKAGES pkg1 [pkg2 ...]
+# [SOURCEPATH ]
+# [CLASSPATH ]
+# [INSTALLPATH ]
+# [DOCTITLE "the documentation title"]
+# [WINDOWTITLE "the title of the document"]
+# [AUTHOR TRUE|FALSE]
+# [USE TRUE|FALSE]
+# [VERSION TRUE|FALSE]
+# )
+#
+# Create java documentation based on files or packages. For more
+# details please read the javadoc manpage.
+#
+# There are two main signatures for create_javadoc. The first
+# signature works with package names on a path with source files:
+#
+# Example:
+# create_javadoc(my_example_doc
+# PACKAGES com.exmaple.foo com.example.bar
+# SOURCEPATH "${CMAKE_CURRENT_SOURCE_DIR}"
+# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH}
+# WINDOWTITLE "My example"
+# DOCTITLE "
My example
"
+# AUTHOR TRUE
+# USE TRUE
+# VERSION TRUE
+# )
+#
+# The second signature for create_javadoc works on a given list of
+# files.
+#
+# create_javadoc(
+# FILES file1 [file2 ...]
+# [CLASSPATH ]
+# [INSTALLPATH ]
+# [DOCTITLE "the documentation title"]
+# [WINDOWTITLE "the title of the document"]
+# [AUTHOR TRUE|FALSE]
+# [USE TRUE|FALSE]
+# [VERSION TRUE|FALSE]
+# )
+#
+# Example:
+# create_javadoc(my_example_doc
+# FILES ${example_SRCS}
+# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH}
+# WINDOWTITLE "My example"
+# DOCTITLE "My example
"
+# AUTHOR TRUE
+# USE TRUE
+# VERSION TRUE
+# )
+#
+# Both signatures share most of the options. These options are the
+# same as what you can find in the javadoc manpage. Please look at
+# the manpage for CLASSPATH, DOCTITLE, WINDOWTITLE, AUTHOR, USE and
+# VERSION.
+#
+# The documentation will be by default installed to
+#
+# ${CMAKE_INSTALL_PREFIX}/share/javadoc/
+#
+# if you don't set the INSTALLPATH.
+#
+
+#=============================================================================
+# Copyright 2010-2011 Andreas schneider
+# Copyright 2010 Ben Boeckel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+function (__java_copy_file src dest comment)
+ add_custom_command(
+ OUTPUT ${dest}
+ COMMAND cmake -E copy_if_different
+ ARGS ${src}
+ ${dest}
+ DEPENDS ${src}
+ COMMENT ${comment})
+endfunction (__java_copy_file src dest comment)
+
+# define helper scripts
+set(_JAVA_CLASS_FILELIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaClassFilelist.cmake)
+set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake)
+
+function(add_jar _TARGET_NAME)
+ set(_JAVA_SOURCE_FILES ${ARGN})
+
+ if (NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR)
+ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
+ endif(NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR)
+
+ if (CMAKE_JAVA_JAR_ENTRY_POINT)
+ set(_ENTRY_POINT_OPTION e)
+ set(_ENTRY_POINT_VALUE ${CMAKE_JAVA_JAR_ENTRY_POINT})
+ endif (CMAKE_JAVA_JAR_ENTRY_POINT)
+
+ if (LIBRARY_OUTPUT_PATH)
+ set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH})
+ else (LIBRARY_OUTPUT_PATH)
+ set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR})
+ endif (LIBRARY_OUTPUT_PATH)
+
+ set(CMAKE_JAVA_INCLUDE_PATH
+ ${CMAKE_JAVA_INCLUDE_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_JAVA_OBJECT_OUTPUT_PATH}
+ ${CMAKE_JAVA_LIBRARY_OUTPUT_PATH}
+ )
+
+ if (WIN32 AND NOT CYGWIN AND NOT CMAKE_CROSSCOMPILING)
+ set(CMAKE_JAVA_INCLUDE_FLAG_SEP ";")
+ else ()
+ set(CMAKE_JAVA_INCLUDE_FLAG_SEP ":")
+ endif()
+
+ foreach (JAVA_INCLUDE_DIR ${CMAKE_JAVA_INCLUDE_PATH})
+ set(CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_PATH_FINAL}${CMAKE_JAVA_INCLUDE_FLAG_SEP}${JAVA_INCLUDE_DIR}")
+ endforeach(JAVA_INCLUDE_DIR)
+
+ set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${CMAKE_JAVA_TARGET_OUTPUT_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir")
+
+ set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}.jar")
+ if (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION)
+ set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar")
+ set(_JAVA_TARGET_OUTPUT_LINK "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar")
+ elseif (CMAKE_JAVA_TARGET_VERSION)
+ set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar")
+ set(_JAVA_TARGET_OUTPUT_LINK "${_TARGET_NAME}.jar")
+ elseif (CMAKE_JAVA_TARGET_OUTPUT_NAME)
+ set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar")
+ endif (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION)
+ # reset
+ set(CMAKE_JAVA_TARGET_OUTPUT_NAME)
+
+ set(_JAVA_CLASS_FILES)
+ set(_JAVA_COMPILE_FILES)
+ set(_JAVA_DEPENDS)
+ set(_JAVA_RESOURCE_FILES)
+ foreach(_JAVA_SOURCE_FILE ${_JAVA_SOURCE_FILES})
+ get_filename_component(_JAVA_EXT ${_JAVA_SOURCE_FILE} EXT)
+ get_filename_component(_JAVA_FILE ${_JAVA_SOURCE_FILE} NAME_WE)
+ get_filename_component(_JAVA_PATH ${_JAVA_SOURCE_FILE} PATH)
+ get_filename_component(_JAVA_FULL ${_JAVA_SOURCE_FILE} ABSOLUTE)
+
+ file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR} ${_JAVA_FULL})
+ file(RELATIVE_PATH _JAVA_REL_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${_JAVA_FULL})
+ string(LENGTH ${_JAVA_REL_BINARY_PATH} _BIN_LEN)
+ string(LENGTH ${_JAVA_REL_SOURCE_PATH} _SRC_LEN)
+ if (${_BIN_LEN} LESS ${_SRC_LEN})
+ set(_JAVA_REL_PATH ${_JAVA_REL_BINARY_PATH})
+ else (${_BIN_LEN} LESS ${_SRC_LEN})
+ set(_JAVA_REL_PATH ${_JAVA_REL_SOURCE_PATH})
+ endif (${_BIN_LEN} LESS ${_SRC_LEN})
+ get_filename_component(_JAVA_REL_PATH ${_JAVA_REL_PATH} PATH)
+
+ if (_JAVA_EXT MATCHES ".java")
+ list(APPEND _JAVA_COMPILE_FILES ${_JAVA_SOURCE_FILE})
+ set(_JAVA_CLASS_FILE "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_REL_PATH}/${_JAVA_FILE}.class")
+ set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES} ${_JAVA_CLASS_FILE})
+
+ elseif (_JAVA_EXT MATCHES ".jar"
+ OR _JAVA_EXT MATCHES ".war"
+ OR _JAVA_EXT MATCHES ".ear"
+ OR _JAVA_EXT MATCHES ".sar")
+ list(APPEND CMAKE_JAVA_INCLUDE_PATH ${_JAVA_SOURCE_FILE})
+
+ elseif (_JAVA_EXT STREQUAL "")
+ list(APPEND CMAKE_JAVA_INCLUDE_PATH ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}} ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}_CLASSPATH})
+ list(APPEND _JAVA_DEPENDS ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}})
+
+ else (_JAVA_EXT MATCHES ".java")
+ __java_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/${_JAVA_SOURCE_FILE}
+ ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_SOURCE_FILE}
+ "Copying ${_JAVA_SOURCE_FILE} to the build directory")
+ list(APPEND _JAVA_RESOURCE_FILES ${_JAVA_SOURCE_FILE})
+ endif (_JAVA_EXT MATCHES ".java")
+ endforeach(_JAVA_SOURCE_FILE)
+
+ # create an empty java_class_filelist
+ if (NOT EXISTS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist)
+ file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist "")
+ endif()
+
+ if (_JAVA_COMPILE_FILES)
+ # Compile the java files and create a list of class files
+ add_custom_command(
+ # NOTE: this command generates an artificial dependency file
+ OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
+ COMMAND ${Java_JAVAC_EXECUTABLE}
+ ${CMAKE_JAVA_COMPILE_FLAGS}
+ -classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}"
+ -d ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ ${_JAVA_COMPILE_FILES}
+ COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
+ DEPENDS ${_JAVA_COMPILE_FILES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Building Java objects for ${_TARGET_NAME}.jar"
+ )
+ add_custom_command(
+ OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
+ COMMAND ${CMAKE_COMMAND}
+ -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ -DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}"
+ -P ${_JAVA_CLASS_FILELIST_SCRIPT}
+ DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ endif (_JAVA_COMPILE_FILES)
+
+ # create the jar file
+ set(_JAVA_JAR_OUTPUT_PATH
+ ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_NAME})
+ if (CMAKE_JNI_TARGET)
+ add_custom_command(
+ OUTPUT ${_JAVA_JAR_OUTPUT_PATH}
+ COMMAND ${Java_JAR_EXECUTABLE}
+ -cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE}
+ ${_JAVA_RESOURCE_FILES} @java_class_filelist
+ COMMAND ${CMAKE_COMMAND}
+ -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
+ -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME}
+ -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
+ -P ${_JAVA_SYMLINK_SCRIPT}
+ COMMAND ${CMAKE_COMMAND}
+ -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
+ -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_JAR_OUTPUT_PATH}
+ -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
+ -P ${_JAVA_SYMLINK_SCRIPT}
+ DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
+ WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}"
+ )
+ else ()
+ add_custom_command(
+ OUTPUT ${_JAVA_JAR_OUTPUT_PATH}
+ COMMAND ${Java_JAR_EXECUTABLE}
+ -cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE}
+ ${_JAVA_RESOURCE_FILES} @java_class_filelist
+ COMMAND ${CMAKE_COMMAND}
+ -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
+ -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME}
+ -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
+ -P ${_JAVA_SYMLINK_SCRIPT}
+ WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
+ COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}"
+ )
+ endif (CMAKE_JNI_TARGET)
+
+ # Add the target and make sure we have the latest resource files.
+ add_custom_target(${_TARGET_NAME} ALL DEPENDS ${_JAVA_JAR_OUTPUT_PATH})
+
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ INSTALL_FILES
+ ${_JAVA_JAR_OUTPUT_PATH}
+ )
+
+ if (_JAVA_TARGET_OUTPUT_LINK)
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ INSTALL_FILES
+ ${_JAVA_JAR_OUTPUT_PATH}
+ ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK}
+ )
+
+ if (CMAKE_JNI_TARGET)
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ JNI_SYMLINK
+ ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK}
+ )
+ endif (CMAKE_JNI_TARGET)
+ endif (_JAVA_TARGET_OUTPUT_LINK)
+
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ JAR_FILE
+ ${_JAVA_JAR_OUTPUT_PATH}
+ )
+
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ CLASSDIR
+ ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ )
+
+endfunction(add_jar)
+
+function(INSTALL_JAR _TARGET_NAME _DESTINATION)
+ get_property(__FILES
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ INSTALL_FILES
+ )
+
+ if (__FILES)
+ install(
+ FILES
+ ${__FILES}
+ DESTINATION
+ ${_DESTINATION}
+ )
+ else (__FILES)
+ message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.")
+ endif (__FILES)
+endfunction(INSTALL_JAR _TARGET_NAME _DESTINATION)
+
+function(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION)
+ get_property(__SYMLINK
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ JNI_SYMLINK
+ )
+
+ if (__SYMLINK)
+ install(
+ FILES
+ ${__SYMLINK}
+ DESTINATION
+ ${_DESTINATION}
+ )
+ else (__SYMLINK)
+ message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.")
+ endif (__SYMLINK)
+endfunction(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION)
+
+function (find_jar VARIABLE)
+ set(_jar_names)
+ set(_jar_files)
+ set(_jar_versions)
+ set(_jar_paths
+ /usr/share/java/
+ /usr/local/share/java/
+ ${Java_JAR_PATHS})
+ set(_jar_doc "NOTSET")
+
+ set(_state "name")
+
+ foreach (arg ${ARGN})
+ if (${_state} STREQUAL "name")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "NAMES")
+ set(_jar_names ${arg})
+ if (_jar_doc STREQUAL "NOTSET")
+ set(_jar_doc "Finding ${arg} jar")
+ endif (_jar_doc STREQUAL "NOTSET")
+ endif (${arg} STREQUAL "VERSIONS")
+ elseif (${_state} STREQUAL "versions")
+ if (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "NAMES")
+ set(_jar_versions ${_jar_versions} ${arg})
+ endif (${arg} STREQUAL "NAMES")
+ elseif (${_state} STREQUAL "names")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "VERSIONS")
+ set(_jar_names ${_jar_names} ${arg})
+ if (_jar_doc STREQUAL "NOTSET")
+ set(_jar_doc "Finding ${arg} jar")
+ endif (_jar_doc STREQUAL "NOTSET")
+ endif (${arg} STREQUAL "VERSIONS")
+ elseif (${_state} STREQUAL "paths")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "VERSIONS")
+ set(_jar_paths ${_jar_paths} ${arg})
+ endif (${arg} STREQUAL "VERSIONS")
+ elseif (${_state} STREQUAL "doc")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ else (${arg} STREQUAL "VERSIONS")
+ set(_jar_doc ${arg})
+ endif (${arg} STREQUAL "VERSIONS")
+ endif (${_state} STREQUAL "name")
+ endforeach (arg ${ARGN})
+
+ if (NOT _jar_names)
+ message(FATAL_ERROR "find_jar: No name to search for given")
+ endif (NOT _jar_names)
+
+ foreach (jar_name ${_jar_names})
+ foreach (version ${_jar_versions})
+ set(_jar_files ${_jar_files} ${jar_name}-${version}.jar)
+ endforeach (version ${_jar_versions})
+ set(_jar_files ${_jar_files} ${jar_name}.jar)
+ endforeach (jar_name ${_jar_names})
+
+ find_file(${VARIABLE}
+ NAMES ${_jar_files}
+ PATHS ${_jar_paths}
+ DOC ${_jar_doc}
+ NO_DEFAULT_PATH)
+endfunction (find_jar VARIABLE)
+
+function(create_javadoc _target)
+ set(_javadoc_packages)
+ set(_javadoc_files)
+ set(_javadoc_sourcepath)
+ set(_javadoc_classpath)
+ set(_javadoc_installpath "${CMAKE_INSTALL_PREFIX}/share/javadoc")
+ set(_javadoc_doctitle)
+ set(_javadoc_windowtitle)
+ set(_javadoc_author FALSE)
+ set(_javadoc_version FALSE)
+ set(_javadoc_use FALSE)
+
+ set(_state "package")
+
+ foreach (arg ${ARGN})
+ if (${_state} STREQUAL "package")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_packages ${arg})
+ set(_state "packages")
+ endif ()
+ elseif (${_state} STREQUAL "packages")
+ if (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_packages ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "files")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_files ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "sourcepath")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_sourcepath ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "classpath")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_classpath ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "installpath")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_installpath ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "doctitle")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_doctitle ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "windowtitle")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_windowtitle ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "author")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_author ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "use")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_use ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "version")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_version ${arg})
+ endif ()
+ endif (${_state} STREQUAL "package")
+ endforeach (arg ${ARGN})
+
+ set(_javadoc_builddir ${CMAKE_CURRENT_BINARY_DIR}/javadoc/${_target})
+ set(_javadoc_options -d ${_javadoc_builddir})
+
+ if (_javadoc_sourcepath)
+ set(_start TRUE)
+ foreach(_path ${_javadoc_sourcepath})
+ if (_start)
+ set(_sourcepath ${_path})
+ set(_start FALSE)
+ else (_start)
+ set(_sourcepath ${_sourcepath}:${_path})
+ endif (_start)
+ endforeach(_path ${_javadoc_sourcepath})
+ set(_javadoc_options ${_javadoc_options} -sourcepath ${_sourcepath})
+ endif (_javadoc_sourcepath)
+
+ if (_javadoc_classpath)
+ set(_start TRUE)
+ foreach(_path ${_javadoc_classpath})
+ if (_start)
+ set(_classpath ${_path})
+ set(_start FALSE)
+ else (_start)
+ set(_classpath ${_classpath}:${_path})
+ endif (_start)
+ endforeach(_path ${_javadoc_classpath})
+ set(_javadoc_options ${_javadoc_options} -classpath "${_classpath}")
+ endif (_javadoc_classpath)
+
+ if (_javadoc_doctitle)
+ set(_javadoc_options ${_javadoc_options} -doctitle '${_javadoc_doctitle}')
+ endif (_javadoc_doctitle)
+
+ if (_javadoc_windowtitle)
+ set(_javadoc_options ${_javadoc_options} -windowtitle '${_javadoc_windowtitle}')
+ endif (_javadoc_windowtitle)
+
+ if (_javadoc_author)
+ set(_javadoc_options ${_javadoc_options} -author)
+ endif (_javadoc_author)
+
+ if (_javadoc_use)
+ set(_javadoc_options ${_javadoc_options} -use)
+ endif (_javadoc_use)
+
+ if (_javadoc_version)
+ set(_javadoc_options ${_javadoc_options} -version)
+ endif (_javadoc_version)
+
+ add_custom_target(${_target}_javadoc ALL
+ COMMAND ${Java_JAVADOC_EXECUTABLE} ${_javadoc_options}
+ ${_javadoc_files}
+ ${_javadoc_packages}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+ install(
+ DIRECTORY ${_javadoc_builddir}
+ DESTINATION ${_javadoc_installpath}
+ )
+endfunction(create_javadoc)
diff --git a/ultimmc/cmake/UseJavaClassFilelist.cmake b/ultimmc/cmake/UseJavaClassFilelist.cmake
new file mode 100644
index 0000000..c842bf7
--- /dev/null
+++ b/ultimmc/cmake/UseJavaClassFilelist.cmake
@@ -0,0 +1,52 @@
+#
+# This script create a list of compiled Java class files to be added to a
+# jar file. This avoids including cmake files which get created in the
+# binary directory.
+#
+
+#=============================================================================
+# Copyright 2010-2011 Andreas schneider
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+if (CMAKE_JAVA_CLASS_OUTPUT_PATH)
+ if (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
+
+ set(_JAVA_GLOBBED_FILES)
+ if (CMAKE_JAR_CLASSES_PREFIX)
+ foreach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX})
+ message(STATUS "JAR_CLASS_PREFIX: ${JAR_CLASS_PREFIX}")
+
+ file(GLOB_RECURSE _JAVA_GLOBBED_TMP_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${JAR_CLASS_PREFIX}/*.class")
+ if (_JAVA_GLOBBED_TMP_FILES)
+ list(APPEND _JAVA_GLOBBED_FILES ${_JAVA_GLOBBED_TMP_FILES})
+ endif (_JAVA_GLOBBED_TMP_FILES)
+ endforeach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX})
+ else()
+ file(GLOB_RECURSE _JAVA_GLOBBED_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/*.class")
+ endif (CMAKE_JAR_CLASSES_PREFIX)
+
+ set(_JAVA_CLASS_FILES)
+ # file(GLOB_RECURSE foo RELATIVE) is broken so we need this.
+ foreach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES})
+ file(RELATIVE_PATH _JAVA_CLASS_FILE ${CMAKE_JAVA_CLASS_OUTPUT_PATH} ${_JAVA_GLOBBED_FILE})
+ set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES}${_JAVA_CLASS_FILE}\n)
+ endforeach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES})
+
+ # write to file
+ file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist ${_JAVA_CLASS_FILES})
+
+ else (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
+ message(SEND_ERROR "FATAL: Java class output path doesn't exist")
+ endif (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
+else (CMAKE_JAVA_CLASS_OUTPUT_PATH)
+ message(SEND_ERROR "FATAL: Can't find CMAKE_JAVA_CLASS_OUTPUT_PATH")
+endif (CMAKE_JAVA_CLASS_OUTPUT_PATH)
diff --git a/ultimmc/cmake/UseJavaSymlinks.cmake b/ultimmc/cmake/UseJavaSymlinks.cmake
new file mode 100644
index 0000000..c66ee1e
--- /dev/null
+++ b/ultimmc/cmake/UseJavaSymlinks.cmake
@@ -0,0 +1,32 @@
+#
+# Helper script for UseJava.cmake
+#
+
+#=============================================================================
+# Copyright 2010-2011 Andreas schneider
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+if (UNIX AND _JAVA_TARGET_OUTPUT_LINK)
+ if (_JAVA_TARGET_OUTPUT_NAME)
+ find_program(LN_EXECUTABLE
+ NAMES
+ ln
+ )
+
+ execute_process(
+ COMMAND ${LN_EXECUTABLE} -sf "${_JAVA_TARGET_OUTPUT_NAME}" "${_JAVA_TARGET_OUTPUT_LINK}"
+ WORKING_DIRECTORY ${_JAVA_TARGET_DIR}
+ )
+ else (_JAVA_TARGET_OUTPUT_NAME)
+ message(SEND_ERROR "FATAL: Can't find _JAVA_TARGET_OUTPUT_NAME")
+ endif (_JAVA_TARGET_OUTPUT_NAME)
+endif (UNIX AND _JAVA_TARGET_OUTPUT_LINK)
diff --git a/ultimmc/doc/multimc.1.txt b/ultimmc/doc/multimc.1.txt
new file mode 100644
index 0000000..da65af2
--- /dev/null
+++ b/ultimmc/doc/multimc.1.txt
@@ -0,0 +1,64 @@
+MULTIMC(1)
+==========
+:doctype: manpage
+
+
+NAME
+----
+multimc - a launcher and instance manager for Minecraft.
+
+
+SYNOPSIS
+--------
+*multimc* ['OPTIONS']
+
+
+DESCRIPTION
+-----------
+MultiMC is a custom launcher for Minecraft that allows you to easily manage
+multiple installations of Minecraft at once. It also allows you to easily
+install and remove mods by simply dragging and dropping.
+Here are the current features of MultiMC.
+
+OPTIONS
+-------
+*-d, --dir*='DIRECTORY'::
+ Use 'DIRECTORY' as the MultiMC root.
+
+*-l, --launch*='INSTANCE_ID'::
+ Launch the instance specified by 'INSTANCE_ID'.
+
+*--alive*::
+ Write a small 'live.check' file after MultiMC starts.
+
+*-h, --help*::
+ Display help text and exit.
+
+*-v, --version*::
+ Display program version and exit.
+*-a, --profile*='PROFILE'::
+ Use the account specified by 'PROFILE' (only valid in combination with --launch).
+
+EXIT STATUS
+-----------
+*0*::
+ Success
+
+*1*::
+ Failure (syntax or usage error; configuration error; unexpected error).
+
+BUGS
+----
+
+
+RESOURCES
+---------
+GitHub:
+
+Main website:
+
+AUTHORS
+-------
+peterix
+
+// vim: syntax=asciidoc
diff --git a/ultimmc/launcher/Application.cpp b/ultimmc/launcher/Application.cpp
new file mode 100644
index 0000000..ac255ab
--- /dev/null
+++ b/ultimmc/launcher/Application.cpp
@@ -0,0 +1,1755 @@
+#include "Application.h"
+#include "BuildConfig.h"
+
+#include "ui/MainWindow.h"
+#include "ui/InstanceWindow.h"
+
+#include "ui/instanceview/AccessibleInstanceView.h"
+
+#include "ui/pages/BasePageProvider.h"
+#include "ui/pages/global/LauncherPage.h"
+#include "ui/pages/global/MinecraftPage.h"
+#include "ui/pages/global/JavaPage.h"
+#include "ui/pages/global/LanguagePage.h"
+#include "ui/pages/global/ProxyPage.h"
+#include "ui/pages/global/ExternalToolsPage.h"
+#include "ui/pages/global/AccountListPage.h"
+#include "ui/pages/global/PasteEEPage.h"
+#include "ui/pages/global/CustomCommandsPage.h"
+
+#include "ui/themes/ITheme.h"
+#include "ui/themes/SystemTheme.h"
+#include "ui/themes/DarkTheme.h"
+#include "ui/themes/BrightTheme.h"
+#include "ui/themes/CustomTheme.h"
+
+#include "ui/setupwizard/SetupWizard.h"
+#include "ui/setupwizard/LanguageWizardPage.h"
+#include "ui/setupwizard/JavaWizardPage.h"
+#include "ui/setupwizard/AnalyticsWizardPage.h"
+
+#include "ui/dialogs/CustomMessageBox.h"
+
+#include "ui/pagedialog/PageDialog.h"
+
+#include "ApplicationMessage.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "InstanceList.h"
+
+#include
+#include
+#include "icons/IconList.h"
+#include "net/HttpMetaCache.h"
+
+#include "java/JavaUtils.h"
+
+#include "updater/UpdateChecker.h"
+
+#include "tools/JProfiler.h"
+#include "tools/JVisualVM.h"
+#include "tools/MCEditTool.h"
+#include "AuthServer.h"
+
+#include
+#include "settings/INISettingsObject.h"
+#include "settings/Setting.h"
+
+#include "translations/TranslationsModel.h"
+#include "meta/Index.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+
+#if defined Q_OS_WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include
+#include
+#endif
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+static const QLatin1String liveCheckFile("live.check");
+
+using namespace Commandline;
+
+#define MACOS_HINT "If you are on macOS Sierra, you might have to move the app to your /Applications or ~/Applications folder. "\
+ "This usually fixes the problem and you can move the application elsewhere afterwards.\n"\
+ "\n"
+
+namespace {
+void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ const char *levels = "DWCFIS";
+ const QString format("%1 %2 %3\n");
+
+ qint64 msecstotal = APPLICATION->timeSinceStart();
+ qint64 seconds = msecstotal / 1000;
+ qint64 msecs = msecstotal % 1000;
+ QString foo;
+ char buf[1025] = {0};
+ ::snprintf(buf, 1024, "%5lld.%03lld", seconds, msecs);
+
+ QString out = format.arg(buf).arg(levels[type]).arg(msg);
+
+ APPLICATION->logFile->write(out.toUtf8());
+ APPLICATION->logFile->flush();
+ QTextStream(stderr) << out.toLocal8Bit();
+ fflush(stderr);
+}
+
+QString getIdealPlatform(QString currentPlatform) {
+ auto info = Sys::getKernelInfo();
+ switch(info.kernelType) {
+ case Sys::KernelType::Darwin: {
+ if(info.kernelMajor >= 17) {
+ // macOS 10.13 or newer
+ return "osx64-5.15.2";
+ }
+ else {
+ // macOS 10.12 or older
+ return "osx64";
+ }
+ }
+ case Sys::KernelType::Windows: {
+ // FIXME: 5.15.2 is not stable on Windows, due to a large number of completely unpredictable and hard to reproduce issues
+ break;
+/*
+ if(info.kernelMajor == 6 && info.kernelMinor >= 1) {
+ // Windows 7
+ return "win32-5.15.2";
+ }
+ else if (info.kernelMajor > 6) {
+ // Above Windows 7
+ return "win32-5.15.2";
+ }
+ else {
+ // Below Windows 7
+ return "win32";
+ }
+*/
+ }
+ case Sys::KernelType::Undetermined:
+ case Sys::KernelType::Linux: {
+ break;
+ }
+ }
+ return currentPlatform;
+}
+
+}
+
+Application::Application(int &argc, char **argv) : QApplication(argc, argv)
+{
+#if defined Q_OS_WIN32
+ // attach the parent console
+ if(AttachConsole(ATTACH_PARENT_PROCESS))
+ {
+ // if attach succeeds, reopen and sync all the i/o
+ if(freopen("CON", "w", stdout))
+ {
+ std::cout.sync_with_stdio();
+ }
+ if(freopen("CON", "w", stderr))
+ {
+ std::cerr.sync_with_stdio();
+ }
+ if(freopen("CON", "r", stdin))
+ {
+ std::cin.sync_with_stdio();
+ }
+ auto out = GetStdHandle (STD_OUTPUT_HANDLE);
+ DWORD written;
+ const char * endline = "\n";
+ WriteConsole(out, endline, strlen(endline), &written, NULL);
+ consoleAttached = true;
+ }
+#endif
+ setOrganizationName(BuildConfig.LAUNCHER_NAME);
+ setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
+ setApplicationName(BuildConfig.LAUNCHER_NAME);
+ setApplicationDisplayName(BuildConfig.LAUNCHER_DISPLAYNAME);
+ setApplicationVersion(BuildConfig.printableVersionString());
+
+ startTime = QDateTime::currentDateTime();
+
+#ifdef Q_OS_LINUX
+ {
+ QFile osrelease("/proc/sys/kernel/osrelease");
+ if (osrelease.open(QFile::ReadOnly | QFile::Text)) {
+ QTextStream in(&osrelease);
+ auto contents = in.readAll();
+ if(
+ contents.contains("WSL", Qt::CaseInsensitive) ||
+ contents.contains("Microsoft", Qt::CaseInsensitive)
+ ) {
+ showFatalErrorMessage(
+ "Unsupported system detected!",
+ "Linux-on-Windows distributions are not supported.\n\n"
+ "Please use the Windows binary when playing on Windows."
+ );
+ return;
+ }
+ }
+ }
+#endif
+
+ // Don't quit on hiding the last window
+ this->setQuitOnLastWindowClosed(false);
+
+ // Commandline parsing
+ QHash args;
+ {
+ Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
+
+ // --help
+ parser.addSwitch("help");
+ parser.addShortOpt("help", 'h');
+ parser.addDocumentation("help", "Display this help and exit.");
+ // --version
+ parser.addSwitch("version");
+ parser.addShortOpt("version", 'V');
+ parser.addDocumentation("version", "Display program version and exit.");
+ // --dir
+ parser.addOption("dir");
+ parser.addShortOpt("dir", 'd');
+ parser.addDocumentation("dir", "Use the supplied folder as application root instead of the binary location (use '.' for current)");
+ // --launch
+ parser.addOption("launch");
+ parser.addShortOpt("launch", 'l');
+ parser.addDocumentation("launch", "Launch the specified instance (by instance ID)");
+ // --server
+ parser.addOption("server");
+ parser.addShortOpt("server", 's');
+ parser.addDocumentation("server", "Join the specified server on launch (only valid in combination with --launch, mutually exclusive with --world)");
+ // --world
+ parser.addOption("world");
+ parser.addShortOpt("world", 'w');
+ parser.addDocumentation("world", "Join the singleplayer world with the specified folder name on launch (only valid in combination with --launch, mutually exclusive with --server, only works with Minecraft 23w14a and later)");
+ // --profile
+ parser.addOption("profile");
+ parser.addShortOpt("profile", 'a');
+ parser.addDocumentation("profile", "Use the account specified by its profile name (only valid in combination with --launch)");
+ // --offline
+ parser.addSwitch("offline");
+ parser.addShortOpt("offline", 'o');
+ parser.addDocumentation("offline", "Launch offline (only valid in combination with --launch)");
+ // --name
+ parser.addOption("name");
+ parser.addShortOpt("name", 'n');
+ parser.addDocumentation("name", "When launching offline, use specified name (only makes sense in combination with --launch and --offline)");
+ // --alive
+ parser.addSwitch("alive");
+ parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after the launcher starts");
+ // --import
+ parser.addOption("import");
+ parser.addShortOpt("import", 'I');
+ parser.addDocumentation("import", "Import instance from specified zip (local path or URL)");
+
+ // parse the arguments
+ try
+ {
+ args = parser.parse(arguments());
+ }
+ catch (const ParsingError &e)
+ {
+ std::cerr << "CommandLineError: " << e.what() << std::endl;
+ if(argc > 0)
+ std::cerr << "Try '" << argv[0] << " -h' to get help on command line parameters."
+ << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+
+ // display help and exit
+ if (args["help"].toBool())
+ {
+ std::cout << qPrintable(parser.compileHelp(arguments()[0]));
+ m_status = Application::Succeeded;
+ return;
+ }
+
+ // display version and exit
+ if (args["version"].toBool())
+ {
+ std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
+ std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
+ m_status = Application::Succeeded;
+ return;
+ }
+ }
+ m_instanceIdToLaunch = args["launch"].toString();
+ m_serverToJoin = args["server"].toString();
+ m_worldToJoin = args["world"].toString();
+ m_profileToUse = args["profile"].toString();
+ if(args["offline"].toBool()) {
+ m_offline = true;
+ m_offlineName = args["name"].toString();
+ }
+ m_liveCheck = args["alive"].toBool();
+ m_zipToImport = args["import"].toUrl();
+
+ QString origcwdPath = QDir::currentPath();
+ QString binPath = applicationDirPath();
+ QString adjustedBy;
+ QString dataPath;
+ // change folder
+ QString dirParam = args["dir"].toString();
+ if (!dirParam.isEmpty())
+ {
+ // the dir param. it makes multimc data path point to whatever the user specified
+ // on command line
+ adjustedBy += "Command line " + dirParam;
+ dataPath = dirParam;
+ }
+ else
+ {
+#if defined(Q_OS_MAC)
+ QDir foo(FS::PathCombine(applicationDirPath(), "../../Data"));
+ dataPath = foo.absolutePath();
+ adjustedBy += "Fallback to special Mac location " + dataPath;
+#else
+ dataPath = applicationDirPath();
+ adjustedBy += "Fallback to binary path " + dataPath;
+#endif
+ }
+
+ if (!FS::ensureFolderPathExists(dataPath))
+ {
+ showFatalErrorMessage(
+ "The launcher data folder could not be created.",
+ QString(
+ "The launcher data folder could not be created.\n"
+ "\n"
+#if defined(Q_OS_MAC)
+ MACOS_HINT
+#endif
+ "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n"
+ "(%1)\n"
+ "\n"
+ "The launcher cannot continue until you fix this problem."
+ ).arg(dataPath)
+ );
+ return;
+ }
+ if (!QDir::setCurrent(dataPath))
+ {
+ showFatalErrorMessage(
+ "The launcher data folder could not be opened.",
+ QString(
+ "The launcher data folder could not be opened.\n"
+ "\n"
+#if defined(Q_OS_MAC)
+ MACOS_HINT
+#endif
+ "Make sure you have the right permissions to the launcher data folder.\n"
+ "(%1)\n"
+ "\n"
+ "The launcher cannot continue until you fix this problem."
+ ).arg(dataPath)
+ );
+ return;
+ }
+
+ // --world and --server can't be used together
+ if(!m_worldToJoin.isEmpty() && !m_serverToJoin.isEmpty())
+ {
+ std::cerr << "--server and --world are mutually exclusive!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+
+ // all the things invalid when NOT trying to --launch
+ if(m_instanceIdToLaunch.isEmpty()) {
+ if(!m_serverToJoin.isEmpty())
+ {
+ std::cerr << "--server can only be used in combination with --launch!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+
+ if(!m_worldToJoin.isEmpty())
+ {
+ std::cerr << "--world can only be used in combination with --launch!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+
+ if(!m_profileToUse.isEmpty())
+ {
+ std::cerr << "--account can only be used in combination with --launch!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+
+ if(m_offline)
+ {
+ std::cerr << "--offline can only be used in combination with --launch!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+
+ if(!m_offlineName.isEmpty())
+ {
+ std::cerr << "--offlineName can only be used in combination with --launch and --offline!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+ }
+ else {
+ // all the things invalid when trying to --launch
+ // online, and offline name is set
+ if(!m_offline && !m_offlineName.isEmpty()) {
+ std::cerr << "--offlineName can only be used in combination with --launch and --offline!" << std::endl;
+ m_status = Application::Failed;
+ return;
+ }
+ }
+
+#if defined(Q_OS_MAC)
+ // move user data to new location if on macOS and it still exists in Contents/MacOS
+ QDir fi(applicationDirPath());
+ QString originalData = fi.absolutePath();
+ // if the config file exists in Contents/MacOS, then user data is still there and needs to moved
+ if (QFileInfo::exists(FS::PathCombine(originalData, BuildConfig.LAUNCHER_CONFIGFILE)))
+ {
+ if (!QFileInfo::exists(FS::PathCombine(originalData, "dontmovemacdata")))
+ {
+ QMessageBox::StandardButton askMoveDialogue;
+ askMoveDialogue = QMessageBox::question(
+ nullptr,
+ BuildConfig.LAUNCHER_DISPLAYNAME,
+ "Would you like to move application data to a new data location? It will improve the launcher's performance, but if you switch to older versions it will look like instances have disappeared. If you select no, you can migrate later in settings. You should select yes unless you're commonly switching between different versions (eg. develop and stable).",
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::Yes
+ );
+ if (askMoveDialogue == QMessageBox::Yes)
+ {
+ qDebug() << "On macOS and found config file in old location, moving user data...";
+ QDir dir;
+ QStringList dataFiles {
+ "*.log", // Launcher log files: ${Launcher_Name}-@.log
+ "accounts.json",
+ "accounts",
+ "assets",
+ "cache",
+ "icons",
+ "instances",
+ "libraries",
+ "meta",
+ "metacache",
+ "mods",
+ BuildConfig.LAUNCHER_CONFIGFILE,
+ "themes",
+ "translations"
+ };
+ QDirIterator files(originalData, dataFiles);
+ while (files.hasNext()) {
+ QString filePath(files.next());
+ QString fileName(files.fileName());
+ if (!dir.rename(filePath, FS::PathCombine(dataPath, fileName)))
+ {
+ qWarning() << "Failed to move " << fileName;
+ }
+ }
+ }
+ else
+ {
+ dataPath = originalData;
+ QDir::setCurrent(dataPath);
+ QFile file(originalData + "/dontmovemacdata");
+ file.open(QIODevice::WriteOnly);
+ }
+ }
+ else
+ {
+ dataPath = originalData;
+ QDir::setCurrent(dataPath);
+ }
+ }
+#endif
+
+ /*
+ * Establish the mechanism for communication with an already running MultiMC that uses the same data path.
+ * If there is one, tell it what the user actually wanted to do and exit.
+ * We want to initialize this before logging to avoid messing with the log of a potential already running copy.
+ */
+ auto appID = ApplicationId::fromPathAndVersion(QDir::currentPath(), BuildConfig.printableVersionString());
+ {
+ // FIXME: you can run the same binaries with multiple data dirs and they won't clash. This could cause issues for updates.
+ m_peerInstance = new LocalPeer(this, appID);
+ connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived);
+ if(m_peerInstance->isClient()) {
+ int timeout = 2000;
+
+ if(m_instanceIdToLaunch.isEmpty())
+ {
+ ApplicationMessage activate;
+ activate.command = "activate";
+ m_peerInstance->sendMessage(activate.serialize(), timeout);
+
+ if(!m_zipToImport.isEmpty())
+ {
+ ApplicationMessage import;
+ import.command = "import";
+ import.args.insert("path", m_zipToImport.toString());
+ m_peerInstance->sendMessage(import.serialize(), timeout);
+ }
+ }
+ else
+ {
+ ApplicationMessage launch;
+ launch.command = "launch";
+ launch.args["id"] = m_instanceIdToLaunch;
+
+ if(!m_serverToJoin.isEmpty())
+ {
+ launch.args["server"] = m_serverToJoin;
+ }
+ if(!m_worldToJoin.isEmpty())
+ {
+ launch.args["world"] = m_worldToJoin;
+ }
+ if(!m_profileToUse.isEmpty())
+ {
+ launch.args["profile"] = m_profileToUse;
+ }
+ if(m_offline) {
+ launch.args["offline_enabled"] = "true";
+ launch.args["offline_name"] = m_offlineName;
+ }
+ m_peerInstance->sendMessage(launch.serialize(), timeout);
+ }
+ m_status = Application::Succeeded;
+ return;
+ }
+ }
+
+ // init the logger
+ {
+ static const QString logBase = BuildConfig.LAUNCHER_NAME + "-%0.log";
+ auto moveFile = [](const QString &oldName, const QString &newName)
+ {
+ QFile::remove(newName);
+ QFile::copy(oldName, newName);
+ QFile::remove(oldName);
+ };
+
+ moveFile(logBase.arg(3), logBase.arg(4));
+ moveFile(logBase.arg(2), logBase.arg(3));
+ moveFile(logBase.arg(1), logBase.arg(2));
+ moveFile(logBase.arg(0), logBase.arg(1));
+
+ logFile = std::unique_ptr(new QFile(logBase.arg(0)));
+ if(!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
+ {
+ showFatalErrorMessage(
+ "The launcher data folder is not writable!",
+ QString(
+ "The launcher couldn't create a log file - the data folder is not writable.\n"
+ "\n"
+ #if defined(Q_OS_MAC)
+ MACOS_HINT
+ #endif
+ "Make sure you have write permissions to the data folder.\n"
+ "(%1)\n"
+ "\n"
+ "The launcher cannot continue until you fix this problem."
+ ).arg(dataPath)
+ );
+ return;
+ }
+ qInstallMessageHandler(appDebugOutput);
+ qDebug() << "<> Log initialized.";
+ }
+
+ // Set up paths
+ {
+ // Root path is used for updates.
+#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
+ QDir foo(FS::PathCombine(binPath, ".."));
+ m_rootPath = foo.absolutePath();
+#elif defined(Q_OS_WIN32)
+ m_rootPath = binPath;
+#elif defined(Q_OS_MAC)
+ QDir foo(FS::PathCombine(binPath, "../.."));
+ m_rootPath = foo.absolutePath();
+ // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
+ FS::updateTimestamp(m_rootPath);
+#endif
+
+ qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2023 " << BuildConfig.LAUNCHER_COPYRIGHT;
+ qDebug() << "Version : " << BuildConfig.printableVersionString();
+ qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
+ qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
+ if (adjustedBy.size())
+ {
+ qDebug() << "Work dir before adjustment : " << origcwdPath;
+ qDebug() << "Work dir after adjustment : " << QDir::currentPath();
+ qDebug() << "Adjusted by : " << adjustedBy;
+ }
+ else
+ {
+ qDebug() << "Work dir : " << QDir::currentPath();
+ }
+ qDebug() << "Binary path : " << binPath;
+ qDebug() << "Application root path : " << m_rootPath;
+ if(!m_instanceIdToLaunch.isEmpty())
+ {
+ qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
+ }
+ if(!m_serverToJoin.isEmpty())
+ {
+ qDebug() << "Address of server to join :" << m_serverToJoin;
+ }
+ if(!m_worldToJoin.isEmpty())
+ {
+ qDebug() << "Name of world to join :" << m_worldToJoin;
+ }
+ qDebug() << "<> Paths set.";
+ }
+
+ do // once
+ {
+ if(m_liveCheck)
+ {
+ QFile check(liveCheckFile);
+ if(!check.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ {
+ qWarning() << "Could not open" << liveCheckFile << "for writing!";
+ break;
+ }
+ auto payload = appID.toString().toUtf8();
+ if(check.write(payload) != payload.size())
+ {
+ qWarning() << "Could not write into" << liveCheckFile << "!";
+ check.remove();
+ break;
+ }
+ check.close();
+ }
+ } while(false);
+
+ // Initialize application settings
+ {
+ m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
+ // Updates
+ m_settings->registerSetting("AutoUpdate", true);
+
+ // Theming
+ m_settings->registerSetting("IconTheme", QString("multimc"));
+ m_settings->registerSetting("ApplicationTheme", QString("system"));
+
+ // Notifications
+ m_settings->registerSetting("ShownNotifications", QString());
+
+ // Remembered state
+ m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
+
+ QString defaultMonospace;
+ int defaultSize = 11;
+#ifdef Q_OS_WIN32
+ defaultMonospace = "Courier";
+ defaultSize = 10;
+#elif defined(Q_OS_MAC)
+ defaultMonospace = "Menlo";
+#else
+ defaultMonospace = "Monospace";
+#endif
+
+ // resolve the font so the default actually matches
+ QFont consoleFont;
+ consoleFont.setFamily(defaultMonospace);
+ consoleFont.setStyleHint(QFont::Monospace);
+ consoleFont.setFixedPitch(true);
+ QFontInfo consoleFontInfo(consoleFont);
+ QString resolvedDefaultMonospace = consoleFontInfo.family();
+ QFont resolvedFont(resolvedDefaultMonospace);
+ qDebug() << "Detected default console font:" << resolvedDefaultMonospace
+ << ", substitutions:" << resolvedFont.substitutions().join(',');
+
+ m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace);
+ m_settings->registerSetting("ConsoleFontSize", defaultSize);
+ m_settings->registerSetting("ConsoleMaxLines", 100000);
+ m_settings->registerSetting("ConsoleOverflowStop", true);
+
+ // Folders
+ m_settings->registerSetting("InstanceDir", "instances");
+ m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
+ m_settings->registerSetting("IconsDir", "icons");
+
+ // Editors
+ m_settings->registerSetting("JsonEditor", QString());
+
+ // Language
+ m_settings->registerSetting("Language", QString());
+
+ // Console
+ m_settings->registerSetting("ShowConsole", false);
+ m_settings->registerSetting("AutoCloseConsole", false);
+ m_settings->registerSetting("ShowConsoleOnError", true);
+ m_settings->registerSetting("LogPrePostOutput", true);
+
+ // Window Size
+ m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false);
+ m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854);
+ m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
+
+ // Proxy Settings
+ m_settings->registerSetting("ProxyType", "None");
+ m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1");
+ m_settings->registerSetting("ProxyPort", 8080);
+ m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, "");
+ m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, "");
+
+ // Memory
+ m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
+ m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
+ m_settings->registerSetting("PermGen", 128);
+
+ // Java Settings
+ m_settings->registerSetting("JavaPath", "");
+ m_settings->registerSetting("JavaTimestamp", 0);
+ m_settings->registerSetting("JavaArchitecture", "");
+ m_settings->registerSetting("JavaVersion", "");
+ m_settings->registerSetting("JavaVendor", "");
+ m_settings->registerSetting("LastHostname", "");
+ m_settings->registerSetting("JvmArgs", "");
+
+ // Native library workarounds
+ m_settings->registerSetting("UseNativeOpenAL", false);
+ m_settings->registerSetting("UseNativeGLFW", false);
+
+ // Game time
+ m_settings->registerSetting("ShowGameTime", true);
+ m_settings->registerSetting("ShowGlobalGameTime", true);
+ m_settings->registerSetting("RecordGameTime", true);
+ m_settings->registerSetting("ShowGameTimeHours", false);
+
+ // Minecraft launch method
+ m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
+
+ // Minecraft offline player name
+ m_settings->registerSetting("LastOfflinePlayerName", "");
+
+ // Wrapper command for launch
+ m_settings->registerSetting("WrapperCommand", "");
+
+ // Custom Commands
+ m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
+ m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
+
+ // The cat
+ m_settings->registerSetting("TheCat", false);
+
+ m_settings->registerSetting("InstSortMode", "Name");
+ m_settings->registerSetting("SelectedInstance", QString());
+
+ // Window state and geometry
+ m_settings->registerSetting("MainWindowState", "");
+ m_settings->registerSetting("MainWindowGeometry", "");
+
+ m_settings->registerSetting("ConsoleWindowState", "");
+ m_settings->registerSetting("ConsoleWindowGeometry", "");
+
+ m_settings->registerSetting("SettingsGeometry", "");
+
+ m_settings->registerSetting("PagedGeometry", "");
+
+ m_settings->registerSetting("NewInstanceGeometry", "");
+
+ m_settings->registerSetting("UpdateDialogGeometry", "");
+
+ // paste.ee API key
+ m_settings->registerSetting("PasteEEAPIKey", "multimc");
+
+ if(!BuildConfig.ANALYTICS_ID.isEmpty())
+ {
+ // Analytics
+ m_settings->registerSetting("Analytics", true);
+ m_settings->registerSetting("AnalyticsSeen", 0);
+ m_settings->registerSetting("AnalyticsClientID", QString());
+ }
+
+ // Init page provider
+ {
+ m_globalSettingsProvider = std::make_shared(tr("Settings"));
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
+ }
+ qDebug() << "<> Settings loaded.";
+ }
+
+#ifndef QT_NO_ACCESSIBILITY
+ QAccessible::installFactory(groupViewAccessibleFactory);
+#endif /* !QT_NO_ACCESSIBILITY */
+
+ // initialize network access and proxy setup
+ {
+ m_network = new QNetworkAccessManager();
+ QString proxyTypeStr = settings()->get("ProxyType").toString();
+ QString addr = settings()->get("ProxyAddr").toString();
+ int port = settings()->get("ProxyPort").value();
+ QString user = settings()->get("ProxyUser").toString();
+ QString pass = settings()->get("ProxyPass").toString();
+ updateProxySettings(proxyTypeStr, addr, port, user, pass);
+ qDebug() << "<> Network done.";
+ }
+
+ // load translations
+ {
+ m_translations.reset(new TranslationsModel("translations"));
+ auto bcp47Name = m_settings->get("Language").toString();
+ m_translations->selectLanguage(bcp47Name);
+ qDebug() << "Your language is" << bcp47Name;
+ qDebug() << "<> Translations loaded.";
+ }
+
+ // initialize the updater
+ if(BuildConfig.UPDATER_ENABLED)
+ {
+ auto platform = getIdealPlatform(BuildConfig.BUILD_PLATFORM);
+ auto channelUrl = BuildConfig.UPDATER_BASE + platform + "/channels.json";
+ qDebug() << "Initializing updater with platform: " << platform << " -- " << channelUrl;
+ m_updateChecker.reset(new UpdateChecker(m_network, channelUrl, BuildConfig.VERSION_BUILD));
+ qDebug() << "<> Updater started.";
+ }
+
+ // Instance icons
+ {
+ auto setting = APPLICATION->settings()->getSetting("IconsDir");
+ QStringList instFolders =
+ {
+ ":/icons/multimc/32x32/instances/",
+ ":/icons/multimc/50x50/instances/",
+ ":/icons/multimc/128x128/instances/",
+ ":/icons/multimc/scalable/instances/"
+ };
+ m_icons.reset(new IconList(instFolders, setting->get().toString()));
+ connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value)
+ {
+ m_icons->directoryChanged(value.toString());
+ });
+ qDebug() << "<> Instance icons intialized.";
+ }
+
+ // Icon themes
+ {
+ // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies!
+ // set icon theme search path!
+ auto searchPaths = QIcon::themeSearchPaths();
+ searchPaths.append("iconthemes");
+ QIcon::setThemeSearchPaths(searchPaths);
+ qDebug() << "<> Icon themes initialized.";
+ }
+
+ // Initialize widget themes
+ {
+ auto insertTheme = [this](ITheme * theme)
+ {
+ m_themes.insert(std::make_pair(theme->id(), std::unique_ptr(theme)));
+ };
+ auto darkTheme = new DarkTheme();
+ insertTheme(new SystemTheme());
+ insertTheme(darkTheme);
+ insertTheme(new BrightTheme());
+ insertTheme(new CustomTheme(darkTheme, "custom"));
+ qDebug() << "<> Widget themes initialized.";
+ }
+
+ // initialize and load all instances
+ {
+ auto InstDirSetting = m_settings->getSetting("InstanceDir");
+ // instance path: check for problems with '!' in instance path and warn the user in the log
+ // and remember that we have to show him a dialog when the gui starts (if it does so)
+ QString instDir = InstDirSetting->get().toString();
+ qDebug() << "Instance path : " << instDir;
+ if (FS::checkProblemticPathJava(QDir(instDir)))
+ {
+ qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
+ }
+ m_instances.reset(new InstanceList(m_settings, instDir, this));
+ connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
+ qDebug() << "Loading Instances...";
+ m_instances->loadList();
+ qDebug() << "<> Instances loaded.";
+ }
+
+ {
+ m_authserver.reset(new AuthServer(this));
+ qDebug() << "<> Auth server started.";
+ }
+
+ // load auth providers
+ {
+ AuthProviders::load(m_authserver);
+ }
+
+ // and accounts
+ {
+ m_accounts.reset(new AccountList(this));
+ qDebug() << "Loading accounts...";
+ m_accounts->setListFilePath("accounts.json", true);
+ m_accounts->loadList();
+ m_accounts->fillQueue();
+ qDebug() << "<> Accounts loaded.";
+ }
+
+ // init the http meta cache
+ {
+ m_metacache.reset(new HttpMetaCache("metacache"));
+ m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
+ m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
+ m_metacache->addBase("versions", QDir("versions").absolutePath());
+ m_metacache->addBase("libraries", QDir("libraries").absolutePath());
+ m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
+ m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
+ m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
+ m_metacache->addBase("general", QDir("cache").absolutePath());
+ m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath());
+ m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath());
+ m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath());
+ m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
+ m_metacache->addBase("root", QDir::currentPath());
+ m_metacache->addBase("translations", QDir("translations").absolutePath());
+ m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
+ m_metacache->addBase("meta", QDir("meta").absolutePath());
+ m_metacache->addBase("injectors", QDir("injectors").absolutePath());
+ m_metacache->Load();
+ qDebug() << "<> Cache initialized.";
+ }
+
+ // now we have network, download translation updates
+ m_translations->downloadIndex();
+
+ //FIXME: what to do with these?
+ m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory()));
+ m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory()));
+ for (auto profiler : m_profilers.values())
+ {
+ profiler->registerSettings(m_settings);
+ }
+
+ // Create the MCEdit thing... why is this here?
+ {
+ m_mcedit.reset(new MCEditTool(m_settings));
+ }
+
+ connect(this, &Application::aboutToQuit, [this](){
+ if(m_instances)
+ {
+ // save any remaining instance state
+ m_instances->saveNow();
+ }
+ if(logFile)
+ {
+ logFile->flush();
+ logFile->close();
+ }
+ });
+
+ {
+ setIconTheme(settings()->get("IconTheme").toString());
+ qDebug() << "<> Icon theme set.";
+ setApplicationTheme(settings()->get("ApplicationTheme").toString(), true);
+ qDebug() << "<> Application theme set.";
+ }
+
+ // Initialize analytics
+ /*
+ [this]()
+ {
+ const int analyticsVersion = 2;
+ if(BuildConfig.ANALYTICS_ID.isEmpty())
+ {
+ return;
+ }
+
+ auto analyticsSetting = m_settings->getSetting("Analytics");
+ connect(analyticsSetting.get(), &Setting::SettingChanged, this, &Application::analyticsSettingChanged);
+ QString clientID = m_settings->get("AnalyticsClientID").toString();
+ if(clientID.isEmpty())
+ {
+ clientID = QUuid::createUuid().toString();
+ clientID.remove(QLatin1Char('{'));
+ clientID.remove(QLatin1Char('}'));
+ m_settings->set("AnalyticsClientID", clientID);
+ }
+ m_analytics = new GAnalytics(BuildConfig.ANALYTICS_ID, clientID, analyticsVersion, this);
+ m_analytics->setLogLevel(GAnalytics::Debug);
+ m_analytics->setAnonymizeIPs(true);
+ // FIXME: the ganalytics library has no idea about our fancy shared pointers...
+ m_analytics->setNetworkAccessManager(network().get());
+
+ if(m_settings->get("AnalyticsSeen").toInt() < m_analytics->version())
+ {
+ qDebug() << "Analytics info not seen by user yet (or old version).";
+ return;
+ }
+ if(!m_settings->get("Analytics").toBool())
+ {
+ qDebug() << "Analytics disabled by user.";
+ return;
+ }
+
+ m_analytics->enable();
+ qDebug() << "<> Initialized analytics with tid" << BuildConfig.ANALYTICS_ID;
+ }();
+ */
+
+ if(createSetupWizard())
+ {
+ return;
+ }
+ performMainStartupAction();
+}
+
+bool Application::createSetupWizard()
+{
+ bool javaRequired = [&]()
+ {
+ QString currentHostName = QHostInfo::localHostName();
+ QString oldHostName = settings()->get("LastHostname").toString();
+ if (currentHostName != oldHostName)
+ {
+ settings()->set("LastHostname", currentHostName);
+ return true;
+ }
+ QString currentJavaPath = settings()->get("JavaPath").toString();
+ QString actualPath = FS::ResolveExecutable(currentJavaPath);
+ if (actualPath.isNull())
+ {
+ return true;
+ }
+ return false;
+ }();
+ bool analyticsRequired = [&]()
+ {
+ if(!m_analytics) {
+ return false;
+ }
+ if(BuildConfig.ANALYTICS_ID.isEmpty()) {
+ return false;
+ }
+ if (!settings()->get("Analytics").toBool()) {
+ return false;
+ }
+ if (settings()->get("AnalyticsSeen").toInt() < analytics()->version()) {
+ return true;
+ }
+ return false;
+ }();
+ bool languageRequired = [&]()
+ {
+ if (settings()->get("Language").toString().isEmpty())
+ return true;
+ return false;
+ }();
+ bool wizardRequired = javaRequired || analyticsRequired || languageRequired;
+
+ if(wizardRequired)
+ {
+ m_setupWizard = new SetupWizard(nullptr);
+ if (languageRequired)
+ {
+ m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard));
+ }
+ if (javaRequired)
+ {
+ m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
+ }
+ if(analyticsRequired)
+ {
+ m_setupWizard->addPage(new AnalyticsWizardPage(m_setupWizard));
+ }
+ connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
+ m_setupWizard->show();
+ return true;
+ }
+ return false;
+}
+
+void Application::setupWizardFinished(int status)
+{
+ qDebug() << "Wizard result =" << status;
+ performMainStartupAction();
+}
+
+void Application::performMainStartupAction()
+{
+ m_status = Application::Initialized;
+ if(!m_instanceIdToLaunch.isEmpty())
+ {
+ auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
+ if(inst)
+ {
+ QuickPlayTargetPtr serverOrWorldToJoin = nullptr;
+ MinecraftAccountPtr accountToUse = nullptr;
+ bool offline = m_offline;
+
+ qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching";
+ if(!m_serverToJoin.isEmpty())
+ {
+ // FIXME: validate the server string
+ serverOrWorldToJoin.reset(new QuickPlayTarget(QuickPlayTarget::parseMultiplayer(m_serverToJoin)));
+ qDebug() << " Launching with server" << m_serverToJoin;
+ }
+
+ if(!m_worldToJoin.isEmpty())
+ {
+ serverOrWorldToJoin.reset(new QuickPlayTarget(QuickPlayTarget::parseSingleplayer(m_worldToJoin)));
+ qDebug() << " Launching with world" << m_worldToJoin;
+ }
+
+ if(!m_profileToUse.isEmpty())
+ {
+ accountToUse = accounts()->getAccountByProfileName(m_profileToUse);
+ if(!accountToUse) {
+ return;
+ }
+ qDebug() << " Launching with account" << m_profileToUse;
+ }
+
+ launch(inst, !offline, nullptr, serverOrWorldToJoin, accountToUse, m_offlineName);
+ return;
+ }
+ }
+ if(!m_mainWindow)
+ {
+ // normal main window
+ showMainWindow(false);
+ qDebug() << "<> Main window shown.";
+ }
+ if(!m_zipToImport.isEmpty())
+ {
+ qDebug() << "<> Importing instance from zip:" << m_zipToImport;
+ m_mainWindow->droppedURLs({ m_zipToImport });
+ }
+}
+
+void Application::showFatalErrorMessage(const QString& title, const QString& content)
+{
+ m_status = Application::Failed;
+ auto dialog = CustomMessageBox::selectable(nullptr, title, content, QMessageBox::Critical);
+ dialog->exec();
+}
+
+Application::~Application()
+{
+ // Shut down logger by setting the logger function to nothing
+ qInstallMessageHandler(nullptr);
+
+#if defined Q_OS_WIN32
+ // Detach from Windows console
+ if(consoleAttached)
+ {
+ fclose(stdout);
+ fclose(stdin);
+ fclose(stderr);
+ FreeConsole();
+ }
+#endif
+}
+
+void Application::messageReceived(const QByteArray& message)
+{
+ if(status() != Initialized)
+ {
+ qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
+ return;
+ }
+
+ ApplicationMessage received;
+ received.parse(message);
+
+ auto & command = received.command;
+
+ if(command == "activate")
+ {
+ showMainWindow();
+ }
+ else if(command == "import")
+ {
+ QString path = received.args["path"];
+ if(path.isEmpty())
+ {
+ qWarning() << "Received" << command << "message without a zip path/URL.";
+ return;
+ }
+ m_mainWindow->droppedURLs({ QUrl(path) });
+ }
+ else if(command == "launch")
+ {
+ QString id = received.args["id"];
+ QString server = received.args["server"];
+ QString world = received.args["world"];
+ QString profile = received.args["profile"];
+ bool offline = received.args["offline_enabled"] == "true";
+ QString offlineName = received.args["offline_name"];
+
+ InstancePtr instance;
+ if(!id.isEmpty()) {
+ instance = instances()->getInstanceById(id);
+ if(!instance) {
+ qWarning() << "Launch command requires an valid instance ID. " << id << "resolves to nothing.";
+ return;
+ }
+ }
+ else {
+ qWarning() << "Launch command called without an instance ID...";
+ return;
+ }
+
+ QuickPlayTargetPtr quickPlayTarget = nullptr;
+ if(!server.isEmpty()) {
+ quickPlayTarget = std::make_shared(QuickPlayTarget::parseMultiplayer(server));
+ } else if(!world.isEmpty()) {
+ quickPlayTarget = std::make_shared(QuickPlayTarget::parseSingleplayer(world));
+ }
+
+ MinecraftAccountPtr accountObject;
+ if(!profile.isEmpty()) {
+ accountObject = accounts()->getAccountByProfileName(profile);
+ if(!accountObject) {
+ qWarning() << "Launch command requires the specified profile to be valid. " << profile << "does not resolve to any account.";
+ return;
+ }
+ }
+
+ launch(
+ instance,
+ !offline,
+ nullptr,
+ quickPlayTarget,
+ accountObject,
+ offlineName
+ );
+ }
+ else
+ {
+ qWarning() << "Received invalid message" << message;
+ }
+}
+
+void Application::analyticsSettingChanged(const Setting&, QVariant value)
+{
+ if(!m_analytics)
+ return;
+ bool enabled = value.toBool();
+ if(enabled)
+ {
+ qDebug() << "Analytics enabled by user.";
+ }
+ else
+ {
+ qDebug() << "Analytics disabled by user.";
+ }
+ m_analytics->enable(enabled);
+}
+
+std::shared_ptr Application::translations()
+{
+ return m_translations;
+}
+
+std::shared_ptr Application::javalist()
+{
+ if (!m_javalist)
+ {
+ m_javalist.reset(new JavaInstallList());
+ }
+ return m_javalist;
+}
+
+std::vector Application::getValidApplicationThemes()
+{
+ std::vector ret;
+ auto iter = m_themes.cbegin();
+ while (iter != m_themes.cend())
+ {
+ ret.push_back((*iter).second.get());
+ iter++;
+ }
+ return ret;
+}
+
+void Application::setApplicationTheme(const QString& name, bool initial)
+{
+ auto systemPalette = qApp->palette();
+ auto themeIter = m_themes.find(name);
+ if(themeIter != m_themes.end())
+ {
+ auto & theme = (*themeIter).second;
+ theme->apply(initial);
+ }
+ else
+ {
+ qWarning() << "Tried to set invalid theme:" << name;
+ }
+}
+
+void Application::setIconTheme(const QString& name)
+{
+ XdgIcon::setThemeName(name);
+}
+
+QIcon Application::getThemedIcon(const QString& name)
+{
+ if(name == "logo") {
+ return QIcon(":/logo.svg");
+ }
+ return XdgIcon::fromTheme(name);
+}
+
+bool Application::openJsonEditor(const QString &filename)
+{
+ const QString file = QDir::current().absoluteFilePath(filename);
+ if (m_settings->get("JsonEditor").toString().isEmpty())
+ {
+ return DesktopServices::openUrl(QUrl::fromLocalFile(file));
+ }
+ else
+ {
+ //return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file);
+ return DesktopServices::run(m_settings->get("JsonEditor").toString(), {file});
+ }
+}
+
+bool Application::launch(
+ InstancePtr instance,
+ bool online,
+ BaseProfilerFactory *profiler,
+ QuickPlayTargetPtr quickPlayTarget,
+ MinecraftAccountPtr accountToUse,
+ const QString& offlineName
+) {
+ if(m_updateRunning)
+ {
+ qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
+ }
+ else if(instance->canLaunch())
+ {
+ auto & extras = m_instanceExtras[instance->id()];
+ auto & window = extras.window;
+ if(window)
+ {
+ if(!window->saveAll())
+ {
+ return false;
+ }
+ }
+ auto & controller = extras.controller;
+ controller.reset(new LaunchController());
+ controller->setInstance(instance);
+ controller->setOnline(online);
+ controller->setProfiler(profiler);
+ controller->setQuickPlayTarget(quickPlayTarget);
+ controller->setAuthserver(m_authserver);
+ controller->setAccountToUse(accountToUse);
+ controller->setOfflineName(offlineName);
+ if(window)
+ {
+ controller->setParentWidget(window);
+ }
+ else if(m_mainWindow)
+ {
+ controller->setParentWidget(m_mainWindow);
+ }
+ connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
+ connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
+ addRunningInstance();
+ controller->start();
+ return true;
+ }
+ else if (instance->isRunning())
+ {
+ showInstanceWindow(instance, "console");
+ return true;
+ }
+ else if (instance->canEdit())
+ {
+ showInstanceWindow(instance);
+ return true;
+ }
+ return false;
+}
+
+bool Application::kill(InstancePtr instance)
+{
+ if (!instance->isRunning())
+ {
+ qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
+ return false;
+ }
+ auto & extras = m_instanceExtras[instance->id()];
+ // NOTE: copy of the shared pointer keeps it alive
+ auto controller = extras.controller;
+ if(controller)
+ {
+ return controller->abort();
+ }
+ return true;
+}
+
+void Application::addRunningInstance()
+{
+ m_runningInstances ++;
+ if(m_runningInstances == 1)
+ {
+ emit updateAllowedChanged(false);
+ }
+}
+
+void Application::subRunningInstance()
+{
+ if(m_runningInstances == 0)
+ {
+ qCritical() << "Something went really wrong and we now have less than 0 running instances... WTF";
+ return;
+ }
+ m_runningInstances --;
+ if(m_runningInstances == 0)
+ {
+ emit updateAllowedChanged(true);
+ }
+}
+
+bool Application::shouldExitNow() const
+{
+ return m_runningInstances == 0 && m_openWindows == 0;
+}
+
+bool Application::updatesAreAllowed()
+{
+ return m_runningInstances == 0;
+}
+
+void Application::updateIsRunning(bool running)
+{
+ m_updateRunning = running;
+}
+
+
+void Application::controllerSucceeded()
+{
+ auto controller = qobject_cast(QObject::sender());
+ if(!controller)
+ return;
+ auto id = controller->id();
+ auto & extras = m_instanceExtras[id];
+
+ // on success, do...
+ if (controller->instance()->settings()->get("AutoCloseConsole").toBool())
+ {
+ if(extras.window)
+ {
+ extras.window->close();
+ }
+ }
+ extras.controller.reset();
+ subRunningInstance();
+
+ // quit when there are no more windows.
+ if(shouldExitNow())
+ {
+ m_status = Status::Succeeded;
+ exit(0);
+ }
+}
+
+void Application::controllerFailed(const QString& error)
+{
+ Q_UNUSED(error);
+ auto controller = qobject_cast(QObject::sender());
+ if(!controller)
+ return;
+ auto id = controller->id();
+ auto & extras = m_instanceExtras[id];
+
+ // on failure, do... nothing
+ extras.controller.reset();
+ subRunningInstance();
+
+ // quit when there are no more windows.
+ if(shouldExitNow())
+ {
+ m_status = Status::Failed;
+ exit(1);
+ }
+}
+
+void Application::ShowGlobalSettings(class QWidget* parent, QString open_page)
+{
+ if(!m_globalSettingsProvider) {
+ return;
+ }
+ emit globalSettingsAboutToOpen();
+ {
+ SettingsObject::Lock lock(APPLICATION->settings());
+ PageDialog dlg(m_globalSettingsProvider.get(), open_page, parent);
+ dlg.exec();
+ }
+ emit globalSettingsClosed();
+}
+
+MainWindow* Application::showMainWindow(bool minimized)
+{
+ if(m_mainWindow)
+ {
+ m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized);
+ m_mainWindow->raise();
+ m_mainWindow->activateWindow();
+ }
+ else
+ {
+ m_mainWindow = new MainWindow();
+ m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
+ m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
+ if(minimized)
+ {
+ m_mainWindow->showMinimized();
+ }
+ else
+ {
+ m_mainWindow->show();
+ }
+
+ m_mainWindow->checkInstancePathForProblems();
+ connect(this, &Application::updateAllowedChanged, m_mainWindow, &MainWindow::updatesAllowedChanged);
+ connect(m_mainWindow, &MainWindow::isClosing, this, &Application::on_windowClose);
+ m_openWindows++;
+ }
+ // FIXME: move this somewhere else...
+ if(m_analytics)
+ {
+ auto windowSize = m_mainWindow->size();
+ auto sizeString = QString("%1x%2").arg(windowSize.width()).arg(windowSize.height());
+ qDebug() << "Viewport size" << sizeString;
+ m_analytics->setViewportSize(sizeString);
+ /*
+ * cm1 = java min heap [MB]
+ * cm2 = java max heap [MB]
+ * cm3 = system RAM [MB]
+ *
+ * cd1 = java version
+ * cd2 = java architecture
+ * cd3 = system architecture
+ * cd4 = CPU architecture
+ */
+ QVariantMap customValues;
+ int min = m_settings->get("MinMemAlloc").toInt();
+ int max = m_settings->get("MaxMemAlloc").toInt();
+ if(min < max)
+ {
+ customValues["cm1"] = min;
+ customValues["cm2"] = max;
+ }
+ else
+ {
+ customValues["cm1"] = max;
+ customValues["cm2"] = min;
+ }
+
+ constexpr uint64_t Mega = 1024ull * 1024ull;
+ int ramSize = int(Sys::getSystemRam() / Mega);
+ qDebug() << "RAM size is" << ramSize << "MB";
+ customValues["cm3"] = ramSize;
+
+ customValues["cd1"] = m_settings->get("JavaVersion");
+ customValues["cd2"] = m_settings->get("JavaArchitecture");
+ customValues["cd3"] = Sys::isSystem64bit() ? "64":"32";
+ customValues["cd4"] = Sys::isCPU64bit() ? "64":"32";
+ auto kernelInfo = Sys::getKernelInfo();
+ customValues["cd5"] = kernelInfo.kernelName;
+ customValues["cd6"] = kernelInfo.kernelVersion;
+ auto distInfo = Sys::getDistributionInfo();
+ if(!distInfo.distributionName.isEmpty())
+ {
+ customValues["cd7"] = distInfo.distributionName;
+ }
+ if(!distInfo.distributionVersion.isEmpty())
+ {
+ customValues["cd8"] = distInfo.distributionVersion;
+ }
+ m_analytics->sendScreenView("Main Window", customValues);
+ }
+ return m_mainWindow;
+}
+
+InstanceWindow *Application::showInstanceWindow(InstancePtr instance, QString page)
+{
+ if(!instance)
+ return nullptr;
+ auto id = instance->id();
+ auto & extras = m_instanceExtras[id];
+ auto & window = extras.window;
+
+ if(window)
+ {
+ window->raise();
+ window->activateWindow();
+ }
+ else
+ {
+ window = new InstanceWindow(instance);
+ m_openWindows ++;
+ connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose);
+ }
+ if(!page.isEmpty())
+ {
+ window->selectPage(page);
+ }
+ if(extras.controller)
+ {
+ extras.controller->setParentWidget(window);
+ }
+ return window;
+}
+
+void Application::on_windowClose()
+{
+ m_openWindows--;
+ auto instWindow = qobject_cast(QObject::sender());
+ if(instWindow)
+ {
+ auto & extras = m_instanceExtras[instWindow->instanceId()];
+ extras.window = nullptr;
+ if(extras.controller)
+ {
+ extras.controller->setParentWidget(m_mainWindow);
+ }
+ }
+ auto mainWindow = qobject_cast(QObject::sender());
+ if(mainWindow)
+ {
+ m_mainWindow = nullptr;
+ }
+ // quit when there are no more windows.
+ if(shouldExitNow())
+ {
+ exit(0);
+ }
+}
+
+QString Application::msaClientId() const {
+ return Secrets::getMSAClientID('-');
+}
+
+void Application::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password)
+{
+ // Set the application proxy settings.
+ if (proxyTypeStr == "SOCKS5")
+ {
+ QNetworkProxy::setApplicationProxy(
+ QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password));
+ }
+ else if (proxyTypeStr == "HTTP")
+ {
+ QNetworkProxy::setApplicationProxy(
+ QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password));
+ }
+ else if (proxyTypeStr == "None")
+ {
+ // If we have no proxy set, set no proxy and return.
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
+ }
+ else
+ {
+ // If we have "Default" selected, set Qt to use the system proxy settings.
+ QNetworkProxyFactory::setUseSystemConfiguration(true);
+ }
+
+ qDebug() << "Detecting proxy settings...";
+ QNetworkProxy proxy = QNetworkProxy::applicationProxy();
+ m_network->setProxy(proxy);
+
+ QString proxyDesc;
+ if (proxy.type() == QNetworkProxy::NoProxy)
+ {
+ qDebug() << "Using no proxy is an option!";
+ return;
+ }
+ switch (proxy.type())
+ {
+ case QNetworkProxy::DefaultProxy:
+ proxyDesc = "Default proxy: ";
+ break;
+ case QNetworkProxy::Socks5Proxy:
+ proxyDesc = "Socks5 proxy: ";
+ break;
+ case QNetworkProxy::HttpProxy:
+ proxyDesc = "HTTP proxy: ";
+ break;
+ case QNetworkProxy::HttpCachingProxy:
+ proxyDesc = "HTTP caching: ";
+ break;
+ case QNetworkProxy::FtpCachingProxy:
+ proxyDesc = "FTP caching: ";
+ break;
+ default:
+ proxyDesc = "DERP proxy: ";
+ break;
+ }
+ proxyDesc += QString("%1:%2")
+ .arg(proxy.hostName())
+ .arg(proxy.port());
+ qDebug() << proxyDesc;
+}
+
+shared_qobject_ptr< HttpMetaCache > Application::metacache()
+{
+ return m_metacache;
+}
+
+shared_qobject_ptr Application::network()
+{
+ return m_network;
+}
+
+shared_qobject_ptr Application::metadataIndex()
+{
+ if (!m_metadataIndex)
+ {
+ m_metadataIndex.reset(new Meta::Index());
+ }
+ return m_metadataIndex;
+}
+
+QString Application::getJarsPath()
+{
+ if(m_jarsPath.isEmpty())
+ {
+ return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
+ }
+ return m_jarsPath;
+}
diff --git a/ultimmc/launcher/Application.h b/ultimmc/launcher/Application.h
new file mode 100644
index 0000000..cb6bea8
--- /dev/null
+++ b/ultimmc/launcher/Application.h
@@ -0,0 +1,246 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "minecraft/launch/QuickPlayTarget.h"
+
+class LaunchController;
+class LocalPeer;
+class InstanceWindow;
+class MainWindow;
+class SetupWizard;
+class GenericPageProvider;
+class QFile;
+class HttpMetaCache;
+class SettingsObject;
+class InstanceList;
+class AccountList;
+class IconList;
+class QNetworkAccessManager;
+class JavaInstallList;
+class UpdateChecker;
+class BaseProfilerFactory;
+class BaseDetachedToolFactory;
+class TranslationsModel;
+class ITheme;
+class MCEditTool;
+class AuthServer;
+class GAnalytics;
+
+namespace Meta {
+ class Index;
+}
+
+#if defined(APPLICATION)
+#undef APPLICATION
+#endif
+#define APPLICATION (static_cast(QCoreApplication::instance()))
+
+class Application : public QApplication
+{
+ // friends for the purpose of limiting access to deprecated stuff
+ Q_OBJECT
+public:
+ enum Status {
+ StartingUp,
+ Failed,
+ Succeeded,
+ Initialized
+ };
+
+public:
+ Application(int &argc, char **argv);
+ virtual ~Application();
+
+ GAnalytics *analytics() const {
+ return m_analytics;
+ }
+
+ std::shared_ptr settings() const {
+ return m_settings;
+ }
+
+ qint64 timeSinceStart() const {
+ return startTime.msecsTo(QDateTime::currentDateTime());
+ }
+
+ QIcon getThemedIcon(const QString& name);
+
+ void setIconTheme(const QString& name);
+
+ std::vector getValidApplicationThemes();
+
+ void setApplicationTheme(const QString& name, bool initial);
+
+ shared_qobject_ptr updateChecker() {
+ return m_updateChecker;
+ }
+
+ std::shared_ptr translations();
+
+ std::shared_ptr javalist();
+
+ std::shared_ptr instances() const {
+ return m_instances;
+ }
+
+ std::shared_ptr icons() const {
+ return m_icons;
+ }
+
+ MCEditTool *mcedit() const {
+ return m_mcedit.get();
+ }
+
+ shared_qobject_ptr accounts() const {
+ return m_accounts;
+ }
+
+ QString msaClientId() const;
+
+ Status status() const {
+ return m_status;
+ }
+
+ const QMap> &profilers() const {
+ return m_profilers;
+ }
+
+ void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
+
+ shared_qobject_ptr network();
+
+ shared_qobject_ptr metacache();
+
+ shared_qobject_ptr metadataIndex();
+
+ QString getJarsPath();
+
+ /// this is the root of the 'installation'. Used for automatic updates
+ const QString &root() {
+ return m_rootPath;
+ }
+
+ /*!
+ * Opens a json file using either a system default editor, or, if not empty, the editor
+ * specified in the settings
+ */
+ bool openJsonEditor(const QString &filename);
+
+ InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString());
+ MainWindow *showMainWindow(bool minimized = false);
+
+ void updateIsRunning(bool running);
+ bool updatesAreAllowed();
+
+ void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
+
+signals:
+ void updateAllowedChanged(bool status);
+ void globalSettingsAboutToOpen();
+ void globalSettingsClosed();
+
+public slots:
+ bool launch(
+ InstancePtr instance,
+ bool online = true,
+ BaseProfilerFactory *profiler = nullptr,
+ QuickPlayTargetPtr quickPlayTarget = nullptr,
+ MinecraftAccountPtr accountToUse = nullptr,
+ const QString &offlineName = QString()
+ );
+ bool kill(InstancePtr instance);
+
+private slots:
+ void on_windowClose();
+ void messageReceived(const QByteArray & message);
+ void controllerSucceeded();
+ void controllerFailed(const QString & error);
+ void analyticsSettingChanged(const Setting &setting, QVariant value);
+ void setupWizardFinished(int status);
+
+private:
+ bool createSetupWizard();
+ void performMainStartupAction();
+
+ // sets the fatal error message and m_status to Failed.
+ void showFatalErrorMessage(const QString & title, const QString & content);
+
+private:
+ void addRunningInstance();
+ void subRunningInstance();
+ bool shouldExitNow() const;
+
+private:
+ QDateTime startTime;
+
+ shared_qobject_ptr m_network;
+
+ shared_qobject_ptr m_updateChecker;
+ shared_qobject_ptr m_accounts;
+
+ shared_qobject_ptr m_metacache;
+ shared_qobject_ptr m_metadataIndex;
+
+ std::shared_ptr m_settings;
+ std::shared_ptr m_instances;
+ std::shared_ptr m_icons;
+ std::shared_ptr m_javalist;
+ std::shared_ptr m_translations;
+ std::shared_ptr m_globalSettingsProvider;
+ std::map> m_themes;
+ std::unique_ptr m_mcedit;
+ std::shared_ptr m_authserver;
+ QString m_jarsPath;
+ QSet m_features;
+
+ QMap> m_profilers;
+
+ QString m_rootPath;
+ Status m_status = Application::StartingUp;
+
+#if defined Q_OS_WIN32
+ // used on Windows to attach the standard IO streams
+ bool consoleAttached = false;
+#endif
+
+ // FIXME: attach to instances instead.
+ struct InstanceXtras {
+ InstanceWindow * window = nullptr;
+ shared_qobject_ptr controller;
+ };
+ std::map m_instanceExtras;
+
+ // main state variables
+ size_t m_openWindows = 0;
+ size_t m_runningInstances = 0;
+ bool m_updateRunning = false;
+
+ // main window, if any
+ MainWindow * m_mainWindow = nullptr;
+
+ // peer launcher instance connector - used to implement single instance launcher and signalling
+ LocalPeer * m_peerInstance = nullptr;
+
+ GAnalytics * m_analytics = nullptr;
+ SetupWizard * m_setupWizard = nullptr;
+public:
+ QString m_instanceIdToLaunch;
+ QString m_serverToJoin;
+ QString m_worldToJoin;
+ QString m_profileToUse;
+ bool m_offline = false;
+ QString m_offlineName;
+ bool m_liveCheck = false;
+ QUrl m_zipToImport;
+ std::unique_ptr logFile;
+};
diff --git a/ultimmc/launcher/ApplicationMessage.cpp b/ultimmc/launcher/ApplicationMessage.cpp
new file mode 100644
index 0000000..e22bf13
--- /dev/null
+++ b/ultimmc/launcher/ApplicationMessage.cpp
@@ -0,0 +1,31 @@
+#include "ApplicationMessage.h"
+
+#include
+#include
+
+void ApplicationMessage::parse(const QByteArray & input) {
+ auto doc = QJsonDocument::fromBinaryData(input);
+ auto root = doc.object();
+
+ command = root.value("command").toString();
+ args.clear();
+
+ auto parsedArgs = root.value("args").toObject();
+ for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
+ args[iter.key()] = iter.value().toString();
+ }
+}
+
+QByteArray ApplicationMessage::serialize() {
+ QJsonObject root;
+ root.insert("command", command);
+ QJsonObject outArgs;
+ for (auto iter = args.begin(); iter != args.end(); iter++) {
+ outArgs[iter.key()] = iter.value();
+ }
+ root.insert("args", outArgs);
+
+ QJsonDocument out;
+ out.setObject(root);
+ return out.toBinaryData();
+}
diff --git a/ultimmc/launcher/ApplicationMessage.h b/ultimmc/launcher/ApplicationMessage.h
new file mode 100644
index 0000000..745bdea
--- /dev/null
+++ b/ultimmc/launcher/ApplicationMessage.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include
+#include
+#include
+
+struct ApplicationMessage {
+ QString command;
+ QMap args;
+
+ QByteArray serialize();
+ void parse(const QByteArray & input);
+};
diff --git a/ultimmc/launcher/AuthServer.cpp b/ultimmc/launcher/AuthServer.cpp
new file mode 100644
index 0000000..ebc0bb9
--- /dev/null
+++ b/ultimmc/launcher/AuthServer.cpp
@@ -0,0 +1,188 @@
+#include "AuthServer.h"
+
+#include
+#include
+#include
+#include
+#include