From b6d455a02bd338e9dc0faa09d4d8177ecd8d569a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 10 Apr 2016 15:53:05 +0200 Subject: NOISSUE reorganize and document libraries --- libraries/README.md | 145 + libraries/classparser/CMakeLists.txt | 41 + libraries/classparser/include/classparser_config.h | 22 + libraries/classparser/include/javautils.h | 29 + libraries/classparser/src/annotations.cpp | 85 + libraries/classparser/src/annotations.h | 277 ++ libraries/classparser/src/classfile.h | 156 + libraries/classparser/src/constants.h | 220 + libraries/classparser/src/errors.h | 8 + libraries/classparser/src/javaendian.h | 76 + libraries/classparser/src/javautils.cpp | 83 + libraries/classparser/src/membuffer.h | 63 + libraries/gui/CMakeLists.txt | 28 - libraries/gui/DesktopServices.cpp | 149 - libraries/gui/DesktopServices.h | 37 - libraries/gui/SkinUtils.cpp | 47 - libraries/gui/SkinUtils.h | 25 - libraries/gui/icons/IconList.cpp | 381 -- libraries/gui/icons/IconList.h | 85 - libraries/gui/icons/MMCIcon.cpp | 89 - libraries/gui/icons/MMCIcon.h | 55 - libraries/hoedown/CMakeLists.txt | 26 + libraries/hoedown/LICENSE | 15 + libraries/hoedown/README.md | 9 + libraries/hoedown/include/hoedown/autolink.h | 46 + libraries/hoedown/include/hoedown/buffer.h | 134 + libraries/hoedown/include/hoedown/document.h | 172 + libraries/hoedown/include/hoedown/escape.h | 28 + libraries/hoedown/include/hoedown/html.h | 84 + libraries/hoedown/include/hoedown/stack.h | 52 + libraries/hoedown/include/hoedown/version.h | 33 + libraries/hoedown/src/autolink.c | 281 ++ libraries/hoedown/src/buffer.c | 308 ++ libraries/hoedown/src/document.c | 2958 ++++++++++++ libraries/hoedown/src/escape.c | 188 + libraries/hoedown/src/html.c | 754 +++ libraries/hoedown/src/html_blocks.c | 240 + libraries/hoedown/src/html_smartypants.c | 435 ++ libraries/hoedown/src/stack.c | 79 + libraries/hoedown/src/version.c | 9 + libraries/iconfix/CMakeLists.txt | 18 + libraries/iconfix/internal/qhexstring_p.h | 100 + libraries/iconfix/internal/qiconloader.cpp | 688 +++ libraries/iconfix/internal/qiconloader_p.h | 219 + libraries/iconfix/xdgicon.cpp | 152 + libraries/iconfix/xdgicon.h | 46 + libraries/javacheck/.gitignore | 6 + libraries/javacheck/CMakeLists.txt | 13 + libraries/javacheck/JavaCheck.java | 24 + libraries/launcher/.gitignore | 6 + libraries/launcher/CMakeLists.txt | 34 + libraries/launcher/net/minecraft/Launcher.java | 165 + libraries/launcher/org/multimc/EntryPoint.java | 178 + libraries/launcher/org/multimc/IconLoader.java | 132 + libraries/launcher/org/multimc/Launcher.java | 22 + libraries/launcher/org/multimc/LegacyFrame.java | 112 + .../launcher/org/multimc/NotFoundException.java | 21 + libraries/launcher/org/multimc/ParamBucket.java | 86 + libraries/launcher/org/multimc/ParseException.java | 22 + libraries/launcher/org/multimc/Utils.java | 280 ++ .../org/multimc/legacy/LegacyLauncher.java | 175 + .../org/multimc/onesix/OneSixLauncher.java | 367 ++ .../org/simplericity/macify/eawt/Application.java | 176 + .../macify/eawt/ApplicationAdapter.java | 48 + .../simplericity/macify/eawt/ApplicationEvent.java | 25 + .../macify/eawt/ApplicationListener.java | 27 + .../macify/eawt/DefaultApplication.java | 418 ++ libraries/libnbtplusplus | 1 + libraries/logic/AbstractCommonModel.cpp | 133 - libraries/logic/AbstractCommonModel.h | 462 -- libraries/logic/BaseConfigObject.cpp | 103 - libraries/logic/BaseConfigObject.h | 50 - libraries/logic/BaseInstaller.cpp | 61 - libraries/logic/BaseInstaller.h | 46 - libraries/logic/BaseInstance.cpp | 270 -- libraries/logic/BaseInstance.h | 243 - libraries/logic/BaseVersion.h | 59 - libraries/logic/BaseVersionList.cpp | 104 - libraries/logic/BaseVersionList.h | 126 - libraries/logic/CMakeLists.txt | 344 -- libraries/logic/Commandline.cpp | 483 -- libraries/logic/Commandline.h | 252 - libraries/logic/DefaultVariable.h | 35 - libraries/logic/Env.cpp | 222 - libraries/logic/Env.h | 60 - libraries/logic/Exception.h | 34 - libraries/logic/FileSystem.cpp | 436 -- libraries/logic/FileSystem.h | 123 - libraries/logic/GZip.cpp | 115 - libraries/logic/GZip.h | 12 - libraries/logic/InstanceList.cpp | 580 --- libraries/logic/InstanceList.h | 187 - libraries/logic/Json.cpp | 272 -- libraries/logic/Json.h | 249 - libraries/logic/MMCStrings.cpp | 76 - libraries/logic/MMCStrings.h | 10 - libraries/logic/MMCZip.cpp | 491 -- libraries/logic/MMCZip.h | 88 - libraries/logic/NullInstance.h | 90 - libraries/logic/QObjectPtr.h | 78 - libraries/logic/RWStorage.h | 60 - libraries/logic/RecursiveFileSystemWatcher.cpp | 111 - libraries/logic/RecursiveFileSystemWatcher.h | 63 - libraries/logic/SeparatorPrefixTree.h | 298 -- libraries/logic/TypeMagic.h | 37 - libraries/logic/Version.cpp | 140 - libraries/logic/Version.h | 110 - libraries/logic/java/JavaChecker.cpp | 159 - libraries/logic/java/JavaChecker.h | 54 - libraries/logic/java/JavaCheckerJob.cpp | 45 - libraries/logic/java/JavaCheckerJob.h | 84 - libraries/logic/java/JavaInstall.cpp | 28 - libraries/logic/java/JavaInstall.h | 38 - libraries/logic/java/JavaInstallList.cpp | 186 - libraries/logic/java/JavaInstallList.h | 71 - libraries/logic/java/JavaUtils.cpp | 219 - libraries/logic/java/JavaUtils.h | 43 - libraries/logic/java/JavaVersion.cpp | 112 - libraries/logic/java/JavaVersion.h | 30 - libraries/logic/launch/LaunchStep.cpp | 27 - libraries/logic/launch/LaunchStep.h | 48 - libraries/logic/launch/LaunchTask.cpp | 228 - libraries/logic/launch/LaunchTask.h | 122 - libraries/logic/launch/LoggedProcess.cpp | 163 - libraries/logic/launch/LoggedProcess.h | 76 - libraries/logic/launch/MessageLevel.cpp | 36 - libraries/logic/launch/MessageLevel.h | 28 - libraries/logic/launch/steps/CheckJava.cpp | 92 - libraries/logic/launch/steps/CheckJava.h | 41 - libraries/logic/launch/steps/LaunchMinecraft.cpp | 154 - libraries/logic/launch/steps/LaunchMinecraft.h | 48 - libraries/logic/launch/steps/ModMinecraftJar.cpp | 44 - libraries/logic/launch/steps/ModMinecraftJar.h | 39 - libraries/logic/launch/steps/PostLaunchCommand.cpp | 84 - libraries/logic/launch/steps/PostLaunchCommand.h | 39 - libraries/logic/launch/steps/PreLaunchCommand.cpp | 85 - libraries/logic/launch/steps/PreLaunchCommand.h | 39 - libraries/logic/launch/steps/TextPrint.cpp | 29 - libraries/logic/launch/steps/TextPrint.h | 43 - libraries/logic/launch/steps/Update.cpp | 50 - libraries/logic/launch/steps/Update.h | 41 - libraries/logic/minecraft/AssetsUtils.cpp | 230 - libraries/logic/minecraft/AssetsUtils.h | 48 - libraries/logic/minecraft/GradleSpecifier.h | 129 - libraries/logic/minecraft/JarMod.h | 12 - libraries/logic/minecraft/Library.cpp | 239 - libraries/logic/minecraft/Library.h | 184 - libraries/logic/minecraft/MinecraftInstance.cpp | 369 -- libraries/logic/minecraft/MinecraftInstance.h | 69 - libraries/logic/minecraft/MinecraftProfile.cpp | 610 --- libraries/logic/minecraft/MinecraftProfile.h | 200 - libraries/logic/minecraft/MinecraftVersion.cpp | 215 - libraries/logic/minecraft/MinecraftVersion.h | 119 - libraries/logic/minecraft/MinecraftVersionList.cpp | 591 --- libraries/logic/minecraft/MinecraftVersionList.h | 72 - libraries/logic/minecraft/Mod.cpp | 377 -- libraries/logic/minecraft/Mod.h | 134 - libraries/logic/minecraft/ModList.cpp | 616 --- libraries/logic/minecraft/ModList.h | 160 - libraries/logic/minecraft/MojangDownloadInfo.h | 71 - libraries/logic/minecraft/MojangVersionFormat.cpp | 381 -- libraries/logic/minecraft/MojangVersionFormat.h | 25 - libraries/logic/minecraft/OpSys.cpp | 42 - libraries/logic/minecraft/OpSys.h | 37 - libraries/logic/minecraft/ParseUtils.cpp | 34 - libraries/logic/minecraft/ParseUtils.h | 11 - libraries/logic/minecraft/ProfilePatch.h | 104 - libraries/logic/minecraft/ProfileStrategy.h | 35 - libraries/logic/minecraft/ProfileUtils.cpp | 191 - libraries/logic/minecraft/ProfileUtils.h | 25 - libraries/logic/minecraft/Rule.cpp | 93 - libraries/logic/minecraft/Rule.h | 101 - libraries/logic/minecraft/VersionBuildError.h | 58 - libraries/logic/minecraft/VersionFile.cpp | 60 - libraries/logic/minecraft/VersionFile.h | 195 - libraries/logic/minecraft/VersionFilterData.cpp | 75 - libraries/logic/minecraft/VersionFilterData.h | 32 - libraries/logic/minecraft/World.cpp | 385 -- libraries/logic/minecraft/World.h | 83 - libraries/logic/minecraft/WorldList.cpp | 355 -- libraries/logic/minecraft/WorldList.h | 125 - libraries/logic/minecraft/auth/AuthSession.cpp | 30 - libraries/logic/minecraft/auth/AuthSession.h | 51 - libraries/logic/minecraft/auth/MojangAccount.cpp | 278 -- libraries/logic/minecraft/auth/MojangAccount.h | 173 - .../logic/minecraft/auth/MojangAccountList.cpp | 427 -- libraries/logic/minecraft/auth/MojangAccountList.h | 201 - libraries/logic/minecraft/auth/YggdrasilTask.cpp | 255 -- libraries/logic/minecraft/auth/YggdrasilTask.h | 150 - .../minecraft/auth/flows/AuthenticateTask.cpp | 202 - .../logic/minecraft/auth/flows/AuthenticateTask.h | 46 - .../logic/minecraft/auth/flows/RefreshTask.cpp | 144 - libraries/logic/minecraft/auth/flows/RefreshTask.h | 44 - .../logic/minecraft/auth/flows/ValidateTask.cpp | 61 - .../logic/minecraft/auth/flows/ValidateTask.h | 47 - libraries/logic/minecraft/forge/ForgeInstaller.cpp | 458 -- libraries/logic/minecraft/forge/ForgeInstaller.h | 52 - libraries/logic/minecraft/forge/ForgeVersion.cpp | 55 - libraries/logic/minecraft/forge/ForgeVersion.h | 42 - .../logic/minecraft/forge/ForgeVersionList.cpp | 450 -- libraries/logic/minecraft/forge/ForgeVersionList.h | 90 - .../logic/minecraft/forge/ForgeXzDownload.cpp | 358 -- libraries/logic/minecraft/forge/ForgeXzDownload.h | 59 - libraries/logic/minecraft/forge/LegacyForge.cpp | 56 - libraries/logic/minecraft/forge/LegacyForge.h | 25 - libraries/logic/minecraft/ftb/FTBPlugin.cpp | 395 -- libraries/logic/minecraft/ftb/FTBPlugin.h | 13 - .../logic/minecraft/ftb/FTBProfileStrategy.cpp | 128 - libraries/logic/minecraft/ftb/FTBProfileStrategy.h | 21 - libraries/logic/minecraft/ftb/FTBVersion.h | 32 - .../logic/minecraft/ftb/LegacyFTBInstance.cpp | 27 - libraries/logic/minecraft/ftb/LegacyFTBInstance.h | 17 - .../logic/minecraft/ftb/OneSixFTBInstance.cpp | 138 - libraries/logic/minecraft/ftb/OneSixFTBInstance.h | 30 - .../logic/minecraft/legacy/LegacyInstance.cpp | 453 -- libraries/logic/minecraft/legacy/LegacyInstance.h | 142 - libraries/logic/minecraft/legacy/LegacyUpdate.cpp | 393 -- libraries/logic/minecraft/legacy/LegacyUpdate.h | 70 - .../logic/minecraft/legacy/LwjglVersionList.cpp | 189 - .../logic/minecraft/legacy/LwjglVersionList.h | 156 - .../minecraft/liteloader/LiteLoaderInstaller.cpp | 142 - .../minecraft/liteloader/LiteLoaderInstaller.h | 39 - .../minecraft/liteloader/LiteLoaderVersionList.cpp | 276 -- .../minecraft/liteloader/LiteLoaderVersionList.h | 119 - .../logic/minecraft/onesix/OneSixInstance.cpp | 597 --- libraries/logic/minecraft/onesix/OneSixInstance.h | 117 - .../minecraft/onesix/OneSixProfileStrategy.cpp | 418 -- .../logic/minecraft/onesix/OneSixProfileStrategy.h | 26 - libraries/logic/minecraft/onesix/OneSixUpdate.cpp | 342 -- libraries/logic/minecraft/onesix/OneSixUpdate.h | 67 - .../logic/minecraft/onesix/OneSixVersionFormat.cpp | 225 - .../logic/minecraft/onesix/OneSixVersionFormat.h | 22 - libraries/logic/net/ByteArrayDownload.cpp | 105 - libraries/logic/net/ByteArrayDownload.h | 48 - libraries/logic/net/CacheDownload.cpp | 192 - libraries/logic/net/CacheDownload.h | 63 - libraries/logic/net/HttpMetaCache.cpp | 273 -- libraries/logic/net/HttpMetaCache.h | 125 - libraries/logic/net/MD5EtagDownload.cpp | 155 - libraries/logic/net/MD5EtagDownload.h | 52 - libraries/logic/net/NetAction.h | 96 - libraries/logic/net/NetJob.cpp | 125 - libraries/logic/net/NetJob.h | 117 - libraries/logic/net/PasteUpload.cpp | 99 - libraries/logic/net/PasteUpload.h | 50 - libraries/logic/net/URLConstants.cpp | 16 - libraries/logic/net/URLConstants.h | 40 - libraries/logic/news/NewsChecker.cpp | 135 - libraries/logic/news/NewsChecker.h | 107 - libraries/logic/news/NewsEntry.cpp | 77 - libraries/logic/news/NewsEntry.h | 65 - .../logic/notifications/NotificationChecker.cpp | 130 - .../logic/notifications/NotificationChecker.h | 63 - libraries/logic/pathmatcher/FSTreeMatcher.h | 21 - libraries/logic/pathmatcher/IPathMatcher.h | 12 - libraries/logic/pathmatcher/MultiMatcher.h | 31 - libraries/logic/pathmatcher/RegexpMatcher.h | 42 - libraries/logic/resources/Resource.cpp | 155 - libraries/logic/resources/Resource.h | 132 - libraries/logic/resources/ResourceHandler.cpp | 28 - libraries/logic/resources/ResourceHandler.h | 36 - libraries/logic/resources/ResourceObserver.cpp | 55 - libraries/logic/resources/ResourceObserver.h | 73 - libraries/logic/resources/ResourceProxyModel.cpp | 89 - libraries/logic/resources/ResourceProxyModel.h | 39 - libraries/logic/screenshots/ImgurAlbumCreation.cpp | 90 - libraries/logic/screenshots/ImgurAlbumCreation.h | 44 - libraries/logic/screenshots/ImgurUpload.cpp | 114 - libraries/logic/screenshots/ImgurUpload.h | 33 - libraries/logic/screenshots/Screenshot.h | 19 - libraries/logic/settings/INIFile.cpp | 151 - libraries/logic/settings/INIFile.h | 38 - libraries/logic/settings/INISettingsObject.cpp | 107 - libraries/logic/settings/INISettingsObject.h | 66 - libraries/logic/settings/OverrideSetting.cpp | 54 - libraries/logic/settings/OverrideSetting.h | 46 - libraries/logic/settings/PassthroughSetting.cpp | 66 - libraries/logic/settings/PassthroughSetting.h | 45 - libraries/logic/settings/Setting.cpp | 53 - libraries/logic/settings/Setting.h | 119 - libraries/logic/settings/SettingsObject.cpp | 142 - libraries/logic/settings/SettingsObject.h | 214 - libraries/logic/status/StatusChecker.cpp | 153 - libraries/logic/status/StatusChecker.h | 60 - libraries/logic/tasks/SequentialTask.cpp | 55 - libraries/logic/tasks/SequentialTask.h | 31 - libraries/logic/tasks/Task.cpp | 88 - libraries/logic/tasks/Task.h | 96 - libraries/logic/tasks/ThreadTask.cpp | 41 - libraries/logic/tasks/ThreadTask.h | 25 - libraries/logic/tools/BaseExternalTool.cpp | 41 - libraries/logic/tools/BaseExternalTool.h | 60 - libraries/logic/tools/BaseProfiler.cpp | 35 - libraries/logic/tools/BaseProfiler.h | 38 - libraries/logic/tools/JProfiler.cpp | 116 - libraries/logic/tools/JProfiler.h | 15 - libraries/logic/tools/JVisualVM.cpp | 103 - libraries/logic/tools/JVisualVM.h | 15 - libraries/logic/tools/MCEditTool.cpp | 124 - libraries/logic/tools/MCEditTool.h | 26 - libraries/logic/trans/TranslationDownloader.cpp | 53 - libraries/logic/trans/TranslationDownloader.h | 32 - libraries/logic/updater/DownloadTask.cpp | 169 - libraries/logic/updater/DownloadTask.h | 95 - libraries/logic/updater/GoUpdate.cpp | 216 - libraries/logic/updater/GoUpdate.h | 133 - libraries/logic/updater/UpdateChecker.cpp | 269 -- libraries/logic/updater/UpdateChecker.h | 121 - libraries/logic/wonko/BaseWonkoEntity.cpp | 39 - libraries/logic/wonko/BaseWonkoEntity.h | 51 - libraries/logic/wonko/WonkoIndex.cpp | 147 - libraries/logic/wonko/WonkoIndex.h | 68 - libraries/logic/wonko/WonkoReference.cpp | 44 - libraries/logic/wonko/WonkoReference.h | 41 - libraries/logic/wonko/WonkoUtil.cpp | 47 - libraries/logic/wonko/WonkoUtil.h | 31 - libraries/logic/wonko/WonkoVersion.cpp | 102 - libraries/logic/wonko/WonkoVersion.h | 83 - libraries/logic/wonko/WonkoVersionList.cpp | 283 -- libraries/logic/wonko/WonkoVersionList.h | 92 - libraries/logic/wonko/format/WonkoFormat.cpp | 80 - libraries/logic/wonko/format/WonkoFormat.h | 54 - libraries/logic/wonko/format/WonkoFormatV1.cpp | 156 - libraries/logic/wonko/format/WonkoFormatV1.h | 30 - .../wonko/tasks/BaseWonkoEntityLocalLoadTask.cpp | 117 - .../wonko/tasks/BaseWonkoEntityLocalLoadTask.h | 81 - .../wonko/tasks/BaseWonkoEntityRemoteLoadTask.cpp | 126 - .../wonko/tasks/BaseWonkoEntityRemoteLoadTask.h | 85 - libraries/pack200/CMakeLists.txt | 39 + libraries/pack200/LICENSE | 347 ++ libraries/pack200/anti200.cpp | 43 + libraries/pack200/include/unpack200.h | 36 + libraries/pack200/src/bands.cpp | 423 ++ libraries/pack200/src/bands.h | 489 ++ libraries/pack200/src/bytes.cpp | 217 + libraries/pack200/src/bytes.h | 286 ++ libraries/pack200/src/coding.cpp | 1044 +++++ libraries/pack200/src/coding.h | 247 + libraries/pack200/src/constants.h | 442 ++ libraries/pack200/src/defines.h | 65 + libraries/pack200/src/unpack.cpp | 4793 ++++++++++++++++++++ libraries/pack200/src/unpack.h | 547 +++ libraries/pack200/src/unpack200.cpp | 162 + libraries/pack200/src/utils.cpp | 71 + libraries/pack200/src/utils.h | 53 + libraries/pack200/src/zip.cpp | 589 +++ libraries/pack200/src/zip.h | 110 + libraries/rainbow/CMakeLists.txt | 15 + libraries/rainbow/COPYING.LIB | 0 libraries/rainbow/include/rainbow.h | 160 + libraries/rainbow/include/rainbow_config.h | 26 + libraries/rainbow/src/rainbow.cpp | 365 ++ libraries/xz-embedded/CMakeLists.txt | 26 + libraries/xz-embedded/include/xz.h | 321 ++ libraries/xz-embedded/src/xz_config.h | 119 + libraries/xz-embedded/src/xz_crc32.c | 61 + libraries/xz-embedded/src/xz_crc64.c | 52 + libraries/xz-embedded/src/xz_dec_bcj.c | 588 +++ libraries/xz-embedded/src/xz_dec_lzma2.c | 1231 +++++ libraries/xz-embedded/src/xz_dec_stream.c | 860 ++++ libraries/xz-embedded/src/xz_lzma2.h | 204 + libraries/xz-embedded/src/xz_private.h | 150 + libraries/xz-embedded/src/xz_stream.h | 62 + libraries/xz-embedded/xzminidec.c | 144 + 364 files changed, 25004 insertions(+), 33688 deletions(-) create mode 100644 libraries/README.md create mode 100644 libraries/classparser/CMakeLists.txt create mode 100644 libraries/classparser/include/classparser_config.h create mode 100644 libraries/classparser/include/javautils.h create mode 100644 libraries/classparser/src/annotations.cpp create mode 100644 libraries/classparser/src/annotations.h create mode 100644 libraries/classparser/src/classfile.h create mode 100644 libraries/classparser/src/constants.h create mode 100644 libraries/classparser/src/errors.h create mode 100644 libraries/classparser/src/javaendian.h create mode 100644 libraries/classparser/src/javautils.cpp create mode 100644 libraries/classparser/src/membuffer.h delete mode 100644 libraries/gui/CMakeLists.txt delete mode 100644 libraries/gui/DesktopServices.cpp delete mode 100644 libraries/gui/DesktopServices.h delete mode 100644 libraries/gui/SkinUtils.cpp delete mode 100644 libraries/gui/SkinUtils.h delete mode 100644 libraries/gui/icons/IconList.cpp delete mode 100644 libraries/gui/icons/IconList.h delete mode 100644 libraries/gui/icons/MMCIcon.cpp delete mode 100644 libraries/gui/icons/MMCIcon.h create mode 100644 libraries/hoedown/CMakeLists.txt create mode 100644 libraries/hoedown/LICENSE create mode 100644 libraries/hoedown/README.md create mode 100644 libraries/hoedown/include/hoedown/autolink.h create mode 100644 libraries/hoedown/include/hoedown/buffer.h create mode 100644 libraries/hoedown/include/hoedown/document.h create mode 100644 libraries/hoedown/include/hoedown/escape.h create mode 100644 libraries/hoedown/include/hoedown/html.h create mode 100644 libraries/hoedown/include/hoedown/stack.h create mode 100644 libraries/hoedown/include/hoedown/version.h create mode 100644 libraries/hoedown/src/autolink.c create mode 100644 libraries/hoedown/src/buffer.c create mode 100644 libraries/hoedown/src/document.c create mode 100644 libraries/hoedown/src/escape.c create mode 100644 libraries/hoedown/src/html.c create mode 100644 libraries/hoedown/src/html_blocks.c create mode 100644 libraries/hoedown/src/html_smartypants.c create mode 100644 libraries/hoedown/src/stack.c create mode 100644 libraries/hoedown/src/version.c create mode 100644 libraries/iconfix/CMakeLists.txt create mode 100644 libraries/iconfix/internal/qhexstring_p.h create mode 100644 libraries/iconfix/internal/qiconloader.cpp create mode 100644 libraries/iconfix/internal/qiconloader_p.h create mode 100644 libraries/iconfix/xdgicon.cpp create mode 100644 libraries/iconfix/xdgicon.h create mode 100644 libraries/javacheck/.gitignore create mode 100644 libraries/javacheck/CMakeLists.txt create mode 100644 libraries/javacheck/JavaCheck.java create mode 100644 libraries/launcher/.gitignore create mode 100644 libraries/launcher/CMakeLists.txt create mode 100644 libraries/launcher/net/minecraft/Launcher.java create mode 100644 libraries/launcher/org/multimc/EntryPoint.java create mode 100644 libraries/launcher/org/multimc/IconLoader.java create mode 100644 libraries/launcher/org/multimc/Launcher.java create mode 100644 libraries/launcher/org/multimc/LegacyFrame.java create mode 100644 libraries/launcher/org/multimc/NotFoundException.java create mode 100644 libraries/launcher/org/multimc/ParamBucket.java create mode 100644 libraries/launcher/org/multimc/ParseException.java create mode 100644 libraries/launcher/org/multimc/Utils.java create mode 100644 libraries/launcher/org/multimc/legacy/LegacyLauncher.java create mode 100644 libraries/launcher/org/multimc/onesix/OneSixLauncher.java create mode 100644 libraries/launcher/org/simplericity/macify/eawt/Application.java create mode 100644 libraries/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java create mode 100644 libraries/launcher/org/simplericity/macify/eawt/ApplicationEvent.java create mode 100644 libraries/launcher/org/simplericity/macify/eawt/ApplicationListener.java create mode 100644 libraries/launcher/org/simplericity/macify/eawt/DefaultApplication.java create mode 160000 libraries/libnbtplusplus delete mode 100644 libraries/logic/AbstractCommonModel.cpp delete mode 100644 libraries/logic/AbstractCommonModel.h delete mode 100644 libraries/logic/BaseConfigObject.cpp delete mode 100644 libraries/logic/BaseConfigObject.h delete mode 100644 libraries/logic/BaseInstaller.cpp delete mode 100644 libraries/logic/BaseInstaller.h delete mode 100644 libraries/logic/BaseInstance.cpp delete mode 100644 libraries/logic/BaseInstance.h delete mode 100644 libraries/logic/BaseVersion.h delete mode 100644 libraries/logic/BaseVersionList.cpp delete mode 100644 libraries/logic/BaseVersionList.h delete mode 100644 libraries/logic/CMakeLists.txt delete mode 100644 libraries/logic/Commandline.cpp delete mode 100644 libraries/logic/Commandline.h delete mode 100644 libraries/logic/DefaultVariable.h delete mode 100644 libraries/logic/Env.cpp delete mode 100644 libraries/logic/Env.h delete mode 100644 libraries/logic/Exception.h delete mode 100644 libraries/logic/FileSystem.cpp delete mode 100644 libraries/logic/FileSystem.h delete mode 100644 libraries/logic/GZip.cpp delete mode 100644 libraries/logic/GZip.h delete mode 100644 libraries/logic/InstanceList.cpp delete mode 100644 libraries/logic/InstanceList.h delete mode 100644 libraries/logic/Json.cpp delete mode 100644 libraries/logic/Json.h delete mode 100644 libraries/logic/MMCStrings.cpp delete mode 100644 libraries/logic/MMCStrings.h delete mode 100644 libraries/logic/MMCZip.cpp delete mode 100644 libraries/logic/MMCZip.h delete mode 100644 libraries/logic/NullInstance.h delete mode 100644 libraries/logic/QObjectPtr.h delete mode 100644 libraries/logic/RWStorage.h delete mode 100644 libraries/logic/RecursiveFileSystemWatcher.cpp delete mode 100644 libraries/logic/RecursiveFileSystemWatcher.h delete mode 100644 libraries/logic/SeparatorPrefixTree.h delete mode 100644 libraries/logic/TypeMagic.h delete mode 100644 libraries/logic/Version.cpp delete mode 100644 libraries/logic/Version.h delete mode 100644 libraries/logic/java/JavaChecker.cpp delete mode 100644 libraries/logic/java/JavaChecker.h delete mode 100644 libraries/logic/java/JavaCheckerJob.cpp delete mode 100644 libraries/logic/java/JavaCheckerJob.h delete mode 100644 libraries/logic/java/JavaInstall.cpp delete mode 100644 libraries/logic/java/JavaInstall.h delete mode 100644 libraries/logic/java/JavaInstallList.cpp delete mode 100644 libraries/logic/java/JavaInstallList.h delete mode 100644 libraries/logic/java/JavaUtils.cpp delete mode 100644 libraries/logic/java/JavaUtils.h delete mode 100644 libraries/logic/java/JavaVersion.cpp delete mode 100644 libraries/logic/java/JavaVersion.h delete mode 100644 libraries/logic/launch/LaunchStep.cpp delete mode 100644 libraries/logic/launch/LaunchStep.h delete mode 100644 libraries/logic/launch/LaunchTask.cpp delete mode 100644 libraries/logic/launch/LaunchTask.h delete mode 100644 libraries/logic/launch/LoggedProcess.cpp delete mode 100644 libraries/logic/launch/LoggedProcess.h delete mode 100644 libraries/logic/launch/MessageLevel.cpp delete mode 100644 libraries/logic/launch/MessageLevel.h delete mode 100644 libraries/logic/launch/steps/CheckJava.cpp delete mode 100644 libraries/logic/launch/steps/CheckJava.h delete mode 100644 libraries/logic/launch/steps/LaunchMinecraft.cpp delete mode 100644 libraries/logic/launch/steps/LaunchMinecraft.h delete mode 100644 libraries/logic/launch/steps/ModMinecraftJar.cpp delete mode 100644 libraries/logic/launch/steps/ModMinecraftJar.h delete mode 100644 libraries/logic/launch/steps/PostLaunchCommand.cpp delete mode 100644 libraries/logic/launch/steps/PostLaunchCommand.h delete mode 100644 libraries/logic/launch/steps/PreLaunchCommand.cpp delete mode 100644 libraries/logic/launch/steps/PreLaunchCommand.h delete mode 100644 libraries/logic/launch/steps/TextPrint.cpp delete mode 100644 libraries/logic/launch/steps/TextPrint.h delete mode 100644 libraries/logic/launch/steps/Update.cpp delete mode 100644 libraries/logic/launch/steps/Update.h delete mode 100644 libraries/logic/minecraft/AssetsUtils.cpp delete mode 100644 libraries/logic/minecraft/AssetsUtils.h delete mode 100644 libraries/logic/minecraft/GradleSpecifier.h delete mode 100644 libraries/logic/minecraft/JarMod.h delete mode 100644 libraries/logic/minecraft/Library.cpp delete mode 100644 libraries/logic/minecraft/Library.h delete mode 100644 libraries/logic/minecraft/MinecraftInstance.cpp delete mode 100644 libraries/logic/minecraft/MinecraftInstance.h delete mode 100644 libraries/logic/minecraft/MinecraftProfile.cpp delete mode 100644 libraries/logic/minecraft/MinecraftProfile.h delete mode 100644 libraries/logic/minecraft/MinecraftVersion.cpp delete mode 100644 libraries/logic/minecraft/MinecraftVersion.h delete mode 100644 libraries/logic/minecraft/MinecraftVersionList.cpp delete mode 100644 libraries/logic/minecraft/MinecraftVersionList.h delete mode 100644 libraries/logic/minecraft/Mod.cpp delete mode 100644 libraries/logic/minecraft/Mod.h delete mode 100644 libraries/logic/minecraft/ModList.cpp delete mode 100644 libraries/logic/minecraft/ModList.h delete mode 100644 libraries/logic/minecraft/MojangDownloadInfo.h delete mode 100644 libraries/logic/minecraft/MojangVersionFormat.cpp delete mode 100644 libraries/logic/minecraft/MojangVersionFormat.h delete mode 100644 libraries/logic/minecraft/OpSys.cpp delete mode 100644 libraries/logic/minecraft/OpSys.h delete mode 100644 libraries/logic/minecraft/ParseUtils.cpp delete mode 100644 libraries/logic/minecraft/ParseUtils.h delete mode 100644 libraries/logic/minecraft/ProfilePatch.h delete mode 100644 libraries/logic/minecraft/ProfileStrategy.h delete mode 100644 libraries/logic/minecraft/ProfileUtils.cpp delete mode 100644 libraries/logic/minecraft/ProfileUtils.h delete mode 100644 libraries/logic/minecraft/Rule.cpp delete mode 100644 libraries/logic/minecraft/Rule.h delete mode 100644 libraries/logic/minecraft/VersionBuildError.h delete mode 100644 libraries/logic/minecraft/VersionFile.cpp delete mode 100644 libraries/logic/minecraft/VersionFile.h delete mode 100644 libraries/logic/minecraft/VersionFilterData.cpp delete mode 100644 libraries/logic/minecraft/VersionFilterData.h delete mode 100644 libraries/logic/minecraft/World.cpp delete mode 100644 libraries/logic/minecraft/World.h delete mode 100644 libraries/logic/minecraft/WorldList.cpp delete mode 100644 libraries/logic/minecraft/WorldList.h delete mode 100644 libraries/logic/minecraft/auth/AuthSession.cpp delete mode 100644 libraries/logic/minecraft/auth/AuthSession.h delete mode 100644 libraries/logic/minecraft/auth/MojangAccount.cpp delete mode 100644 libraries/logic/minecraft/auth/MojangAccount.h delete mode 100644 libraries/logic/minecraft/auth/MojangAccountList.cpp delete mode 100644 libraries/logic/minecraft/auth/MojangAccountList.h delete mode 100644 libraries/logic/minecraft/auth/YggdrasilTask.cpp delete mode 100644 libraries/logic/minecraft/auth/YggdrasilTask.h delete mode 100644 libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp delete mode 100644 libraries/logic/minecraft/auth/flows/AuthenticateTask.h delete mode 100644 libraries/logic/minecraft/auth/flows/RefreshTask.cpp delete mode 100644 libraries/logic/minecraft/auth/flows/RefreshTask.h delete mode 100644 libraries/logic/minecraft/auth/flows/ValidateTask.cpp delete mode 100644 libraries/logic/minecraft/auth/flows/ValidateTask.h delete mode 100644 libraries/logic/minecraft/forge/ForgeInstaller.cpp delete mode 100644 libraries/logic/minecraft/forge/ForgeInstaller.h delete mode 100644 libraries/logic/minecraft/forge/ForgeVersion.cpp delete mode 100644 libraries/logic/minecraft/forge/ForgeVersion.h delete mode 100644 libraries/logic/minecraft/forge/ForgeVersionList.cpp delete mode 100644 libraries/logic/minecraft/forge/ForgeVersionList.h delete mode 100644 libraries/logic/minecraft/forge/ForgeXzDownload.cpp delete mode 100644 libraries/logic/minecraft/forge/ForgeXzDownload.h delete mode 100644 libraries/logic/minecraft/forge/LegacyForge.cpp delete mode 100644 libraries/logic/minecraft/forge/LegacyForge.h delete mode 100644 libraries/logic/minecraft/ftb/FTBPlugin.cpp delete mode 100644 libraries/logic/minecraft/ftb/FTBPlugin.h delete mode 100644 libraries/logic/minecraft/ftb/FTBProfileStrategy.cpp delete mode 100644 libraries/logic/minecraft/ftb/FTBProfileStrategy.h delete mode 100644 libraries/logic/minecraft/ftb/FTBVersion.h delete mode 100644 libraries/logic/minecraft/ftb/LegacyFTBInstance.cpp delete mode 100644 libraries/logic/minecraft/ftb/LegacyFTBInstance.h delete mode 100644 libraries/logic/minecraft/ftb/OneSixFTBInstance.cpp delete mode 100644 libraries/logic/minecraft/ftb/OneSixFTBInstance.h delete mode 100644 libraries/logic/minecraft/legacy/LegacyInstance.cpp delete mode 100644 libraries/logic/minecraft/legacy/LegacyInstance.h delete mode 100644 libraries/logic/minecraft/legacy/LegacyUpdate.cpp delete mode 100644 libraries/logic/minecraft/legacy/LegacyUpdate.h delete mode 100644 libraries/logic/minecraft/legacy/LwjglVersionList.cpp delete mode 100644 libraries/logic/minecraft/legacy/LwjglVersionList.h delete mode 100644 libraries/logic/minecraft/liteloader/LiteLoaderInstaller.cpp delete mode 100644 libraries/logic/minecraft/liteloader/LiteLoaderInstaller.h delete mode 100644 libraries/logic/minecraft/liteloader/LiteLoaderVersionList.cpp delete mode 100644 libraries/logic/minecraft/liteloader/LiteLoaderVersionList.h delete mode 100644 libraries/logic/minecraft/onesix/OneSixInstance.cpp delete mode 100644 libraries/logic/minecraft/onesix/OneSixInstance.h delete mode 100644 libraries/logic/minecraft/onesix/OneSixProfileStrategy.cpp delete mode 100644 libraries/logic/minecraft/onesix/OneSixProfileStrategy.h delete mode 100644 libraries/logic/minecraft/onesix/OneSixUpdate.cpp delete mode 100644 libraries/logic/minecraft/onesix/OneSixUpdate.h delete mode 100644 libraries/logic/minecraft/onesix/OneSixVersionFormat.cpp delete mode 100644 libraries/logic/minecraft/onesix/OneSixVersionFormat.h delete mode 100644 libraries/logic/net/ByteArrayDownload.cpp delete mode 100644 libraries/logic/net/ByteArrayDownload.h delete mode 100644 libraries/logic/net/CacheDownload.cpp delete mode 100644 libraries/logic/net/CacheDownload.h delete mode 100644 libraries/logic/net/HttpMetaCache.cpp delete mode 100644 libraries/logic/net/HttpMetaCache.h delete mode 100644 libraries/logic/net/MD5EtagDownload.cpp delete mode 100644 libraries/logic/net/MD5EtagDownload.h delete mode 100644 libraries/logic/net/NetAction.h delete mode 100644 libraries/logic/net/NetJob.cpp delete mode 100644 libraries/logic/net/NetJob.h delete mode 100644 libraries/logic/net/PasteUpload.cpp delete mode 100644 libraries/logic/net/PasteUpload.h delete mode 100644 libraries/logic/net/URLConstants.cpp delete mode 100644 libraries/logic/net/URLConstants.h delete mode 100644 libraries/logic/news/NewsChecker.cpp delete mode 100644 libraries/logic/news/NewsChecker.h delete mode 100644 libraries/logic/news/NewsEntry.cpp delete mode 100644 libraries/logic/news/NewsEntry.h delete mode 100644 libraries/logic/notifications/NotificationChecker.cpp delete mode 100644 libraries/logic/notifications/NotificationChecker.h delete mode 100644 libraries/logic/pathmatcher/FSTreeMatcher.h delete mode 100644 libraries/logic/pathmatcher/IPathMatcher.h delete mode 100644 libraries/logic/pathmatcher/MultiMatcher.h delete mode 100644 libraries/logic/pathmatcher/RegexpMatcher.h delete mode 100644 libraries/logic/resources/Resource.cpp delete mode 100644 libraries/logic/resources/Resource.h delete mode 100644 libraries/logic/resources/ResourceHandler.cpp delete mode 100644 libraries/logic/resources/ResourceHandler.h delete mode 100644 libraries/logic/resources/ResourceObserver.cpp delete mode 100644 libraries/logic/resources/ResourceObserver.h delete mode 100644 libraries/logic/resources/ResourceProxyModel.cpp delete mode 100644 libraries/logic/resources/ResourceProxyModel.h delete mode 100644 libraries/logic/screenshots/ImgurAlbumCreation.cpp delete mode 100644 libraries/logic/screenshots/ImgurAlbumCreation.h delete mode 100644 libraries/logic/screenshots/ImgurUpload.cpp delete mode 100644 libraries/logic/screenshots/ImgurUpload.h delete mode 100644 libraries/logic/screenshots/Screenshot.h delete mode 100644 libraries/logic/settings/INIFile.cpp delete mode 100644 libraries/logic/settings/INIFile.h delete mode 100644 libraries/logic/settings/INISettingsObject.cpp delete mode 100644 libraries/logic/settings/INISettingsObject.h delete mode 100644 libraries/logic/settings/OverrideSetting.cpp delete mode 100644 libraries/logic/settings/OverrideSetting.h delete mode 100644 libraries/logic/settings/PassthroughSetting.cpp delete mode 100644 libraries/logic/settings/PassthroughSetting.h delete mode 100644 libraries/logic/settings/Setting.cpp delete mode 100644 libraries/logic/settings/Setting.h delete mode 100644 libraries/logic/settings/SettingsObject.cpp delete mode 100644 libraries/logic/settings/SettingsObject.h delete mode 100644 libraries/logic/status/StatusChecker.cpp delete mode 100644 libraries/logic/status/StatusChecker.h delete mode 100644 libraries/logic/tasks/SequentialTask.cpp delete mode 100644 libraries/logic/tasks/SequentialTask.h delete mode 100644 libraries/logic/tasks/Task.cpp delete mode 100644 libraries/logic/tasks/Task.h delete mode 100644 libraries/logic/tasks/ThreadTask.cpp delete mode 100644 libraries/logic/tasks/ThreadTask.h delete mode 100644 libraries/logic/tools/BaseExternalTool.cpp delete mode 100644 libraries/logic/tools/BaseExternalTool.h delete mode 100644 libraries/logic/tools/BaseProfiler.cpp delete mode 100644 libraries/logic/tools/BaseProfiler.h delete mode 100644 libraries/logic/tools/JProfiler.cpp delete mode 100644 libraries/logic/tools/JProfiler.h delete mode 100644 libraries/logic/tools/JVisualVM.cpp delete mode 100644 libraries/logic/tools/JVisualVM.h delete mode 100644 libraries/logic/tools/MCEditTool.cpp delete mode 100644 libraries/logic/tools/MCEditTool.h delete mode 100644 libraries/logic/trans/TranslationDownloader.cpp delete mode 100644 libraries/logic/trans/TranslationDownloader.h delete mode 100644 libraries/logic/updater/DownloadTask.cpp delete mode 100644 libraries/logic/updater/DownloadTask.h delete mode 100644 libraries/logic/updater/GoUpdate.cpp delete mode 100644 libraries/logic/updater/GoUpdate.h delete mode 100644 libraries/logic/updater/UpdateChecker.cpp delete mode 100644 libraries/logic/updater/UpdateChecker.h delete mode 100644 libraries/logic/wonko/BaseWonkoEntity.cpp delete mode 100644 libraries/logic/wonko/BaseWonkoEntity.h delete mode 100644 libraries/logic/wonko/WonkoIndex.cpp delete mode 100644 libraries/logic/wonko/WonkoIndex.h delete mode 100644 libraries/logic/wonko/WonkoReference.cpp delete mode 100644 libraries/logic/wonko/WonkoReference.h delete mode 100644 libraries/logic/wonko/WonkoUtil.cpp delete mode 100644 libraries/logic/wonko/WonkoUtil.h delete mode 100644 libraries/logic/wonko/WonkoVersion.cpp delete mode 100644 libraries/logic/wonko/WonkoVersion.h delete mode 100644 libraries/logic/wonko/WonkoVersionList.cpp delete mode 100644 libraries/logic/wonko/WonkoVersionList.h delete mode 100644 libraries/logic/wonko/format/WonkoFormat.cpp delete mode 100644 libraries/logic/wonko/format/WonkoFormat.h delete mode 100644 libraries/logic/wonko/format/WonkoFormatV1.cpp delete mode 100644 libraries/logic/wonko/format/WonkoFormatV1.h delete mode 100644 libraries/logic/wonko/tasks/BaseWonkoEntityLocalLoadTask.cpp delete mode 100644 libraries/logic/wonko/tasks/BaseWonkoEntityLocalLoadTask.h delete mode 100644 libraries/logic/wonko/tasks/BaseWonkoEntityRemoteLoadTask.cpp delete mode 100644 libraries/logic/wonko/tasks/BaseWonkoEntityRemoteLoadTask.h create mode 100644 libraries/pack200/CMakeLists.txt create mode 100644 libraries/pack200/LICENSE create mode 100644 libraries/pack200/anti200.cpp create mode 100644 libraries/pack200/include/unpack200.h create mode 100644 libraries/pack200/src/bands.cpp create mode 100644 libraries/pack200/src/bands.h create mode 100644 libraries/pack200/src/bytes.cpp create mode 100644 libraries/pack200/src/bytes.h create mode 100644 libraries/pack200/src/coding.cpp create mode 100644 libraries/pack200/src/coding.h create mode 100644 libraries/pack200/src/constants.h create mode 100644 libraries/pack200/src/defines.h create mode 100644 libraries/pack200/src/unpack.cpp create mode 100644 libraries/pack200/src/unpack.h create mode 100644 libraries/pack200/src/unpack200.cpp create mode 100644 libraries/pack200/src/utils.cpp create mode 100644 libraries/pack200/src/utils.h create mode 100644 libraries/pack200/src/zip.cpp create mode 100644 libraries/pack200/src/zip.h create mode 100644 libraries/rainbow/CMakeLists.txt create mode 100644 libraries/rainbow/COPYING.LIB create mode 100644 libraries/rainbow/include/rainbow.h create mode 100644 libraries/rainbow/include/rainbow_config.h create mode 100644 libraries/rainbow/src/rainbow.cpp create mode 100644 libraries/xz-embedded/CMakeLists.txt create mode 100644 libraries/xz-embedded/include/xz.h create mode 100644 libraries/xz-embedded/src/xz_config.h create mode 100644 libraries/xz-embedded/src/xz_crc32.c create mode 100644 libraries/xz-embedded/src/xz_crc64.c create mode 100644 libraries/xz-embedded/src/xz_dec_bcj.c create mode 100644 libraries/xz-embedded/src/xz_dec_lzma2.c create mode 100644 libraries/xz-embedded/src/xz_dec_stream.c create mode 100644 libraries/xz-embedded/src/xz_lzma2.h create mode 100644 libraries/xz-embedded/src/xz_private.h create mode 100644 libraries/xz-embedded/src/xz_stream.h create mode 100644 libraries/xz-embedded/xzminidec.c (limited to 'libraries') diff --git a/libraries/README.md b/libraries/README.md new file mode 100644 index 00000000..809acb16 --- /dev/null +++ b/libraries/README.md @@ -0,0 +1,145 @@ +# Third-party libraries + +This folder has third-party or otherwise external libraries needed for other parts to work. + +## classparser +A simplistic parser for Java class files. + +This library has served as a base for some (much more full-featured and advanced) work under NDA for AVG. It, however, should NOT be confused with that work. + +Copyright belongs to Petr Mrázek, unless explicitly stated otherwise in the source files. Available under the Apache 2.0 license. + +## hoedown +Hoedown is a revived fork of Sundown, the Markdown parser based on the original code of the Upskirt library by Natacha Porté. + +See [github repo](https://github.com/hoedown/hoedown). + +## iconfix +This was originally part of the razor-qt project and the Qt toolkit, respecitvely. Its sole purpose is to reimplement Qt's icon loading logic to prevent it from using any platform plugins that could break icon loading. + +Licensed under LGPL 2.1 + +## javacheck +Simple Java tool that prints the JVM details - version and platform bitness. + +Do what you want with it. It is so trivial that noone cares. + +## launcher +Java launcher part for Minecraft. + +It: +* Starts a process +* Waits for a launch script on stdin +* Consumes the launch script you feed it +* Proceeds with launch when it gets the `launcher` command + +This means the process is essentially idle until the final command is sent. You can, for example, attach a profiler before you send it. + +A `legacy` and `onesix` launchers are available. + +* `legacy` is intended for use with Minecraft versions < 1.6 and is deprecated. +* `onesix` can handle launching any Minecraft version, at the cost of some extra features `legacy` enables (custom window icon and title). + +Example (some parts have been censored): +``` +mod legacyjavafixer-1.0 +mainClass net.minecraft.launchwrapper.Launch +param --username +param CENSORED +param --version +param MultiMC5 +param --gameDir +param /home/peterix/minecraft/FTB/17ForgeTest/minecraft +param --assetsDir +param /home/peterix/minecraft/mmc5/assets +param --assetIndex +param 1.7.10 +param --uuid +param CENSORED +param --accessToken +param CENSORED +param --userProperties +param {} +param --userType +param mojang +param --tweakClass +param cpw.mods.fml.common.launcher.FMLTweaker +windowTitle MultiMC: 172ForgeTest +windowParams 854x480 +userName CENSORED +sessionId token:CENSORED:CENSORED +cp /home/peterix/minecraft/FTB/libraries/com/mojang/realms/1.3.5/realms-1.3.5.jar +cp /home/peterix/minecraft/FTB/libraries/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar +cp /home/peterix/minecraft/FTB/libraries/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar +cp /home/peterix/minecraft/FTB/libraries/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar +cp /home/peterix/minecraft/FTB/libraries/java3d/vecmath/1.3.1/vecmath-1.3.1.jar +cp /home/peterix/minecraft/FTB/libraries/net/sf/trove4j/trove4j/3.0.3/trove4j-3.0.3.jar +cp /home/peterix/minecraft/FTB/libraries/com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar +cp /home/peterix/minecraft/FTB/libraries/net/sf/jopt-simple/jopt-simple/4.5/jopt-simple-4.5.jar +cp /home/peterix/minecraft/FTB/libraries/com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar +cp /home/peterix/minecraft/FTB/libraries/com/paulscode/codecwav/20101023/codecwav-20101023.jar +cp /home/peterix/minecraft/FTB/libraries/com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar +cp /home/peterix/minecraft/FTB/libraries/com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar +cp /home/peterix/minecraft/FTB/libraries/com/paulscode/soundsystem/20120107/soundsystem-20120107.jar +cp /home/peterix/minecraft/FTB/libraries/io/netty/netty-all/4.0.10.Final/netty-all-4.0.10.Final.jar +cp /home/peterix/minecraft/FTB/libraries/com/google/guava/guava/16.0/guava-16.0.jar +cp /home/peterix/minecraft/FTB/libraries/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1.jar +cp /home/peterix/minecraft/FTB/libraries/commons-io/commons-io/2.4/commons-io-2.4.jar +cp /home/peterix/minecraft/FTB/libraries/commons-codec/commons-codec/1.9/commons-codec-1.9.jar +cp /home/peterix/minecraft/FTB/libraries/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar +cp /home/peterix/minecraft/FTB/libraries/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar +cp /home/peterix/minecraft/FTB/libraries/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar +cp /home/peterix/minecraft/FTB/libraries/com/mojang/authlib/1.5.16/authlib-1.5.16.jar +cp /home/peterix/minecraft/FTB/libraries/org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar +cp /home/peterix/minecraft/FTB/libraries/org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar +cp /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl/2.9.1/lwjgl-2.9.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl_util/2.9.1/lwjgl_util-2.9.1.jar +cp /home/peterix/minecraft/FTB/libraries/tv/twitch/twitch/5.16/twitch-5.16.jar +cp /home/peterix/minecraft/FTB/libraries/net/minecraftforge/forge/1.7.10-10.13.0.1178/forge-1.7.10-10.13.0.1178.jar +cp /home/peterix/minecraft/FTB/libraries/net/minecraft/launchwrapper/1.9/launchwrapper-1.9.jar +cp /home/peterix/minecraft/FTB/libraries/org/ow2/asm/asm-all/4.1/asm-all-4.1.jar +cp /home/peterix/minecraft/FTB/libraries/com/typesafe/akka/akka-actor_2.11/2.3.3/akka-actor_2.11-2.3.3.jar +cp /home/peterix/minecraft/FTB/libraries/com/typesafe/config/1.2.1/config-1.2.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-actors-migration_2.11/1.1.0/scala-actors-migration_2.11-1.1.0.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-compiler/2.11.1/scala-compiler-2.11.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/plugins/scala-continuations-library_2.11/1.0.2/scala-continuations-library_2.11-1.0.2.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/plugins/scala-continuations-plugin_2.11.1/1.0.2/scala-continuations-plugin_2.11.1-1.0.2.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-library/2.11.1/scala-library-2.11.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-parser-combinators_2.11/1.0.1/scala-parser-combinators_2.11-1.0.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-reflect/2.11.1/scala-reflect-2.11.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-swing_2.11/1.0.1/scala-swing_2.11-1.0.1.jar +cp /home/peterix/minecraft/FTB/libraries/org/scala-lang/scala-xml_2.11/1.0.2/scala-xml_2.11-1.0.2.jar +cp /home/peterix/minecraft/FTB/libraries/lzma/lzma/0.0.1/lzma-0.0.1.jar +ext /home/peterix/minecraft/FTB/libraries/org/lwjgl/lwjgl/lwjgl-platform/2.9.1/lwjgl-platform-2.9.1-natives-linux.jar +ext /home/peterix/minecraft/FTB/libraries/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar +natives /home/peterix/minecraft/FTB/17ForgeTest/natives +cp /home/peterix/minecraft/FTB/versions/1.7.10/1.7.10.jar +launcher onesix +``` + +Available under the Apache 2.0 license. + +## libnbtplusplus +libnbt++ is a free C++ library for Minecraft's file format Named Binary Tag (NBT). It can read and write compressed and uncompressed NBT files and provides a code interface for working with NBT data. + +See [github repo](https://github.com/ljfa-ag/libnbtplusplus). + +Available either under LGPL version 3 or later. + +## pack200 +Unpacks pack200 archives (squished, compression-optimized Java jars). This format is only used by Forge to save bandwidth. + +A horrible little thing extracted from the depths of the OpenJDK codebase. Please don't look at it, or you will praise Cthulhu for his clean code for the rest of your days. + +Available under GPL 2 with classpath exception. + +## rainbow +Color functions extracted from [KGuiAddons](https://inqlude.org/libraries/kguiaddons.html). Used for adaptive text coloring. + +Available either under LGPL version 2.1 or later. + +## xz-embedded +Tiny implementation of LZMA2 de/compression. This format is only used by Forge to save bandwidth. + +Public domain. \ No newline at end of file diff --git a/libraries/classparser/CMakeLists.txt b/libraries/classparser/CMakeLists.txt new file mode 100644 index 00000000..a6c3fa14 --- /dev/null +++ b/libraries/classparser/CMakeLists.txt @@ -0,0 +1,41 @@ +project(classparser) + +set(CMAKE_AUTOMOC ON) + +######## Check endianness ######## +include(TestBigEndian) +test_big_endian(BIGENDIAN) +if(${BIGENDIAN}) + add_definitions(-DMULTIMC_BIG_ENDIAN) +endif(${BIGENDIAN}) + +# Find Qt +find_package(Qt5Core REQUIRED) + +# Include Qt headers. +include_directories(${Qt5Base_INCLUDE_DIRS}) + +set(CLASSPARSER_HEADERS +# Public headers +include/classparser_config.h +include/javautils.h + +# Private headers +src/annotations.h +src/classfile.h +src/constants.h +src/errors.h +src/javaendian.h +src/membuffer.h +) + +set(CLASSPARSER_SOURCES +src/javautils.cpp +src/annotations.cpp +) + +add_definitions(-DCLASSPARSER_LIBRARY) + +add_library(classparser SHARED ${CLASSPARSER_SOURCES} ${CLASSPARSER_HEADERS}) +target_include_directories(classparser PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") +qt5_use_modules(classparser Core) diff --git a/libraries/classparser/include/classparser_config.h b/libraries/classparser/include/classparser_config.h new file mode 100644 index 00000000..bfd4dcb4 --- /dev/null +++ b/libraries/classparser/include/classparser_config.h @@ -0,0 +1,22 @@ +/* Copyright 2013-2015 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. + */ + +#include + +#ifdef CLASSPARSER_LIBRARY +#define CLASSPARSER_EXPORT Q_DECL_EXPORT +#else +#define CLASSPARSER_EXPORT Q_DECL_IMPORT +#endif diff --git a/libraries/classparser/include/javautils.h b/libraries/classparser/include/javautils.h new file mode 100644 index 00000000..43cca4bc --- /dev/null +++ b/libraries/classparser/include/javautils.h @@ -0,0 +1,29 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Authors: Orochimarufan + * + * 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. + */ +#pragma once +#include +#include "classparser_config.h" + +#define MCVer_Unknown "Unknown" + +namespace javautils +{ +/** + * @brief Get the version from a minecraft.jar by parsing its class files. Expensive! + */ +QString GetMinecraftJarVersion(QString jar); +} diff --git a/libraries/classparser/src/annotations.cpp b/libraries/classparser/src/annotations.cpp new file mode 100644 index 00000000..d1a7c046 --- /dev/null +++ b/libraries/classparser/src/annotations.cpp @@ -0,0 +1,85 @@ +#include "classfile.h" +#include "annotations.h" +#include + +namespace java +{ +std::string annotation::toString() +{ + std::ostringstream ss; + ss << "Annotation type : " << type_index << " - " << pool[type_index].str_data << std::endl; + ss << "Contains " << name_val_pairs.size() << " pairs:" << std::endl; + for (unsigned i = 0; i < name_val_pairs.size(); i++) + { + std::pair &val = name_val_pairs[i]; + auto name_idx = val.first; + ss << pool[name_idx].str_data << "(" << name_idx << ")" + << " = " << val.second->toString() << std::endl; + } + return ss.str(); +} + +annotation *annotation::read(util::membuffer &input, constant_pool &pool) +{ + uint16_t type_index = 0; + input.read_be(type_index); + annotation *ann = new annotation(type_index, pool); + + uint16_t num_pairs = 0; + input.read_be(num_pairs); + while (num_pairs) + { + uint16_t name_idx = 0; + // read name index + input.read_be(name_idx); + auto elem = element_value::readElementValue(input, pool); + // read value + ann->add_pair(name_idx, elem); + num_pairs--; + } + return ann; +} + +element_value *element_value::readElementValue(util::membuffer &input, + java::constant_pool &pool) +{ + element_value_type type = INVALID; + input.read(type); + uint16_t index = 0; + uint16_t index2 = 0; + std::vector vals; + switch (type) + { + case PRIMITIVE_BYTE: + case PRIMITIVE_CHAR: + case PRIMITIVE_DOUBLE: + case PRIMITIVE_FLOAT: + case PRIMITIVE_INT: + case PRIMITIVE_LONG: + case PRIMITIVE_SHORT: + case PRIMITIVE_BOOLEAN: + case STRING: + input.read_be(index); + return new element_value_simple(type, index, pool); + case ENUM_CONSTANT: + input.read_be(index); + input.read_be(index2); + return new element_value_enum(type, index, index2, pool); + case CLASS: // Class + input.read_be(index); + return new element_value_class(type, index, pool); + case ANNOTATION: // Annotation + // FIXME: runtime visibility info needs to be passed from parent + return new element_value_annotation(ANNOTATION, annotation::read(input, pool), pool); + case ARRAY: // Array + input.read_be(index); + for (int i = 0; i < index; i++) + { + vals.push_back(element_value::readElementValue(input, pool)); + } + return new element_value_array(ARRAY, vals, pool); + default: + throw new java::classfile_exception(); + } +} +} \ No newline at end of file diff --git a/libraries/classparser/src/annotations.h b/libraries/classparser/src/annotations.h new file mode 100644 index 00000000..aa25d241 --- /dev/null +++ b/libraries/classparser/src/annotations.h @@ -0,0 +1,277 @@ +#pragma once +#include "classfile.h" +#include +#include + +namespace java +{ +enum element_value_type : uint8_t +{ + INVALID = 0, + STRING = 's', + ENUM_CONSTANT = 'e', + CLASS = 'c', + ANNOTATION = '@', + ARRAY = '[', // one array dimension + PRIMITIVE_INT = 'I', // integer + PRIMITIVE_BYTE = 'B', // signed byte + PRIMITIVE_CHAR = 'C', // Unicode character code point in the Basic Multilingual Plane, + // encoded with UTF-16 + PRIMITIVE_DOUBLE = 'D', // double-precision floating-point value + PRIMITIVE_FLOAT = 'F', // single-precision floating-point value + PRIMITIVE_LONG = 'J', // long integer + PRIMITIVE_SHORT = 'S', // signed short + PRIMITIVE_BOOLEAN = 'Z' // true or false +}; +/** + * The element_value structure is a discriminated union representing the value of an + *element-value pair. + * It is used to represent element values in all attributes that describe annotations + * - RuntimeVisibleAnnotations + * - RuntimeInvisibleAnnotations + * - RuntimeVisibleParameterAnnotations + * - RuntimeInvisibleParameterAnnotations). + * + * The element_value structure has the following format: + */ +class element_value +{ +protected: + element_value_type type; + constant_pool &pool; + +public: + element_value(element_value_type type, constant_pool &pool) : type(type), pool(pool) {}; + + element_value_type getElementValueType() + { + return type; + } + + virtual std::string toString() = 0; + + static element_value *readElementValue(util::membuffer &input, constant_pool &pool); +}; + +/** + * Each value of the annotations table represents a single runtime-visible annotation on a + * program element. + * The annotation structure has the following format: + */ +class annotation +{ +public: + typedef std::vector> value_list; + +protected: + /** + * The value of the type_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing a field descriptor representing the annotation type corresponding + * to the annotation represented by this annotation structure. + */ + uint16_t type_index; + /** + * map between element_name_index and value. + * + * The value of the element_name_index item must be a valid index into the constant_pool + *table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + *representing + * a valid field descriptor (§4.3.2) that denotes the name of the annotation type element + *represented + * by this element_value_pairs entry. + */ + value_list name_val_pairs; + /** + * Reference to the parent constant pool + */ + constant_pool &pool; + +public: + annotation(uint16_t type_index, constant_pool &pool) + : type_index(type_index), pool(pool) {}; + ~annotation() + { + for (unsigned i = 0; i < name_val_pairs.size(); i++) + { + delete name_val_pairs[i].second; + } + } + void add_pair(uint16_t key, element_value *value) + { + name_val_pairs.push_back(std::make_pair(key, value)); + } + ; + value_list::const_iterator begin() + { + return name_val_pairs.cbegin(); + } + value_list::const_iterator end() + { + return name_val_pairs.cend(); + } + std::string toString(); + static annotation *read(util::membuffer &input, constant_pool &pool); +}; +typedef std::vector annotation_table; + +/// type for simple value annotation elements +class element_value_simple : public element_value +{ +protected: + /// index of the constant in the constant pool + uint16_t index; + +public: + element_value_simple(element_value_type type, uint16_t index, constant_pool &pool) + : element_value(type, pool), index(index) { + // TODO: verify consistency + }; + uint16_t getIndex() + { + return index; + } + virtual std::string toString() + { + return pool[index].toString(); + } + ; +}; +/// The enum_const_value item is used if the tag item is 'e'. +class element_value_enum : public element_value +{ +protected: + /** + * The value of the type_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing a valid field descriptor (§4.3.2) that denotes the internal form of the + * binary + * name (§4.2.1) of the type of the enum constant represented by this element_value + * structure. + */ + uint16_t typeIndex; + /** + * The value of the const_name_index item must be a valid index into the constant_pool + * table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing the simple name of the enum constant represented by this element_value + * structure. + */ + uint16_t valueIndex; + +public: + element_value_enum(element_value_type type, uint16_t typeIndex, uint16_t valueIndex, + constant_pool &pool) + : element_value(type, pool), typeIndex(typeIndex), valueIndex(valueIndex) + { + // TODO: verify consistency + } + uint16_t getValueIndex() + { + return valueIndex; + } + uint16_t getTypeIndex() + { + return typeIndex; + } + virtual std::string toString() + { + return "enum value"; + } + ; +}; + +class element_value_class : public element_value +{ +protected: + /** + * The class_info_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing the return descriptor (§4.3.3) of the type that is reified by the class + * represented by this element_value structure. + * + * For example, 'V' for Void.class, 'Ljava/lang/Object;' for Object, etc. + * + * Or in plain english, you can store type information in annotations. Yay. + */ + uint16_t classIndex; + +public: + element_value_class(element_value_type type, uint16_t classIndex, constant_pool &pool) + : element_value(type, pool), classIndex(classIndex) + { + // TODO: verify consistency + } + uint16_t getIndex() + { + return classIndex; + } + virtual std::string toString() + { + return "class"; + } + ; +}; + +/// nested annotations... yay +class element_value_annotation : public element_value +{ +private: + annotation *nestedAnnotation; + +public: + element_value_annotation(element_value_type type, annotation *nestedAnnotation, + constant_pool &pool) + : element_value(type, pool), nestedAnnotation(nestedAnnotation) {}; + ~element_value_annotation() + { + if (nestedAnnotation) + { + delete nestedAnnotation; + nestedAnnotation = nullptr; + } + } + virtual std::string toString() + { + return "nested annotation"; + } + ; +}; + +/// and arrays! +class element_value_array : public element_value +{ +public: + typedef std::vector elem_vec; + +protected: + elem_vec values; + +public: + element_value_array(element_value_type type, std::vector &values, + constant_pool &pool) + : element_value(type, pool), values(values) {}; + ~element_value_array() + { + for (unsigned i = 0; i < values.size(); i++) + { + delete values[i]; + } + } + ; + elem_vec::const_iterator begin() + { + return values.cbegin(); + } + elem_vec::const_iterator end() + { + return values.cend(); + } + virtual std::string toString() + { + return "array"; + } + ; +}; +} \ No newline at end of file diff --git a/libraries/classparser/src/classfile.h b/libraries/classparser/src/classfile.h new file mode 100644 index 00000000..a5e7ee50 --- /dev/null +++ b/libraries/classparser/src/classfile.h @@ -0,0 +1,156 @@ +#pragma once +#include "membuffer.h" +#include "constants.h" +#include "annotations.h" +#include +namespace java +{ +/** + * Class representing a Java .class file + */ +class classfile : public util::membuffer +{ +public: + classfile(char *data, std::size_t size) : membuffer(data, size) + { + valid = false; + is_synthetic = false; + read_be(magic); + if (magic != 0xCAFEBABE) + throw new classfile_exception(); + read_be(minor_version); + read_be(major_version); + constants.load(*this); + read_be(access_flags); + read_be(this_class); + read_be(super_class); + + // Interfaces + uint16_t iface_count = 0; + read_be(iface_count); + while (iface_count) + { + uint16_t iface; + read_be(iface); + interfaces.push_back(iface); + iface_count--; + } + + // Fields + // read fields (and attributes from inside fields) (and possible inner classes. yay for + // recursion!) + // for now though, we will ignore all attributes + /* + * field_info + * { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + uint16_t field_count = 0; + read_be(field_count); + while (field_count) + { + // skip field stuff + skip(6); + // and skip field attributes + uint16_t attr_count = 0; + read_be(attr_count); + while (attr_count) + { + skip(2); + uint32_t attr_length = 0; + read_be(attr_length); + skip(attr_length); + attr_count--; + } + field_count--; + } + + // class methods + /* + * method_info + * { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + uint16_t method_count = 0; + read_be(method_count); + while (method_count) + { + skip(6); + // and skip method attributes + uint16_t attr_count = 0; + read_be(attr_count); + while (attr_count) + { + skip(2); + uint32_t attr_length = 0; + read_be(attr_length); + skip(attr_length); + attr_count--; + } + method_count--; + } + + // class attributes + // there are many kinds of attributes. this is just the generic wrapper structure. + // type is decided by attribute name. extensions to the standard are *possible* + // class annotations are one kind of a attribute (one per class) + /* + * attribute_info + * { + * u2 attribute_name_index; + * u4 attribute_length; + * u1 info[attribute_length]; + * } + */ + uint16_t class_attr_count = 0; + read_be(class_attr_count); + while (class_attr_count) + { + uint16_t name_idx = 0; + read_be(name_idx); + uint32_t attr_length = 0; + read_be(attr_length); + + auto name = constants[name_idx]; + if (name.str_data == "RuntimeVisibleAnnotations") + { + uint16_t num_annotations = 0; + read_be(num_annotations); + while (num_annotations) + { + visible_class_annotations.push_back(annotation::read(*this, constants)); + num_annotations--; + } + } + else + skip(attr_length); + class_attr_count--; + } + valid = true; + } + ; + bool valid; + bool is_synthetic; + uint32_t magic; + uint16_t minor_version; + uint16_t major_version; + constant_pool constants; + uint16_t access_flags; + uint16_t this_class; + uint16_t super_class; + // interfaces this class implements ? must be. investigate. + std::vector interfaces; + // FIXME: doesn't free up memory on delete + java::annotation_table visible_class_annotations; +}; +} \ No newline at end of file diff --git a/libraries/classparser/src/constants.h b/libraries/classparser/src/constants.h new file mode 100644 index 00000000..242b943e --- /dev/null +++ b/libraries/classparser/src/constants.h @@ -0,0 +1,220 @@ +#pragma once +#include "errors.h" +#include + +namespace java +{ +class constant +{ +public: + enum type_t : uint8_t + { + j_hole = 0, // HACK: this is a hole in the array, because java is crazy + j_string_data = 1, + j_int = 3, + j_float = 4, + j_long = 5, + j_double = 6, + j_class = 7, + j_string = 8, + j_fieldref = 9, + j_methodref = 10, + j_interface_methodref = 11, + j_nameandtype = 12 + } type; + + constant(util::membuffer &buf) + { + buf.read(type); + // invalid constant type! + if (type > j_nameandtype || type == (type_t)0 || type == (type_t)2) + throw new classfile_exception(); + + // load data depending on type + switch (type) + { + case j_float: + case j_int: + buf.read_be(int_data); // same as float data really + break; + case j_double: + case j_long: + buf.read_be(long_data); // same as double + break; + case j_class: + buf.read_be(ref_type.class_idx); + break; + case j_fieldref: + case j_methodref: + case j_interface_methodref: + buf.read_be(ref_type.class_idx); + buf.read_be(ref_type.name_and_type_idx); + break; + case j_string: + buf.read_be(index); + break; + case j_string_data: + // HACK HACK: for now, we call these UTF-8 and do no further processing. + // Later, we should do some decoding. It's really modified UTF-8 + // * U+0000 is represented as 0xC0,0x80 invalid character + // * any single zero byte ends the string + // * characters above U+10000 are encoded like in CESU-8 + buf.read_jstr(str_data); + break; + case j_nameandtype: + buf.read_be(name_and_type.name_index); + buf.read_be(name_and_type.descriptor_index); + break; + } + } + + constant(int fake) + { + type = j_hole; + } + + std::string toString() + { + std::ostringstream ss; + switch (type) + { + case j_hole: + ss << "Fake legacy entry"; + break; + case j_float: + ss << "Float: " << float_data; + break; + case j_double: + ss << "Double: " << double_data; + break; + case j_int: + ss << "Int: " << int_data; + break; + case j_long: + ss << "Long: " << long_data; + break; + case j_string_data: + ss << "StrData: " << str_data; + break; + case j_string: + ss << "Str: " << index; + break; + case j_fieldref: + ss << "FieldRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_methodref: + ss << "MethodRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_interface_methodref: + ss << "IfMethodRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_class: + ss << "Class: " << ref_type.class_idx; + break; + case j_nameandtype: + ss << "NameAndType: " << name_and_type.name_index << " " + << name_and_type.descriptor_index; + break; + } + return ss.str(); + } + + std::string str_data; /** String data in 'modified utf-8'.*/ + // store everything here. + union + { + int32_t int_data; + int64_t long_data; + float float_data; + double double_data; + uint16_t index; + struct + { + /** + * Class reference: + * an index within the constant pool to a UTF-8 string containing + * the fully qualified class name (in internal format) + * Used for j_class, j_fieldref, j_methodref and j_interface_methodref + */ + uint16_t class_idx; + // used for j_fieldref, j_methodref and j_interface_methodref + uint16_t name_and_type_idx; + } ref_type; + struct + { + uint16_t name_index; + uint16_t descriptor_index; + } name_and_type; + }; +}; + +/** + * A helper class that represents the custom container used in Java class file for storage of + * constants + */ +class constant_pool +{ +public: + /** + * Create a pool of constants + */ + constant_pool() + { + } + /** + * Load a java constant pool + */ + void load(util::membuffer &buf) + { + uint16_t length = 0; + buf.read_be(length); + length--; + uint16_t index = 1; + const constant *last_constant = nullptr; + while (length) + { + const constant &cnst = constant(buf); + constants.push_back(cnst); + last_constant = &constants[constants.size() - 1]; + if (last_constant->type == constant::j_double || + last_constant->type == constant::j_long) + { + // push in a fake constant to preserve indexing + constants.push_back(constant(0)); + length -= 2; + index += 2; + } + else + { + length--; + index++; + } + } + } + typedef std::vector container_type; + /** + * Access constants based on jar file index numbers (index of the first element is 1) + */ + java::constant &operator[](std::size_t constant_index) + { + if (constant_index == 0 || constant_index > constants.size()) + { + throw new classfile_exception(); + } + return constants[constant_index - 1]; + } + ; + container_type::const_iterator begin() const + { + return constants.begin(); + } + ; + container_type::const_iterator end() const + { + return constants.end(); + } + +private: + container_type constants; +}; +} diff --git a/libraries/classparser/src/errors.h b/libraries/classparser/src/errors.h new file mode 100644 index 00000000..ddbbd828 --- /dev/null +++ b/libraries/classparser/src/errors.h @@ -0,0 +1,8 @@ +#pragma once +#include +namespace java +{ +class classfile_exception : public std::exception +{ +}; +} diff --git a/libraries/classparser/src/javaendian.h b/libraries/classparser/src/javaendian.h new file mode 100644 index 00000000..d488b382 --- /dev/null +++ b/libraries/classparser/src/javaendian.h @@ -0,0 +1,76 @@ +#pragma once +#include + +/** + * Swap bytes between big endian and local number representation + */ +namespace util +{ +#ifdef MULTIMC_BIG_ENDIAN +inline uint64_t bigswap(uint64_t x) +{ + return x; +} +; +inline uint32_t bigswap(uint32_t x) +{ + return x; +} +; +inline uint16_t bigswap(uint16_t x) +{ + return x; +} +; +inline int64_t bigswap(int64_t x) +{ + return x; +} +; +inline int32_t bigswap(int32_t x) +{ + return x; +} +; +inline int16_t bigswap(int16_t x) +{ + return x; +} +; +#else +inline uint64_t bigswap(uint64_t x) +{ + return (x >> 56) | ((x << 40) & 0x00FF000000000000) | ((x << 24) & 0x0000FF0000000000) | + ((x << 8) & 0x000000FF00000000) | ((x >> 8) & 0x00000000FF000000) | + ((x >> 24) & 0x0000000000FF0000) | ((x >> 40) & 0x000000000000FF00) | (x << 56); +} +; +inline uint32_t bigswap(uint32_t x) +{ + return (x >> 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x << 24); +} +; +inline uint16_t bigswap(uint16_t x) +{ + return (x >> 8) | (x << 8); +} +; +inline int64_t bigswap(int64_t x) +{ + return (x >> 56) | ((x << 40) & 0x00FF000000000000) | ((x << 24) & 0x0000FF0000000000) | + ((x << 8) & 0x000000FF00000000) | ((x >> 8) & 0x00000000FF000000) | + ((x >> 24) & 0x0000000000FF0000) | ((x >> 40) & 0x000000000000FF00) | (x << 56); +} +; +inline int32_t bigswap(int32_t x) +{ + return (x >> 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x << 24); +} +; +inline int16_t bigswap(int16_t x) +{ + return (x >> 8) | (x << 8); +} +; +#endif +} diff --git a/libraries/classparser/src/javautils.cpp b/libraries/classparser/src/javautils.cpp new file mode 100644 index 00000000..f9310e5f --- /dev/null +++ b/libraries/classparser/src/javautils.cpp @@ -0,0 +1,83 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Authors: Orochimarufan + * + * 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. + */ +#include "classfile.h" +#include "javautils.h" + +#include +#include + +namespace javautils +{ + +QString GetMinecraftJarVersion(QString jarName) +{ + QString version = MCVer_Unknown; + + // check if minecraft.jar exists + QFile jar(jarName); + if (!jar.exists()) + return version; + + // open minecraft.jar + QuaZip zip(&jar); + if (!zip.open(QuaZip::mdUnzip)) + return version; + + // open Minecraft.class + zip.setCurrentFile("net/minecraft/client/Minecraft.class", QuaZip::csSensitive); + QuaZipFile Minecraft(&zip); + if (!Minecraft.open(QuaZipFile::ReadOnly)) + return version; + + // read Minecraft.class + qint64 size = Minecraft.size(); + char *classfile = new char[size]; + Minecraft.read(classfile, size); + + // parse Minecraft.class + try + { + char *temp = classfile; + java::classfile MinecraftClass(temp, size); + java::constant_pool constants = MinecraftClass.constants; + for (java::constant_pool::container_type::const_iterator iter = constants.begin(); + iter != constants.end(); iter++) + { + const java::constant &constant = *iter; + if (constant.type != java::constant::j_string_data) + continue; + const std::string &str = constant.str_data; + if (str.compare(0, 20, "Minecraft Minecraft ") == 0) + { + version = str.substr(20).data(); + break; + } + } + } + catch (java::classfile_exception &) + { + } + + // clean up + delete[] classfile; + Minecraft.close(); + zip.close(); + jar.close(); + + return version; +} +} diff --git a/libraries/classparser/src/membuffer.h b/libraries/classparser/src/membuffer.h new file mode 100644 index 00000000..ab83412a --- /dev/null +++ b/libraries/classparser/src/membuffer.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include +#include "javaendian.h" + +namespace util +{ +class membuffer +{ +public: + membuffer(char *buffer, std::size_t size) + { + current = start = buffer; + end = start + size; + } + ~membuffer() + { + // maybe? possibly? left out to avoid confusion. for now. + // delete start; + } + /** + * Read some value. That's all ;) + */ + template void read(T &val) + { + val = *(T *)current; + current += sizeof(T); + } + /** + * Read a big-endian number + * valid for 2-byte, 4-byte and 8-byte variables + */ + template void read_be(T &val) + { + val = util::bigswap(*(T *)current); + current += sizeof(T); + } + /** + * Read a string in the format: + * 2B length (big endian, unsigned) + * length bytes data + */ + void read_jstr(std::string &str) + { + uint16_t length = 0; + read_be(length); + str.append(current, length); + current += length; + } + /** + * Skip N bytes + */ + void skip(std::size_t N) + { + current += N; + } + +private: + char *start, *end, *current; +}; +} diff --git a/libraries/gui/CMakeLists.txt b/libraries/gui/CMakeLists.txt deleted file mode 100644 index 1551a927..00000000 --- a/libraries/gui/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -project(MultiMC_logic) - -set(GUI_SOURCES - DesktopServices.h - DesktopServices.cpp - - # Icons - icons/MMCIcon.h - icons/MMCIcon.cpp - icons/IconList.h - icons/IconList.cpp - - SkinUtils.cpp - SkinUtils.h -) -################################ COMPILE ################################ - -add_library(MultiMC_gui SHARED ${GUI_SOURCES}) -set_target_properties(MultiMC_gui PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) - -generate_export_header(MultiMC_gui) - -# Link -target_link_libraries(MultiMC_gui iconfix MultiMC_logic) -qt5_use_modules(MultiMC_gui Gui) - -# Mark and export headers -target_include_directories(MultiMC_gui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/libraries/gui/DesktopServices.cpp b/libraries/gui/DesktopServices.cpp deleted file mode 100644 index 3154ea01..00000000 --- a/libraries/gui/DesktopServices.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "DesktopServices.h" -#include -#include -#include -#include - -/** - * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. - */ -#if defined(Q_OS_LINUX) - -#include -#include -#include -#include - -template -bool IndirectOpen(T callable, qint64 *pid_forked = nullptr) -{ - auto pid = fork(); - if(pid_forked) - { - if(pid > 0) - *pid_forked = pid; - else - *pid_forked = 0; - } - if(pid == -1) - { - qWarning() << "IndirectOpen failed to fork: " << errno; - return false; - } - // child - do the stuff - if(pid == 0) - { - // unset all this garbage so it doesn't get passed to the child process - qunsetenv("LD_PRELOAD"); - qunsetenv("LD_LIBRARY_PATH"); - qunsetenv("LD_DEBUG"); - qunsetenv("QT_PLUGIN_PATH"); - qunsetenv("QT_FONTPATH"); - - // open the URL - auto status = callable(); - - // detach from the parent process group. - setsid(); - - // die. now. do not clean up anything, it would just hang forever. - _exit(status ? 0 : 1); - } - else - { - //parent - assume it worked. - int status; - while (waitpid(pid, &status, 0)) - { - if(WIFEXITED(status)) - { - return WEXITSTATUS(status) == 0; - } - if(WIFSIGNALED(status)) - { - return false; - } - } - return true; - } -} -#endif - -namespace DesktopServices { -bool openDirectory(const QString &path, bool ensureExists) -{ - qDebug() << "Opening directory" << path; - QDir parentPath; - QDir dir(path); - if (!dir.exists()) - { - parentPath.mkpath(dir.absolutePath()); - } - auto f = [&]() - { - return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); - }; -#if defined(Q_OS_LINUX) - return IndirectOpen(f); -#else - return f(); -#endif -} - -bool openFile(const QString &path) -{ - qDebug() << "Opening file" << path; - auto f = [&]() - { - return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); - }; -#if defined(Q_OS_LINUX) - return IndirectOpen(f); -#else - return f(); -#endif -} - -bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid) -{ - qDebug() << "Opening file" << path << "using" << application; -#if defined(Q_OS_LINUX) - // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - return IndirectOpen([&]() - { - return QProcess::startDetached(application, QStringList() << path, workingDirectory); - }, pid); -#else - return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid); -#endif -} - -bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid) -{ - qDebug() << "Running" << application << "with args" << args.join(' '); -#if defined(Q_OS_LINUX) - // FIXME: the pid here is fake. So if something depends on it, it will likely misbehave - return IndirectOpen([&]() - { - return QProcess::startDetached(application, args, workingDirectory); - }, pid); -#else - return QProcess::startDetached(application, args, workingDirectory, pid); -#endif -} - -bool openUrl(const QUrl &url) -{ - qDebug() << "Opening URL" << url.toString(); - auto f = [&]() - { - return QDesktopServices::openUrl(url); - }; -#if defined(Q_OS_LINUX) - return IndirectOpen(f); -#else - return f(); -#endif -} - -} diff --git a/libraries/gui/DesktopServices.h b/libraries/gui/DesktopServices.h deleted file mode 100644 index 9daf192a..00000000 --- a/libraries/gui/DesktopServices.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include "multimc_gui_export.h" - -/** - * This wraps around QDesktopServices and adds workarounds where needed - * Use this instead of QDesktopServices! - */ -namespace DesktopServices -{ - /** - * Open a file in whatever application is applicable - */ - MULTIMC_GUI_EXPORT bool openFile(const QString &path); - - /** - * Open a file in the specified application - */ - MULTIMC_GUI_EXPORT bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0); - - /** - * Run an application - */ - MULTIMC_GUI_EXPORT bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0); - - /** - * Open a directory - */ - MULTIMC_GUI_EXPORT bool openDirectory(const QString &path, bool ensureExists = false); - - /** - * Open the URL, most likely in a browser. Maybe. - */ - MULTIMC_GUI_EXPORT bool openUrl(const QUrl &url); -}; diff --git a/libraries/gui/SkinUtils.cpp b/libraries/gui/SkinUtils.cpp deleted file mode 100644 index f69a1071..00000000 --- a/libraries/gui/SkinUtils.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "SkinUtils.h" -#include "net/HttpMetaCache.h" -#include "Env.h" - -#include -#include -#include -#include - -namespace SkinUtils -{ -/* - * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise - */ -QPixmap getFaceFromCache(QString username, int height, int width) -{ - QFile fskin(ENV.metacache() - ->resolveEntry("skins", username + ".png") - ->getFullPath()); - - if (fskin.exists()) - { - QPixmap skin(fskin.fileName()); - if(!skin.isNull()) - { - return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio); - } - } - - return QPixmap(); -} -} diff --git a/libraries/gui/SkinUtils.h b/libraries/gui/SkinUtils.h deleted file mode 100644 index 29dcd6a6..00000000 --- a/libraries/gui/SkinUtils.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include - -#include "multimc_gui_export.h" - -namespace SkinUtils -{ -QPixmap MULTIMC_GUI_EXPORT getFaceFromCache(QString id, int height = 64, int width = 64); -} diff --git a/libraries/gui/icons/IconList.cpp b/libraries/gui/icons/IconList.cpp deleted file mode 100644 index 99def3b7..00000000 --- a/libraries/gui/icons/IconList.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "IconList.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_SIZE 1024 - -IconList::IconList(QString builtinPath, QString path, QObject *parent) : QAbstractListModel(parent) -{ - // add builtin icons - QDir instance_icons(builtinPath); - auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); - for (auto file_info : file_info_list) - { - QString key = file_info.baseName(); - addIcon(key, key, file_info.absoluteFilePath(), MMCIcon::Builtin); - } - - m_watcher.reset(new QFileSystemWatcher()); - is_watching = false; - connect(m_watcher.get(), SIGNAL(directoryChanged(QString)), - SLOT(directoryChanged(QString))); - connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); - - directoryChanged(path); -} - -void IconList::directoryChanged(const QString &path) -{ - QDir new_dir (path); - if(m_dir.absolutePath() != new_dir.absolutePath()) - { - m_dir.setPath(path); - m_dir.refresh(); - if(is_watching) - stopWatching(); - startWatching(); - } - if(!m_dir.exists()) - if(!FS::ensureFolderPathExists(m_dir.absolutePath())) - return; - m_dir.refresh(); - auto new_list = m_dir.entryList(QDir::Files, QDir::Name); - for (auto it = new_list.begin(); it != new_list.end(); it++) - { - QString &foo = (*it); - foo = m_dir.filePath(foo); - } - auto new_set = new_list.toSet(); - QList current_list; - for (auto &it : icons) - { - if (!it.has(MMCIcon::FileBased)) - continue; - current_list.push_back(it.m_images[MMCIcon::FileBased].filename); - } - QSet current_set = current_list.toSet(); - - QSet to_remove = current_set; - to_remove -= new_set; - - QSet to_add = new_set; - to_add -= current_set; - - for (auto remove : to_remove) - { - qDebug() << "Removing " << remove; - QFileInfo rmfile(remove); - QString key = rmfile.baseName(); - int idx = getIconIndex(key); - if (idx == -1) - continue; - icons[idx].remove(MMCIcon::FileBased); - if (icons[idx].type() == MMCIcon::ToBeDeleted) - { - beginRemoveRows(QModelIndex(), idx, idx); - icons.remove(idx); - reindex(); - endRemoveRows(); - } - else - { - dataChanged(index(idx), index(idx)); - } - m_watcher->removePath(remove); - emit iconUpdated(key); - } - - for (auto add : to_add) - { - qDebug() << "Adding " << add; - QFileInfo addfile(add); - QString key = addfile.baseName(); - if (addIcon(key, QString(), addfile.filePath(), MMCIcon::FileBased)) - { - m_watcher->addPath(add); - emit iconUpdated(key); - } - } -} - -void IconList::fileChanged(const QString &path) -{ - qDebug() << "Checking " << path; - QFileInfo checkfile(path); - if (!checkfile.exists()) - return; - QString key = checkfile.baseName(); - int idx = getIconIndex(key); - if (idx == -1) - return; - QIcon icon(path); - if (!icon.availableSizes().size()) - return; - - icons[idx].m_images[MMCIcon::FileBased].icon = icon; - dataChanged(index(idx), index(idx)); - emit iconUpdated(key); -} - -void IconList::SettingChanged(const Setting &setting, QVariant value) -{ - if(setting.id() != "IconsDir") - return; - - directoryChanged(value.toString()); -} - -void IconList::startWatching() -{ - auto abs_path = m_dir.absolutePath(); - FS::ensureFolderPathExists(abs_path); - is_watching = m_watcher->addPath(abs_path); - if (is_watching) - { - qDebug() << "Started watching " << abs_path; - } - else - { - qDebug() << "Failed to start watching " << abs_path; - } -} - -void IconList::stopWatching() -{ - m_watcher->removePaths(m_watcher->files()); - m_watcher->removePaths(m_watcher->directories()); - is_watching = false; -} - -QStringList IconList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - return types; -} -Qt::DropActions IconList::supportedDropActions() const -{ - return Qt::CopyAction; -} - -bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent) -{ - if (action == Qt::IgnoreAction) - return true; - // check if the action is supported - if (!data || !(action & supportedDropActions())) - return false; - - // files dropped from outside? - if (data->hasUrls()) - { - auto urls = data->urls(); - QStringList iconFiles; - for (auto url : urls) - { - // only local files may be dropped... - if (!url.isLocalFile()) - continue; - iconFiles += url.toLocalFile(); - } - installIcons(iconFiles); - return true; - } - return false; -} - -Qt::ItemFlags IconList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsDropEnabled | defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; -} - -QVariant IconList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - - if (row < 0 || row >= icons.size()) - return QVariant(); - - switch (role) - { - case Qt::DecorationRole: - return icons[row].icon(); - case Qt::DisplayRole: - return icons[row].name(); - case Qt::UserRole: - return icons[row].m_key; - default: - return QVariant(); - } -} - -int IconList::rowCount(const QModelIndex &parent) const -{ - return icons.size(); -} - -void IconList::installIcons(QStringList iconFiles) -{ - for (QString file : iconFiles) - { - QFileInfo fileinfo(file); - if (!fileinfo.isReadable() || !fileinfo.isFile()) - continue; - QString target = FS::PathCombine(m_dir.dirName(), fileinfo.fileName()); - - QString suffix = fileinfo.suffix(); - if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico") - continue; - - if (!QFile::copy(file, target)) - continue; - } -} - -bool IconList::iconFileExists(QString key) -{ - auto iconEntry = icon(key); - if(!iconEntry) - { - return false; - } - return iconEntry->has(MMCIcon::FileBased); -} - -const MMCIcon *IconList::icon(QString key) -{ - int iconIdx = getIconIndex(key); - if (iconIdx == -1) - return nullptr; - return &icons[iconIdx]; -} - -bool IconList::deleteIcon(QString key) -{ - int iconIdx = getIconIndex(key); - if (iconIdx == -1) - return false; - auto &iconEntry = icons[iconIdx]; - if (iconEntry.has(MMCIcon::FileBased)) - { - return QFile::remove(iconEntry.m_images[MMCIcon::FileBased].filename); - } - return false; -} - -bool IconList::addIcon(QString key, QString name, QString path, MMCIcon::Type type) -{ - // replace the icon even? is the input valid? - QIcon icon(path); - if (!icon.availableSizes().size()) - return false; - auto iter = name_index.find(key); - if (iter != name_index.end()) - { - auto &oldOne = icons[*iter]; - oldOne.replace(type, icon, path); - dataChanged(index(*iter), index(*iter)); - return true; - } - else - { - // add a new icon - beginInsertRows(QModelIndex(), icons.size(), icons.size()); - { - MMCIcon mmc_icon; - mmc_icon.m_name = name; - mmc_icon.m_key = key; - mmc_icon.replace(type, icon, path); - icons.push_back(mmc_icon); - name_index[key] = icons.size() - 1; - } - endInsertRows(); - return true; - } -} - -void IconList::reindex() -{ - name_index.clear(); - int i = 0; - for (auto &iter : icons) - { - name_index[iter.m_key] = i; - i++; - } -} - -QIcon IconList::getIcon(QString key) -{ - int icon_index = getIconIndex(key); - - if (icon_index != -1) - return icons[icon_index].icon(); - - // Fallback for icons that don't exist. - icon_index = getIconIndex("infinity"); - - if (icon_index != -1) - return icons[icon_index].icon(); - return QIcon(); -} - -QIcon IconList::getBigIcon(QString key) -{ - int icon_index = getIconIndex(key); - - if (icon_index == -1) - key = "infinity"; - - // Fallback for icons that don't exist. - icon_index = getIconIndex(key); - - if (icon_index == -1) - return QIcon(); - - QPixmap bigone = icons[icon_index].icon().pixmap(256,256).scaled(256,256); - return QIcon(bigone); -} - -int IconList::getIconIndex(QString key) -{ - if (key == "default") - key = "infinity"; - - auto iter = name_index.find(key); - if (iter != name_index.end()) - return *iter; - - return -1; -} - -//#include "IconList.moc" diff --git a/libraries/gui/icons/IconList.h b/libraries/gui/icons/IconList.h deleted file mode 100644 index 971db824..00000000 --- a/libraries/gui/icons/IconList.h +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "MMCIcon.h" -#include "settings/Setting.h" -#include "Env.h" // there is a global icon list inside Env. - -#include "multimc_logic_export.h" - -class QFileSystemWatcher; - -class MULTIMC_LOGIC_EXPORT IconList : public QAbstractListModel -{ - Q_OBJECT -public: - explicit IconList(QString builtinPath, QString path, QObject *parent = 0); - virtual ~IconList() {}; - - QIcon getIcon(QString key); - QIcon getBigIcon(QString key); - int getIconIndex(QString key); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - - bool addIcon(QString key, QString name, QString path, MMCIcon::Type type); - bool deleteIcon(QString key); - bool iconFileExists(QString key); - - virtual QStringList mimeTypes() const; - virtual Qt::DropActions supportedDropActions() const; - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent); - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - void installIcons(QStringList iconFiles); - - const MMCIcon * icon(QString key); - - void startWatching(); - void stopWatching(); - -signals: - void iconUpdated(QString key); - -private: - // hide copy constructor - IconList(const IconList &) = delete; - // hide assign op - IconList &operator=(const IconList &) = delete; - void reindex(); - -public slots: - void directoryChanged(const QString &path); - -protected slots: - void fileChanged(const QString &path); - void SettingChanged(const Setting & setting, QVariant value); -private: - std::shared_ptr m_watcher; - bool is_watching; - QMap name_index; - QVector icons; - QDir m_dir; -}; diff --git a/libraries/gui/icons/MMCIcon.cpp b/libraries/gui/icons/MMCIcon.cpp deleted file mode 100644 index 6b4eef39..00000000 --- a/libraries/gui/icons/MMCIcon.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "MMCIcon.h" -#include - -MMCIcon::Type operator--(MMCIcon::Type &t, int) -{ - MMCIcon::Type temp = t; - switch (t) - { - case MMCIcon::Type::Builtin: - t = MMCIcon::Type::ToBeDeleted; - break; - case MMCIcon::Type::Transient: - t = MMCIcon::Type::Builtin; - break; - case MMCIcon::Type::FileBased: - t = MMCIcon::Type::Transient; - break; - default: - { - } - } - return temp; -} - -MMCIcon::Type MMCIcon::type() const -{ - return m_current_type; -} - -QString MMCIcon::name() const -{ - if (m_name.size()) - return m_name; - return m_key; -} - -bool MMCIcon::has(MMCIcon::Type _type) const -{ - return m_images[_type].present(); -} - -QIcon MMCIcon::icon() const -{ - if (m_current_type == Type::ToBeDeleted) - return QIcon(); - return m_images[m_current_type].icon; -} - -void MMCIcon::remove(Type rm_type) -{ - m_images[rm_type].filename = QString(); - m_images[rm_type].icon = QIcon(); - for (auto iter = rm_type; iter != Type::ToBeDeleted; iter--) - { - if (m_images[iter].present()) - { - m_current_type = iter; - return; - } - } - m_current_type = Type::ToBeDeleted; -} - -void MMCIcon::replace(MMCIcon::Type new_type, QIcon icon, QString path) -{ - QFileInfo foo(path); - if (new_type > m_current_type || m_current_type == MMCIcon::ToBeDeleted) - { - m_current_type = new_type; - } - m_images[new_type].icon = icon; - m_images[new_type].changed = foo.lastModified(); - m_images[new_type].filename = path; -} diff --git a/libraries/gui/icons/MMCIcon.h b/libraries/gui/icons/MMCIcon.h deleted file mode 100644 index 6f9617c2..00000000 --- a/libraries/gui/icons/MMCIcon.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once -#include -#include -#include - -#include "multimc_gui_export.h" - -struct MULTIMC_GUI_EXPORT MMCImage -{ - QIcon icon; - QString filename; - QDateTime changed; - bool present() const - { - return !icon.isNull(); - } -}; - -struct MULTIMC_GUI_EXPORT MMCIcon -{ - enum Type : unsigned - { - Builtin, - Transient, - FileBased, - ICONS_TOTAL, - ToBeDeleted - }; - QString m_key; - QString m_name; - MMCImage m_images[ICONS_TOTAL]; - Type m_current_type = ToBeDeleted; - - Type type() const; - QString name() const; - bool has(Type _type) const; - QIcon icon() const; - void remove(Type rm_type); - void replace(Type new_type, QIcon icon, QString path = QString()); -}; diff --git a/libraries/hoedown/CMakeLists.txt b/libraries/hoedown/CMakeLists.txt new file mode 100644 index 00000000..7902e734 --- /dev/null +++ b/libraries/hoedown/CMakeLists.txt @@ -0,0 +1,26 @@ +# hoedown 3.0.2 - https://github.com/hoedown/hoedown/archive/3.0.2.tar.gz +project(hoedown LANGUAGES C VERSION 3.0.2) + +set(HOEDOWN_SOURCES +include/hoedown/autolink.h +include/hoedown/buffer.h +include/hoedown/document.h +include/hoedown/escape.h +include/hoedown/html.h +include/hoedown/stack.h +include/hoedown/version.h +src/autolink.c +src/buffer.c +src/document.c +src/escape.c +src/html.c +src/html_blocks.c +src/html_smartypants.c +src/stack.c +src/version.c +) + +# Include self. +add_library(hoedown STATIC ${HOEDOWN_SOURCES}) + +target_include_directories(hoedown PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/libraries/hoedown/LICENSE b/libraries/hoedown/LICENSE new file mode 100644 index 00000000..4e75de4d --- /dev/null +++ b/libraries/hoedown/LICENSE @@ -0,0 +1,15 @@ +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. diff --git a/libraries/hoedown/README.md b/libraries/hoedown/README.md new file mode 100644 index 00000000..abe2b6ca --- /dev/null +++ b/libraries/hoedown/README.md @@ -0,0 +1,9 @@ +Hoedown +======= + +This is Hoedown 3.0.2, taken from [the hoedown github repo](https://github.com/hoedown/hoedown). + +`Hoedown` is a revived fork of [Sundown](https://github.com/vmg/sundown), +the Markdown parser based on the original code of the +[Upskirt library](http://fossil.instinctive.eu/libupskirt/index) +by Natacha Porté. diff --git a/libraries/hoedown/include/hoedown/autolink.h b/libraries/hoedown/include/hoedown/autolink.h new file mode 100644 index 00000000..528885c9 --- /dev/null +++ b/libraries/hoedown/include/hoedown/autolink.h @@ -0,0 +1,46 @@ +/* autolink.h - versatile autolinker */ + +#ifndef HOEDOWN_AUTOLINK_H +#define HOEDOWN_AUTOLINK_H + +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +typedef enum hoedown_autolink_flags { + HOEDOWN_AUTOLINK_SHORT_DOMAINS = (1 << 0) +} hoedown_autolink_flags; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_autolink_is_safe: verify that a URL has a safe protocol */ +int hoedown_autolink_is_safe(const uint8_t *data, size_t size); + +/* hoedown_autolink__www: search for the next www link in data */ +size_t hoedown_autolink__www(size_t *rewind_p, hoedown_buffer *link, + uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); + +/* hoedown_autolink__email: search for the next email in data */ +size_t hoedown_autolink__email(size_t *rewind_p, hoedown_buffer *link, + uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); + +/* hoedown_autolink__url: search for the next URL in data */ +size_t hoedown_autolink__url(size_t *rewind_p, hoedown_buffer *link, + uint8_t *data, size_t offset, size_t size, hoedown_autolink_flags flags); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_AUTOLINK_H **/ diff --git a/libraries/hoedown/include/hoedown/buffer.h b/libraries/hoedown/include/hoedown/buffer.h new file mode 100644 index 00000000..d7703f8d --- /dev/null +++ b/libraries/hoedown/include/hoedown/buffer.h @@ -0,0 +1,134 @@ +/* buffer.h - simple, fast buffers */ + +#ifndef HOEDOWN_BUFFER_H +#define HOEDOWN_BUFFER_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#define __attribute__(x) +#define inline __inline +#define __builtin_expect(x,n) x +#endif + + +/********* + * TYPES * + *********/ + +typedef void *(*hoedown_realloc_callback)(void *, size_t); +typedef void (*hoedown_free_callback)(void *); + +struct hoedown_buffer { + uint8_t *data; /* actual character data */ + size_t size; /* size of the string */ + size_t asize; /* allocated size (0 = volatile buffer) */ + size_t unit; /* reallocation unit size (0 = read-only buffer) */ + + hoedown_realloc_callback data_realloc; + hoedown_free_callback data_free; + hoedown_free_callback buffer_free; +}; + +typedef struct hoedown_buffer hoedown_buffer; + + +/************* + * FUNCTIONS * + *************/ + +/* allocation wrappers */ +void *hoedown_malloc(size_t size) __attribute__ ((malloc)); +void *hoedown_calloc(size_t nmemb, size_t size) __attribute__ ((malloc)); +void *hoedown_realloc(void *ptr, size_t size) __attribute__ ((malloc)); + +/* hoedown_buffer_init: initialize a buffer with custom allocators */ +void hoedown_buffer_init( + hoedown_buffer *buffer, + size_t unit, + hoedown_realloc_callback data_realloc, + hoedown_free_callback data_free, + hoedown_free_callback buffer_free +); + +/* hoedown_buffer_uninit: uninitialize an existing buffer */ +void hoedown_buffer_uninit(hoedown_buffer *buf); + +/* hoedown_buffer_new: allocate a new buffer */ +hoedown_buffer *hoedown_buffer_new(size_t unit) __attribute__ ((malloc)); + +/* hoedown_buffer_reset: free internal data of the buffer */ +void hoedown_buffer_reset(hoedown_buffer *buf); + +/* hoedown_buffer_grow: increase the allocated size to the given value */ +void hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz); + +/* hoedown_buffer_put: append raw data to a buffer */ +void hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size); + +/* hoedown_buffer_puts: append a NUL-terminated string to a buffer */ +void hoedown_buffer_puts(hoedown_buffer *buf, const char *str); + +/* hoedown_buffer_putc: append a single char to a buffer */ +void hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c); + +/* hoedown_buffer_putf: read from a file and append to a buffer, until EOF or error */ +int hoedown_buffer_putf(hoedown_buffer *buf, FILE* file); + +/* hoedown_buffer_set: replace the buffer's contents with raw data */ +void hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size); + +/* hoedown_buffer_sets: replace the buffer's contents with a NUL-terminated string */ +void hoedown_buffer_sets(hoedown_buffer *buf, const char *str); + +/* hoedown_buffer_eq: compare a buffer's data with other data for equality */ +int hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size); + +/* hoedown_buffer_eq: compare a buffer's data with NUL-terminated string for equality */ +int hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str); + +/* hoedown_buffer_prefix: compare the beginning of a buffer with a string */ +int hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix); + +/* hoedown_buffer_slurp: remove a given number of bytes from the head of the buffer */ +void hoedown_buffer_slurp(hoedown_buffer *buf, size_t size); + +/* hoedown_buffer_cstr: NUL-termination of the string array (making a C-string) */ +const char *hoedown_buffer_cstr(hoedown_buffer *buf); + +/* hoedown_buffer_printf: formatted printing to a buffer */ +void hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +/* hoedown_buffer_put_utf8: put a Unicode character encoded as UTF-8 */ +void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int codepoint); + +/* hoedown_buffer_free: free the buffer */ +void hoedown_buffer_free(hoedown_buffer *buf); + + +/* HOEDOWN_BUFPUTSL: optimized hoedown_buffer_puts of a string literal */ +#define HOEDOWN_BUFPUTSL(output, literal) \ + hoedown_buffer_put(output, (const uint8_t *)literal, sizeof(literal) - 1) + +/* HOEDOWN_BUFSETSL: optimized hoedown_buffer_sets of a string literal */ +#define HOEDOWN_BUFSETSL(output, literal) \ + hoedown_buffer_set(output, (const uint8_t *)literal, sizeof(literal) - 1) + +/* HOEDOWN_BUFEQSL: optimized hoedown_buffer_eqs of a string literal */ +#define HOEDOWN_BUFEQSL(output, literal) \ + hoedown_buffer_eq(output, (const uint8_t *)literal, sizeof(literal) - 1) + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_BUFFER_H **/ diff --git a/libraries/hoedown/include/hoedown/document.h b/libraries/hoedown/include/hoedown/document.h new file mode 100644 index 00000000..a8178fec --- /dev/null +++ b/libraries/hoedown/include/hoedown/document.h @@ -0,0 +1,172 @@ +/* document.h - generic markdown parser */ + +#ifndef HOEDOWN_DOCUMENT_H +#define HOEDOWN_DOCUMENT_H + +#include "buffer.h" +#include "autolink.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +typedef enum hoedown_extensions { + /* block-level extensions */ + HOEDOWN_EXT_TABLES = (1 << 0), + HOEDOWN_EXT_FENCED_CODE = (1 << 1), + HOEDOWN_EXT_FOOTNOTES = (1 << 2), + + /* span-level extensions */ + HOEDOWN_EXT_AUTOLINK = (1 << 3), + HOEDOWN_EXT_STRIKETHROUGH = (1 << 4), + HOEDOWN_EXT_UNDERLINE = (1 << 5), + HOEDOWN_EXT_HIGHLIGHT = (1 << 6), + HOEDOWN_EXT_QUOTE = (1 << 7), + HOEDOWN_EXT_SUPERSCRIPT = (1 << 8), + HOEDOWN_EXT_MATH = (1 << 9), + + /* other flags */ + HOEDOWN_EXT_NO_INTRA_EMPHASIS = (1 << 11), + HOEDOWN_EXT_SPACE_HEADERS = (1 << 12), + HOEDOWN_EXT_MATH_EXPLICIT = (1 << 13), + + /* negative flags */ + HOEDOWN_EXT_DISABLE_INDENTED_CODE = (1 << 14) +} hoedown_extensions; + +#define HOEDOWN_EXT_BLOCK (\ + HOEDOWN_EXT_TABLES |\ + HOEDOWN_EXT_FENCED_CODE |\ + HOEDOWN_EXT_FOOTNOTES ) + +#define HOEDOWN_EXT_SPAN (\ + HOEDOWN_EXT_AUTOLINK |\ + HOEDOWN_EXT_STRIKETHROUGH |\ + HOEDOWN_EXT_UNDERLINE |\ + HOEDOWN_EXT_HIGHLIGHT |\ + HOEDOWN_EXT_QUOTE |\ + HOEDOWN_EXT_SUPERSCRIPT |\ + HOEDOWN_EXT_MATH ) + +#define HOEDOWN_EXT_FLAGS (\ + HOEDOWN_EXT_NO_INTRA_EMPHASIS |\ + HOEDOWN_EXT_SPACE_HEADERS |\ + HOEDOWN_EXT_MATH_EXPLICIT ) + +#define HOEDOWN_EXT_NEGATIVE (\ + HOEDOWN_EXT_DISABLE_INDENTED_CODE ) + +typedef enum hoedown_list_flags { + HOEDOWN_LIST_ORDERED = (1 << 0), + HOEDOWN_LI_BLOCK = (1 << 1) /*
  • containing block data */ +} hoedown_list_flags; + +typedef enum hoedown_table_flags { + HOEDOWN_TABLE_ALIGN_LEFT = 1, + HOEDOWN_TABLE_ALIGN_RIGHT = 2, + HOEDOWN_TABLE_ALIGN_CENTER = 3, + HOEDOWN_TABLE_ALIGNMASK = 3, + HOEDOWN_TABLE_HEADER = 4 +} hoedown_table_flags; + +typedef enum hoedown_autolink_type { + HOEDOWN_AUTOLINK_NONE, /* used internally when it is not an autolink*/ + HOEDOWN_AUTOLINK_NORMAL, /* normal http/http/ftp/mailto/etc link */ + HOEDOWN_AUTOLINK_EMAIL /* e-mail link without explit mailto: */ +} hoedown_autolink_type; + + +/********* + * TYPES * + *********/ + +struct hoedown_document; +typedef struct hoedown_document hoedown_document; + +struct hoedown_renderer_data { + void *opaque; +}; +typedef struct hoedown_renderer_data hoedown_renderer_data; + +/* hoedown_renderer - functions for rendering parsed data */ +struct hoedown_renderer { + /* state object */ + void *opaque; + + /* block level callbacks - NULL skips the block */ + void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data); + void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*header)(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data); + void (*hrule)(hoedown_buffer *ob, const hoedown_renderer_data *data); + void (*list)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); + void (*listitem)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data); + void (*paragraph)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_header)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_body)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_row)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*table_cell)(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data); + void (*footnotes)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + void (*footnote_def)(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data); + void (*blockhtml)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* span level callbacks - NULL or return 0 prints the span verbatim */ + int (*autolink)(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data); + int (*codespan)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + int (*double_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*underline)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*highlight)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*quote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*image)(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data); + int (*linebreak)(hoedown_buffer *ob, const hoedown_renderer_data *data); + int (*link)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data); + int (*triple_emphasis)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*strikethrough)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*superscript)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data); + int (*footnote_ref)(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data); + int (*math)(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data); + int (*raw_html)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* low level callbacks - NULL copies input directly into the output */ + void (*entity)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + void (*normal_text)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data); + + /* miscellaneous callbacks */ + void (*doc_header)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); + void (*doc_footer)(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data); +}; +typedef struct hoedown_renderer hoedown_renderer; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_document_new: allocate a new document processor instance */ +hoedown_document *hoedown_document_new( + const hoedown_renderer *renderer, + hoedown_extensions extensions, + size_t max_nesting +) __attribute__ ((malloc)); + +/* hoedown_document_render: render regular Markdown using the document processor */ +void hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_document_render_inline: render inline Markdown using the document processor */ +void hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_document_free: deallocate a document processor instance */ +void hoedown_document_free(hoedown_document *doc); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_DOCUMENT_H **/ diff --git a/libraries/hoedown/include/hoedown/escape.h b/libraries/hoedown/include/hoedown/escape.h new file mode 100644 index 00000000..d7659c27 --- /dev/null +++ b/libraries/hoedown/include/hoedown/escape.h @@ -0,0 +1,28 @@ +/* escape.h - escape utilities */ + +#ifndef HOEDOWN_ESCAPE_H +#define HOEDOWN_ESCAPE_H + +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_escape_href: escape (part of) a URL inside HTML */ +void hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_escape_html: escape HTML */ +void hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_ESCAPE_H **/ diff --git a/libraries/hoedown/include/hoedown/html.h b/libraries/hoedown/include/hoedown/html.h new file mode 100644 index 00000000..e46e7fd6 --- /dev/null +++ b/libraries/hoedown/include/hoedown/html.h @@ -0,0 +1,84 @@ +/* html.h - HTML renderer and utilities */ + +#ifndef HOEDOWN_HTML_H +#define HOEDOWN_HTML_H + +#include "document.h" +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +typedef enum hoedown_html_flags { + HOEDOWN_HTML_SKIP_HTML = (1 << 0), + HOEDOWN_HTML_ESCAPE = (1 << 1), + HOEDOWN_HTML_HARD_WRAP = (1 << 2), + HOEDOWN_HTML_USE_XHTML = (1 << 3) +} hoedown_html_flags; + +typedef enum hoedown_html_tag { + HOEDOWN_HTML_TAG_NONE = 0, + HOEDOWN_HTML_TAG_OPEN, + HOEDOWN_HTML_TAG_CLOSE +} hoedown_html_tag; + + +/********* + * TYPES * + *********/ + +struct hoedown_html_renderer_state { + void *opaque; + + struct { + int header_count; + int current_level; + int level_offset; + int nesting_level; + } toc_data; + + hoedown_html_flags flags; + + /* extra callbacks */ + void (*link_attributes)(hoedown_buffer *ob, const hoedown_buffer *url, const hoedown_renderer_data *data); +}; +typedef struct hoedown_html_renderer_state hoedown_html_renderer_state; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_html_smartypants: process an HTML snippet using SmartyPants for smart punctuation */ +void hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *data, size_t size); + +/* hoedown_html_is_tag: checks if data starts with a specific tag, returns the tag type or NONE */ +hoedown_html_tag hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname); + + +/* hoedown_html_renderer_new: allocates a regular HTML renderer */ +hoedown_renderer *hoedown_html_renderer_new( + hoedown_html_flags render_flags, + int nesting_level +) __attribute__ ((malloc)); + +/* hoedown_html_toc_renderer_new: like hoedown_html_renderer_new, but the returned renderer produces the Table of Contents */ +hoedown_renderer *hoedown_html_toc_renderer_new( + int nesting_level +) __attribute__ ((malloc)); + +/* hoedown_html_renderer_free: deallocate an HTML renderer */ +void hoedown_html_renderer_free(hoedown_renderer *renderer); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_HTML_H **/ diff --git a/libraries/hoedown/include/hoedown/stack.h b/libraries/hoedown/include/hoedown/stack.h new file mode 100644 index 00000000..bf9b439b --- /dev/null +++ b/libraries/hoedown/include/hoedown/stack.h @@ -0,0 +1,52 @@ +/* stack.h - simple stacking */ + +#ifndef HOEDOWN_STACK_H +#define HOEDOWN_STACK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/********* + * TYPES * + *********/ + +struct hoedown_stack { + void **item; + size_t size; + size_t asize; +}; +typedef struct hoedown_stack hoedown_stack; + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_stack_init: initialize a stack */ +void hoedown_stack_init(hoedown_stack *st, size_t initial_size); + +/* hoedown_stack_uninit: free internal data of the stack */ +void hoedown_stack_uninit(hoedown_stack *st); + +/* hoedown_stack_grow: increase the allocated size to the given value */ +void hoedown_stack_grow(hoedown_stack *st, size_t neosz); + +/* hoedown_stack_push: push an item to the top of the stack */ +void hoedown_stack_push(hoedown_stack *st, void *item); + +/* hoedown_stack_pop: retrieve and remove the item at the top of the stack */ +void *hoedown_stack_pop(hoedown_stack *st); + +/* hoedown_stack_top: retrieve the item at the top of the stack */ +void *hoedown_stack_top(const hoedown_stack *st); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_STACK_H **/ diff --git a/libraries/hoedown/include/hoedown/version.h b/libraries/hoedown/include/hoedown/version.h new file mode 100644 index 00000000..4938cae5 --- /dev/null +++ b/libraries/hoedown/include/hoedown/version.h @@ -0,0 +1,33 @@ +/* version.h - holds Hoedown's version */ + +#ifndef HOEDOWN_VERSION_H +#define HOEDOWN_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/************* + * CONSTANTS * + *************/ + +#define HOEDOWN_VERSION "3.0.2" +#define HOEDOWN_VERSION_MAJOR 3 +#define HOEDOWN_VERSION_MINOR 0 +#define HOEDOWN_VERSION_REVISION 2 + + +/************* + * FUNCTIONS * + *************/ + +/* hoedown_version: retrieve Hoedown's version numbers */ +void hoedown_version(int *major, int *minor, int *revision); + + +#ifdef __cplusplus +} +#endif + +#endif /** HOEDOWN_VERSION_H **/ diff --git a/libraries/hoedown/src/autolink.c b/libraries/hoedown/src/autolink.c new file mode 100644 index 00000000..9bc7fad5 --- /dev/null +++ b/libraries/hoedown/src/autolink.c @@ -0,0 +1,281 @@ +#include "hoedown/autolink.h" + +#include +#include +#include +#include + +#ifndef _MSC_VER +#include +#else +#define strncasecmp _strnicmp +#endif + +int +hoedown_autolink_is_safe(const uint8_t *data, size_t size) +{ + static const size_t valid_uris_count = 6; + static const char *valid_uris[] = { + "http://", "https://", "/", "#", "ftp://", "mailto:" + }; + static const size_t valid_uris_size[] = { 7, 8, 1, 1, 6, 7 }; + size_t i; + + for (i = 0; i < valid_uris_count; ++i) { + size_t len = valid_uris_size[i]; + + if (size > len && + strncasecmp((char *)data, valid_uris[i], len) == 0 && + isalnum(data[len])) + return 1; + } + + return 0; +} + +static size_t +autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size) +{ + uint8_t cclose, copen = 0; + size_t i; + + for (i = 0; i < link_end; ++i) + if (data[i] == '<') { + link_end = i; + break; + } + + while (link_end > 0) { + if (strchr("?!.,:", data[link_end - 1]) != NULL) + link_end--; + + else if (data[link_end - 1] == ';') { + size_t new_end = link_end - 2; + + while (new_end > 0 && isalpha(data[new_end])) + new_end--; + + if (new_end < link_end - 2 && data[new_end] == '&') + link_end = new_end; + else + link_end--; + } + else break; + } + + if (link_end == 0) + return 0; + + cclose = data[link_end - 1]; + + switch (cclose) { + case '"': copen = '"'; break; + case '\'': copen = '\''; break; + case ')': copen = '('; break; + case ']': copen = '['; break; + case '}': copen = '{'; break; + } + + if (copen != 0) { + size_t closing = 0; + size_t opening = 0; + size_t i = 0; + + /* Try to close the final punctuation sign in this same line; + * if we managed to close it outside of the URL, that means that it's + * not part of the URL. If it closes inside the URL, that means it + * is part of the URL. + * + * Examples: + * + * foo http://www.pokemon.com/Pikachu_(Electric) bar + * => http://www.pokemon.com/Pikachu_(Electric) + * + * foo (http://www.pokemon.com/Pikachu_(Electric)) bar + * => http://www.pokemon.com/Pikachu_(Electric) + * + * foo http://www.pokemon.com/Pikachu_(Electric)) bar + * => http://www.pokemon.com/Pikachu_(Electric)) + * + * (foo http://www.pokemon.com/Pikachu_(Electric)) bar + * => foo http://www.pokemon.com/Pikachu_(Electric) + */ + + while (i < link_end) { + if (data[i] == copen) + opening++; + else if (data[i] == cclose) + closing++; + + i++; + } + + if (closing != opening) + link_end--; + } + + return link_end; +} + +static size_t +check_domain(uint8_t *data, size_t size, int allow_short) +{ + size_t i, np = 0; + + if (!isalnum(data[0])) + return 0; + + for (i = 1; i < size - 1; ++i) { + if (strchr(".:", data[i]) != NULL) np++; + else if (!isalnum(data[i]) && data[i] != '-') break; + } + + if (allow_short) { + /* We don't need a valid domain in the strict sense (with + * least one dot; so just make sure it's composed of valid + * domain characters and return the length of the the valid + * sequence. */ + return i; + } else { + /* a valid domain needs to have at least a dot. + * that's as far as we get */ + return np ? i : 0; + } +} + +size_t +hoedown_autolink__www( + size_t *rewind_p, + hoedown_buffer *link, + uint8_t *data, + size_t max_rewind, + size_t size, + unsigned int flags) +{ + size_t link_end; + + if (max_rewind > 0 && !ispunct(data[-1]) && !isspace(data[-1])) + return 0; + + if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0) + return 0; + + link_end = check_domain(data, size, 0); + + if (link_end == 0) + return 0; + + while (link_end < size && !isspace(data[link_end])) + link_end++; + + link_end = autolink_delim(data, link_end, max_rewind, size); + + if (link_end == 0) + return 0; + + hoedown_buffer_put(link, data, link_end); + *rewind_p = 0; + + return (int)link_end; +} + +size_t +hoedown_autolink__email( + size_t *rewind_p, + hoedown_buffer *link, + uint8_t *data, + size_t max_rewind, + size_t size, + unsigned int flags) +{ + size_t link_end, rewind; + int nb = 0, np = 0; + + for (rewind = 0; rewind < max_rewind; ++rewind) { + uint8_t c = data[-1 - rewind]; + + if (isalnum(c)) + continue; + + if (strchr(".+-_", c) != NULL) + continue; + + break; + } + + if (rewind == 0) + return 0; + + for (link_end = 0; link_end < size; ++link_end) { + uint8_t c = data[link_end]; + + if (isalnum(c)) + continue; + + if (c == '@') + nb++; + else if (c == '.' && link_end < size - 1) + np++; + else if (c != '-' && c != '_') + break; + } + + if (link_end < 2 || nb != 1 || np == 0 || + !isalpha(data[link_end - 1])) + return 0; + + link_end = autolink_delim(data, link_end, max_rewind, size); + + if (link_end == 0) + return 0; + + hoedown_buffer_put(link, data - rewind, link_end + rewind); + *rewind_p = rewind; + + return link_end; +} + +size_t +hoedown_autolink__url( + size_t *rewind_p, + hoedown_buffer *link, + uint8_t *data, + size_t max_rewind, + size_t size, + unsigned int flags) +{ + size_t link_end, rewind = 0, domain_len; + + if (size < 4 || data[1] != '/' || data[2] != '/') + return 0; + + while (rewind < max_rewind && isalpha(data[-1 - rewind])) + rewind++; + + if (!hoedown_autolink_is_safe(data - rewind, size + rewind)) + return 0; + + link_end = strlen("://"); + + domain_len = check_domain( + data + link_end, + size - link_end, + flags & HOEDOWN_AUTOLINK_SHORT_DOMAINS); + + if (domain_len == 0) + return 0; + + link_end += domain_len; + while (link_end < size && !isspace(data[link_end])) + link_end++; + + link_end = autolink_delim(data, link_end, max_rewind, size); + + if (link_end == 0) + return 0; + + hoedown_buffer_put(link, data - rewind, link_end + rewind); + *rewind_p = rewind; + + return link_end; +} diff --git a/libraries/hoedown/src/buffer.c b/libraries/hoedown/src/buffer.c new file mode 100644 index 00000000..1c7ba55a --- /dev/null +++ b/libraries/hoedown/src/buffer.c @@ -0,0 +1,308 @@ +#include "hoedown/buffer.h" + +#include +#include +#include +#include + +void * +hoedown_malloc(size_t size) +{ + void *ret = malloc(size); + + if (!ret) { + fprintf(stderr, "Allocation failed.\n"); + abort(); + } + + return ret; +} + +void * +hoedown_calloc(size_t nmemb, size_t size) +{ + void *ret = calloc(nmemb, size); + + if (!ret) { + fprintf(stderr, "Allocation failed.\n"); + abort(); + } + + return ret; +} + +void * +hoedown_realloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + + if (!ret) { + fprintf(stderr, "Allocation failed.\n"); + abort(); + } + + return ret; +} + +void +hoedown_buffer_init( + hoedown_buffer *buf, + size_t unit, + hoedown_realloc_callback data_realloc, + hoedown_free_callback data_free, + hoedown_free_callback buffer_free) +{ + assert(buf); + + buf->data = NULL; + buf->size = buf->asize = 0; + buf->unit = unit; + buf->data_realloc = data_realloc; + buf->data_free = data_free; + buf->buffer_free = buffer_free; +} + +void +hoedown_buffer_uninit(hoedown_buffer *buf) +{ + assert(buf && buf->unit); + buf->data_free(buf->data); +} + +hoedown_buffer * +hoedown_buffer_new(size_t unit) +{ + hoedown_buffer *ret = hoedown_malloc(sizeof (hoedown_buffer)); + hoedown_buffer_init(ret, unit, hoedown_realloc, free, free); + return ret; +} + +void +hoedown_buffer_free(hoedown_buffer *buf) +{ + if (!buf) return; + assert(buf && buf->unit); + + buf->data_free(buf->data); + + if (buf->buffer_free) + buf->buffer_free(buf); +} + +void +hoedown_buffer_reset(hoedown_buffer *buf) +{ + assert(buf && buf->unit); + + buf->data_free(buf->data); + buf->data = NULL; + buf->size = buf->asize = 0; +} + +void +hoedown_buffer_grow(hoedown_buffer *buf, size_t neosz) +{ + size_t neoasz; + assert(buf && buf->unit); + + if (buf->asize >= neosz) + return; + + neoasz = buf->asize + buf->unit; + while (neoasz < neosz) + neoasz += buf->unit; + + buf->data = (uint8_t *) buf->data_realloc(buf->data, neoasz); + buf->asize = neoasz; +} + +void +hoedown_buffer_put(hoedown_buffer *buf, const uint8_t *data, size_t size) +{ + assert(buf && buf->unit); + + if (buf->size + size > buf->asize) + hoedown_buffer_grow(buf, buf->size + size); + + memcpy(buf->data + buf->size, data, size); + buf->size += size; +} + +void +hoedown_buffer_puts(hoedown_buffer *buf, const char *str) +{ + hoedown_buffer_put(buf, (const uint8_t *)str, strlen(str)); +} + +void +hoedown_buffer_putc(hoedown_buffer *buf, uint8_t c) +{ + assert(buf && buf->unit); + + if (buf->size >= buf->asize) + hoedown_buffer_grow(buf, buf->size + 1); + + buf->data[buf->size] = c; + buf->size += 1; +} + +int +hoedown_buffer_putf(hoedown_buffer *buf, FILE *file) +{ + assert(buf && buf->unit); + + while (!(feof(file) || ferror(file))) { + hoedown_buffer_grow(buf, buf->size + buf->unit); + buf->size += fread(buf->data + buf->size, 1, buf->unit, file); + } + + return ferror(file); +} + +void +hoedown_buffer_set(hoedown_buffer *buf, const uint8_t *data, size_t size) +{ + assert(buf && buf->unit); + + if (size > buf->asize) + hoedown_buffer_grow(buf, size); + + memcpy(buf->data, data, size); + buf->size = size; +} + +void +hoedown_buffer_sets(hoedown_buffer *buf, const char *str) +{ + hoedown_buffer_set(buf, (const uint8_t *)str, strlen(str)); +} + +int +hoedown_buffer_eq(const hoedown_buffer *buf, const uint8_t *data, size_t size) +{ + if (buf->size != size) return 0; + return memcmp(buf->data, data, size) == 0; +} + +int +hoedown_buffer_eqs(const hoedown_buffer *buf, const char *str) +{ + return hoedown_buffer_eq(buf, (const uint8_t *)str, strlen(str)); +} + +int +hoedown_buffer_prefix(const hoedown_buffer *buf, const char *prefix) +{ + size_t i; + + for (i = 0; i < buf->size; ++i) { + if (prefix[i] == 0) + return 0; + + if (buf->data[i] != prefix[i]) + return buf->data[i] - prefix[i]; + } + + return 0; +} + +void +hoedown_buffer_slurp(hoedown_buffer *buf, size_t size) +{ + assert(buf && buf->unit); + + if (size >= buf->size) { + buf->size = 0; + return; + } + + buf->size -= size; + memmove(buf->data, buf->data + size, buf->size); +} + +const char * +hoedown_buffer_cstr(hoedown_buffer *buf) +{ + assert(buf && buf->unit); + + if (buf->size < buf->asize && buf->data[buf->size] == 0) + return (char *)buf->data; + + hoedown_buffer_grow(buf, buf->size + 1); + buf->data[buf->size] = 0; + + return (char *)buf->data; +} + +void +hoedown_buffer_printf(hoedown_buffer *buf, const char *fmt, ...) +{ + va_list ap; + int n; + + assert(buf && buf->unit); + + if (buf->size >= buf->asize) + hoedown_buffer_grow(buf, buf->size + 1); + + va_start(ap, fmt); + n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); + va_end(ap); + + if (n < 0) { +#ifndef _MSC_VER + return; +#else + va_start(ap, fmt); + n = _vscprintf(fmt, ap); + va_end(ap); +#endif + } + + if ((size_t)n >= buf->asize - buf->size) { + hoedown_buffer_grow(buf, buf->size + n + 1); + + va_start(ap, fmt); + n = vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); + va_end(ap); + } + + if (n < 0) + return; + + buf->size += n; +} + +void hoedown_buffer_put_utf8(hoedown_buffer *buf, unsigned int c) { + unsigned char unichar[4]; + + assert(buf && buf->unit); + + if (c < 0x80) { + hoedown_buffer_putc(buf, c); + } + else if (c < 0x800) { + unichar[0] = 192 + (c / 64); + unichar[1] = 128 + (c % 64); + hoedown_buffer_put(buf, unichar, 2); + } + else if (c - 0xd800u < 0x800) { + HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd"); + } + else if (c < 0x10000) { + unichar[0] = 224 + (c / 4096); + unichar[1] = 128 + (c / 64) % 64; + unichar[2] = 128 + (c % 64); + hoedown_buffer_put(buf, unichar, 3); + } + else if (c < 0x110000) { + unichar[0] = 240 + (c / 262144); + unichar[1] = 128 + (c / 4096) % 64; + unichar[2] = 128 + (c / 64) % 64; + unichar[3] = 128 + (c % 64); + hoedown_buffer_put(buf, unichar, 4); + } + else { + HOEDOWN_BUFPUTSL(buf, "\xef\xbf\xbd"); + } +} diff --git a/libraries/hoedown/src/document.c b/libraries/hoedown/src/document.c new file mode 100644 index 00000000..8ba82e47 --- /dev/null +++ b/libraries/hoedown/src/document.c @@ -0,0 +1,2958 @@ +#include "hoedown/document.h" + +#include +#include +#include +#include + +#include "hoedown/stack.h" + +#ifndef _MSC_VER +#include +#else +#define strncasecmp _strnicmp +#endif + +#define REF_TABLE_SIZE 8 + +#define BUFFER_BLOCK 0 +#define BUFFER_SPAN 1 + +#define HOEDOWN_LI_END 8 /* internal list flag */ + +const char *hoedown_find_block_tag(const char *str, unsigned int len); + +/*************** + * LOCAL TYPES * + ***************/ + +/* link_ref: reference to a link */ +struct link_ref { + unsigned int id; + + hoedown_buffer *link; + hoedown_buffer *title; + + struct link_ref *next; +}; + +/* footnote_ref: reference to a footnote */ +struct footnote_ref { + unsigned int id; + + int is_used; + unsigned int num; + + hoedown_buffer *contents; +}; + +/* footnote_item: an item in a footnote_list */ +struct footnote_item { + struct footnote_ref *ref; + struct footnote_item *next; +}; + +/* footnote_list: linked list of footnote_item */ +struct footnote_list { + unsigned int count; + struct footnote_item *head; + struct footnote_item *tail; +}; + +/* char_trigger: function pointer to render active chars */ +/* returns the number of chars taken care of */ +/* data is the pointer of the beginning of the span */ +/* offset is the number of valid chars before data */ +typedef size_t +(*char_trigger)(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); + +static size_t char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); +static size_t char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size); + +enum markdown_char_t { + MD_CHAR_NONE = 0, + MD_CHAR_EMPHASIS, + MD_CHAR_CODESPAN, + MD_CHAR_LINEBREAK, + MD_CHAR_LINK, + MD_CHAR_LANGLE, + MD_CHAR_ESCAPE, + MD_CHAR_ENTITY, + MD_CHAR_AUTOLINK_URL, + MD_CHAR_AUTOLINK_EMAIL, + MD_CHAR_AUTOLINK_WWW, + MD_CHAR_SUPERSCRIPT, + MD_CHAR_QUOTE, + MD_CHAR_MATH +}; + +static char_trigger markdown_char_ptrs[] = { + NULL, + &char_emphasis, + &char_codespan, + &char_linebreak, + &char_link, + &char_langle_tag, + &char_escape, + &char_entity, + &char_autolink_url, + &char_autolink_email, + &char_autolink_www, + &char_superscript, + &char_quote, + &char_math +}; + +struct hoedown_document { + hoedown_renderer md; + hoedown_renderer_data data; + + struct link_ref *refs[REF_TABLE_SIZE]; + struct footnote_list footnotes_found; + struct footnote_list footnotes_used; + uint8_t active_char[256]; + hoedown_stack work_bufs[2]; + hoedown_extensions ext_flags; + size_t max_nesting; + int in_link_body; +}; + +/*************************** + * HELPER FUNCTIONS * + ***************************/ + +static hoedown_buffer * +newbuf(hoedown_document *doc, int type) +{ + static const size_t buf_size[2] = {256, 64}; + hoedown_buffer *work = NULL; + hoedown_stack *pool = &doc->work_bufs[type]; + + if (pool->size < pool->asize && + pool->item[pool->size] != NULL) { + work = pool->item[pool->size++]; + work->size = 0; + } else { + work = hoedown_buffer_new(buf_size[type]); + hoedown_stack_push(pool, work); + } + + return work; +} + +static void +popbuf(hoedown_document *doc, int type) +{ + doc->work_bufs[type].size--; +} + +static void +unscape_text(hoedown_buffer *ob, hoedown_buffer *src) +{ + size_t i = 0, org; + while (i < src->size) { + org = i; + while (i < src->size && src->data[i] != '\\') + i++; + + if (i > org) + hoedown_buffer_put(ob, src->data + org, i - org); + + if (i + 1 >= src->size) + break; + + hoedown_buffer_putc(ob, src->data[i + 1]); + i += 2; + } +} + +static unsigned int +hash_link_ref(const uint8_t *link_ref, size_t length) +{ + size_t i; + unsigned int hash = 0; + + for (i = 0; i < length; ++i) + hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash; + + return hash; +} + +static struct link_ref * +add_link_ref( + struct link_ref **references, + const uint8_t *name, size_t name_size) +{ + struct link_ref *ref = hoedown_calloc(1, sizeof(struct link_ref)); + + ref->id = hash_link_ref(name, name_size); + ref->next = references[ref->id % REF_TABLE_SIZE]; + + references[ref->id % REF_TABLE_SIZE] = ref; + return ref; +} + +static struct link_ref * +find_link_ref(struct link_ref **references, uint8_t *name, size_t length) +{ + unsigned int hash = hash_link_ref(name, length); + struct link_ref *ref = NULL; + + ref = references[hash % REF_TABLE_SIZE]; + + while (ref != NULL) { + if (ref->id == hash) + return ref; + + ref = ref->next; + } + + return NULL; +} + +static void +free_link_refs(struct link_ref **references) +{ + size_t i; + + for (i = 0; i < REF_TABLE_SIZE; ++i) { + struct link_ref *r = references[i]; + struct link_ref *next; + + while (r) { + next = r->next; + hoedown_buffer_free(r->link); + hoedown_buffer_free(r->title); + free(r); + r = next; + } + } +} + +static struct footnote_ref * +create_footnote_ref(struct footnote_list *list, const uint8_t *name, size_t name_size) +{ + struct footnote_ref *ref = hoedown_calloc(1, sizeof(struct footnote_ref)); + + ref->id = hash_link_ref(name, name_size); + + return ref; +} + +static int +add_footnote_ref(struct footnote_list *list, struct footnote_ref *ref) +{ + struct footnote_item *item = hoedown_calloc(1, sizeof(struct footnote_item)); + if (!item) + return 0; + item->ref = ref; + + if (list->head == NULL) { + list->head = list->tail = item; + } else { + list->tail->next = item; + list->tail = item; + } + list->count++; + + return 1; +} + +static struct footnote_ref * +find_footnote_ref(struct footnote_list *list, uint8_t *name, size_t length) +{ + unsigned int hash = hash_link_ref(name, length); + struct footnote_item *item = NULL; + + item = list->head; + + while (item != NULL) { + if (item->ref->id == hash) + return item->ref; + item = item->next; + } + + return NULL; +} + +static void +free_footnote_ref(struct footnote_ref *ref) +{ + hoedown_buffer_free(ref->contents); + free(ref); +} + +static void +free_footnote_list(struct footnote_list *list, int free_refs) +{ + struct footnote_item *item = list->head; + struct footnote_item *next; + + while (item) { + next = item->next; + if (free_refs) + free_footnote_ref(item->ref); + free(item); + item = next; + } +} + + +/* + * Check whether a char is a Markdown spacing char. + + * Right now we only consider spaces the actual + * space and a newline: tabs and carriage returns + * are filtered out during the preprocessing phase. + * + * If we wanted to actually be UTF-8 compliant, we + * should instead extract an Unicode codepoint from + * this character and check for space properties. + */ +static int +_isspace(int c) +{ + return c == ' ' || c == '\n'; +} + +/* is_empty_all: verify that all the data is spacing */ +static int +is_empty_all(const uint8_t *data, size_t size) +{ + size_t i = 0; + while (i < size && _isspace(data[i])) i++; + return i == size; +} + +/* + * Replace all spacing characters in data with spaces. As a special + * case, this collapses a newline with the previous space, if possible. + */ +static void +replace_spacing(hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + size_t i = 0, mark; + hoedown_buffer_grow(ob, size); + while (1) { + mark = i; + while (i < size && data[i] != '\n') i++; + hoedown_buffer_put(ob, data + mark, i - mark); + + if (i >= size) break; + + if (!(i > 0 && data[i-1] == ' ')) + hoedown_buffer_putc(ob, ' '); + i++; + } +} + +/**************************** + * INLINE PARSING FUNCTIONS * + ****************************/ + +/* is_mail_autolink • looks for the address part of a mail autolink and '>' */ +/* this is less strict than the original markdown e-mail address matching */ +static size_t +is_mail_autolink(uint8_t *data, size_t size) +{ + size_t i = 0, nb = 0; + + /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */ + for (i = 0; i < size; ++i) { + if (isalnum(data[i])) + continue; + + switch (data[i]) { + case '@': + nb++; + + case '-': + case '.': + case '_': + break; + + case '>': + return (nb == 1) ? i + 1 : 0; + + default: + return 0; + } + } + + return 0; +} + +/* tag_length • returns the length of the given tag, or 0 is it's not valid */ +static size_t +tag_length(uint8_t *data, size_t size, hoedown_autolink_type *autolink) +{ + size_t i, j; + + /* a valid tag can't be shorter than 3 chars */ + if (size < 3) return 0; + + /* begins with a '<' optionally followed by '/', followed by letter or number */ + if (data[0] != '<') return 0; + i = (data[1] == '/') ? 2 : 1; + + if (!isalnum(data[i])) + return 0; + + /* scheme test */ + *autolink = HOEDOWN_AUTOLINK_NONE; + + /* try to find the beginning of an URI */ + while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-')) + i++; + + if (i > 1 && data[i] == '@') { + if ((j = is_mail_autolink(data + i, size - i)) != 0) { + *autolink = HOEDOWN_AUTOLINK_EMAIL; + return i + j; + } + } + + if (i > 2 && data[i] == ':') { + *autolink = HOEDOWN_AUTOLINK_NORMAL; + i++; + } + + /* completing autolink test: no spacing or ' or " */ + if (i >= size) + *autolink = HOEDOWN_AUTOLINK_NONE; + + else if (*autolink) { + j = i; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == '>' || data[i] == '\'' || + data[i] == '"' || data[i] == ' ' || data[i] == '\n') + break; + else i++; + } + + if (i >= size) return 0; + if (i > j && data[i] == '>') return i + 1; + /* one of the forbidden chars has been found */ + *autolink = HOEDOWN_AUTOLINK_NONE; + } + + /* looking for something looking like a tag end */ + while (i < size && data[i] != '>') i++; + if (i >= size) return 0; + return i + 1; +} + +/* parse_inline • parses inline markdown elements */ +static void +parse_inline(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t i = 0, end = 0, consumed = 0; + hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; + uint8_t *active_char = doc->active_char; + + if (doc->work_bufs[BUFFER_SPAN].size + + doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting) + return; + + while (i < size) { + /* copying inactive chars into the output */ + while (end < size && active_char[data[end]] == 0) + end++; + + if (doc->md.normal_text) { + work.data = data + i; + work.size = end - i; + doc->md.normal_text(ob, &work, &doc->data); + } + else + hoedown_buffer_put(ob, data + i, end - i); + + if (end >= size) break; + i = end; + + end = markdown_char_ptrs[ (int)active_char[data[end]] ](ob, doc, data + i, i - consumed, size - i); + if (!end) /* no action from the callback */ + end = i + 1; + else { + i += end; + end = i; + consumed = i; + } + } +} + +/* is_escaped • returns whether special char at data[loc] is escaped by '\\' */ +static int +is_escaped(uint8_t *data, size_t loc) +{ + size_t i = loc; + while (i >= 1 && data[i - 1] == '\\') + i--; + + /* odd numbers of backslashes escapes data[loc] */ + return (loc - i) % 2; +} + +/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */ +static size_t +find_emph_char(uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0; + + while (i < size) { + while (i < size && data[i] != c && data[i] != '[' && data[i] != '`') + i++; + + if (i == size) + return 0; + + /* not counting escaped chars */ + if (is_escaped(data, i)) { + i++; continue; + } + + if (data[i] == c) + return i; + + /* skipping a codespan */ + if (data[i] == '`') { + size_t span_nb = 0, bt; + size_t tmp_i = 0; + + /* counting the number of opening backticks */ + while (i < size && data[i] == '`') { + i++; span_nb++; + } + + if (i >= size) return 0; + + /* finding the matching closing sequence */ + bt = 0; + while (i < size && bt < span_nb) { + if (!tmp_i && data[i] == c) tmp_i = i; + if (data[i] == '`') bt++; + else bt = 0; + i++; + } + + /* not a well-formed codespan; use found matching emph char */ + if (i >= size) return tmp_i; + } + /* skipping a link */ + else if (data[i] == '[') { + size_t tmp_i = 0; + uint8_t cc; + + i++; + while (i < size && data[i] != ']') { + if (!tmp_i && data[i] == c) tmp_i = i; + i++; + } + + i++; + while (i < size && _isspace(data[i])) + i++; + + if (i >= size) + return tmp_i; + + switch (data[i]) { + case '[': + cc = ']'; break; + + case '(': + cc = ')'; break; + + default: + if (tmp_i) + return tmp_i; + else + continue; + } + + i++; + while (i < size && data[i] != cc) { + if (!tmp_i && data[i] == c) tmp_i = i; + i++; + } + + if (i >= size) + return tmp_i; + + i++; + } + } + + return 0; +} + +/* parse_emph1 • parsing single emphase */ +/* closed by a symbol not preceded by spacing and not followed by symbol */ +static size_t +parse_emph1(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + hoedown_buffer *work = 0; + int r; + + /* skipping one symbol if coming from emph3 */ + if (size > 1 && data[0] == c && data[1] == c) i = 1; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + if (i >= size) return 0; + + if (data[i] == c && !_isspace(data[i - 1])) { + + if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) { + if (i + 1 < size && isalnum(data[i + 1])) + continue; + } + + work = newbuf(doc, BUFFER_SPAN); + parse_inline(work, doc, data, i); + + if (doc->ext_flags & HOEDOWN_EXT_UNDERLINE && c == '_') + r = doc->md.underline(ob, work, &doc->data); + else + r = doc->md.emphasis(ob, work, &doc->data); + + popbuf(doc, BUFFER_SPAN); + return r ? i + 1 : 0; + } + } + + return 0; +} + +/* parse_emph2 • parsing single emphase */ +static size_t +parse_emph2(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + hoedown_buffer *work = 0; + int r; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + + if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) { + work = newbuf(doc, BUFFER_SPAN); + parse_inline(work, doc, data, i); + + if (c == '~') + r = doc->md.strikethrough(ob, work, &doc->data); + else if (c == '=') + r = doc->md.highlight(ob, work, &doc->data); + else + r = doc->md.double_emphasis(ob, work, &doc->data); + + popbuf(doc, BUFFER_SPAN); + return r ? i + 2 : 0; + } + i++; + } + return 0; +} + +/* parse_emph3 • parsing single emphase */ +/* finds the first closing tag, and delegates to the other emph */ +static size_t +parse_emph3(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + int r; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + + /* skip spacing preceded symbols */ + if (data[i] != c || _isspace(data[i - 1])) + continue; + + if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && doc->md.triple_emphasis) { + /* triple symbol found */ + hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); + + parse_inline(work, doc, data, i); + r = doc->md.triple_emphasis(ob, work, &doc->data); + popbuf(doc, BUFFER_SPAN); + return r ? i + 3 : 0; + + } else if (i + 1 < size && data[i + 1] == c) { + /* double symbol found, handing over to emph1 */ + len = parse_emph1(ob, doc, data - 2, size + 2, c); + if (!len) return 0; + else return len - 2; + + } else { + /* single symbol found, handing over to emph2 */ + len = parse_emph2(ob, doc, data - 1, size + 1, c); + if (!len) return 0; + else return len - 1; + } + } + return 0; +} + +/* parse_math • parses a math span until the given ending delimiter */ +static size_t +parse_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size, const char *end, size_t delimsz, int displaymode) +{ + hoedown_buffer text = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t i = delimsz; + + if (!doc->md.math) + return 0; + + /* find ending delimiter */ + while (1) { + while (i < size && data[i] != (uint8_t)end[0]) + i++; + + if (i >= size) + return 0; + + if (!is_escaped(data, i) && !(i + delimsz > size) + && memcmp(data + i, end, delimsz) == 0) + break; + + i++; + } + + /* prepare buffers */ + text.data = data + delimsz; + text.size = i - delimsz; + + /* if this is a $$ and MATH_EXPLICIT is not active, + * guess whether displaymode should be enabled from the context */ + i += delimsz; + if (delimsz == 2 && !(doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT)) + displaymode = is_empty_all(data - offset, offset) && is_empty_all(data + i, size - i); + + /* call callback */ + if (doc->md.math(ob, &text, displaymode, &doc->data)) + return i; + + return 0; +} + +/* char_emphasis • single and double emphasis parsing */ +static size_t +char_emphasis(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + uint8_t c = data[0]; + size_t ret; + + if (doc->ext_flags & HOEDOWN_EXT_NO_INTRA_EMPHASIS) { + if (offset > 0 && !_isspace(data[-1]) && data[-1] != '>' && data[-1] != '(') + return 0; + } + + if (size > 2 && data[1] != c) { + /* spacing cannot follow an opening emphasis; + * strikethrough and highlight only takes two characters '~~' */ + if (c == '~' || c == '=' || _isspace(data[1]) || (ret = parse_emph1(ob, doc, data + 1, size - 1, c)) == 0) + return 0; + + return ret + 1; + } + + if (size > 3 && data[1] == c && data[2] != c) { + if (_isspace(data[2]) || (ret = parse_emph2(ob, doc, data + 2, size - 2, c)) == 0) + return 0; + + return ret + 2; + } + + if (size > 4 && data[1] == c && data[2] == c && data[3] != c) { + if (c == '~' || c == '=' || _isspace(data[3]) || (ret = parse_emph3(ob, doc, data + 3, size - 3, c)) == 0) + return 0; + + return ret + 3; + } + + return 0; +} + + +/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */ +static size_t +char_linebreak(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + if (offset < 2 || data[-1] != ' ' || data[-2] != ' ') + return 0; + + /* removing the last space from ob and rendering */ + while (ob->size && ob->data[ob->size - 1] == ' ') + ob->size--; + + return doc->md.linebreak(ob, &doc->data) ? 1 : 0; +} + + +/* char_codespan • '`' parsing a code span (assuming codespan != 0) */ +static size_t +char_codespan(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t end, nb = 0, i, f_begin, f_end; + + /* counting the number of backticks in the delimiter */ + while (nb < size && data[nb] == '`') + nb++; + + /* finding the next delimiter */ + i = 0; + for (end = nb; end < size && i < nb; end++) { + if (data[end] == '`') i++; + else i = 0; + } + + if (i < nb && end >= size) + return 0; /* no matching delimiter */ + + /* trimming outside spaces */ + f_begin = nb; + while (f_begin < end && data[f_begin] == ' ') + f_begin++; + + f_end = end - nb; + while (f_end > nb && data[f_end-1] == ' ') + f_end--; + + /* real code span */ + if (f_begin < f_end) { + work.data = data + f_begin; + work.size = f_end - f_begin; + + if (!doc->md.codespan(ob, &work, &doc->data)) + end = 0; + } else { + if (!doc->md.codespan(ob, 0, &doc->data)) + end = 0; + } + + return end; +} + +/* char_quote • '"' parsing a quote */ +static size_t +char_quote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + size_t end, nq = 0, i, f_begin, f_end; + + /* counting the number of quotes in the delimiter */ + while (nq < size && data[nq] == '"') + nq++; + + /* finding the next delimiter */ + end = nq; + while (1) { + i = end; + end += find_emph_char(data + end, size - end, '"'); + if (end == i) return 0; /* no matching delimiter */ + i = end; + while (end < size && data[end] == '"' && end - i < nq) end++; + if (end - i >= nq) break; + } + + /* trimming outside spaces */ + f_begin = nq; + while (f_begin < end && data[f_begin] == ' ') + f_begin++; + + f_end = end - nq; + while (f_end > nq && data[f_end-1] == ' ') + f_end--; + + /* real quote */ + if (f_begin < f_end) { + hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); + parse_inline(work, doc, data + f_begin, f_end - f_begin); + + if (!doc->md.quote(ob, work, &doc->data)) + end = 0; + popbuf(doc, BUFFER_SPAN); + } else { + if (!doc->md.quote(ob, 0, &doc->data)) + end = 0; + } + + return end; +} + + +/* char_escape • '\\' backslash escape */ +static size_t +char_escape(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~=\"$"; + hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; + size_t w; + + if (size > 1) { + if (data[1] == '\\' && (doc->ext_flags & HOEDOWN_EXT_MATH) && + size > 2 && (data[2] == '(' || data[2] == '[')) { + const char *end = (data[2] == '[') ? "\\\\]" : "\\\\)"; + w = parse_math(ob, doc, data, offset, size, end, 3, data[2] == '['); + if (w) return w; + } + + if (strchr(escape_chars, data[1]) == NULL) + return 0; + + if (doc->md.normal_text) { + work.data = data + 1; + work.size = 1; + doc->md.normal_text(ob, &work, &doc->data); + } + else hoedown_buffer_putc(ob, data[1]); + } else if (size == 1) { + hoedown_buffer_putc(ob, data[0]); + } + + return 2; +} + +/* char_entity • '&' escaped when it doesn't belong to an entity */ +/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */ +static size_t +char_entity(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + size_t end = 1; + hoedown_buffer work = { 0, 0, 0, 0, NULL, NULL, NULL }; + + if (end < size && data[end] == '#') + end++; + + while (end < size && isalnum(data[end])) + end++; + + if (end < size && data[end] == ';') + end++; /* real entity */ + else + return 0; /* lone '&' */ + + if (doc->md.entity) { + work.data = data; + work.size = end; + doc->md.entity(ob, &work, &doc->data); + } + else hoedown_buffer_put(ob, data, end); + + return end; +} + +/* char_langle_tag • '<' when tags or autolinks are allowed */ +static size_t +char_langle_tag(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + hoedown_autolink_type altype = HOEDOWN_AUTOLINK_NONE; + size_t end = tag_length(data, size, &altype); + int ret = 0; + + work.data = data; + work.size = end; + + if (end > 2) { + if (doc->md.autolink && altype != HOEDOWN_AUTOLINK_NONE) { + hoedown_buffer *u_link = newbuf(doc, BUFFER_SPAN); + work.data = data + 1; + work.size = end - 2; + unscape_text(u_link, &work); + ret = doc->md.autolink(ob, u_link, altype, &doc->data); + popbuf(doc, BUFFER_SPAN); + } + else if (doc->md.raw_html) + ret = doc->md.raw_html(ob, &work, &doc->data); + } + + if (!ret) return 0; + else return end; +} + +static size_t +char_autolink_www(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer *link, *link_url, *link_text; + size_t link_len, rewind; + + if (!doc->md.link || doc->in_link_body) + return 0; + + link = newbuf(doc, BUFFER_SPAN); + + if ((link_len = hoedown_autolink__www(&rewind, link, data, offset, size, HOEDOWN_AUTOLINK_SHORT_DOMAINS)) > 0) { + link_url = newbuf(doc, BUFFER_SPAN); + HOEDOWN_BUFPUTSL(link_url, "http://"); + hoedown_buffer_put(link_url, link->data, link->size); + + ob->size -= rewind; + if (doc->md.normal_text) { + link_text = newbuf(doc, BUFFER_SPAN); + doc->md.normal_text(link_text, link, &doc->data); + doc->md.link(ob, link_text, link_url, NULL, &doc->data); + popbuf(doc, BUFFER_SPAN); + } else { + doc->md.link(ob, link, link_url, NULL, &doc->data); + } + popbuf(doc, BUFFER_SPAN); + } + + popbuf(doc, BUFFER_SPAN); + return link_len; +} + +static size_t +char_autolink_email(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer *link; + size_t link_len, rewind; + + if (!doc->md.autolink || doc->in_link_body) + return 0; + + link = newbuf(doc, BUFFER_SPAN); + + if ((link_len = hoedown_autolink__email(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_EMAIL, &doc->data); + } + + popbuf(doc, BUFFER_SPAN); + return link_len; +} + +static size_t +char_autolink_url(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + hoedown_buffer *link; + size_t link_len, rewind; + + if (!doc->md.autolink || doc->in_link_body) + return 0; + + link = newbuf(doc, BUFFER_SPAN); + + if ((link_len = hoedown_autolink__url(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + doc->md.autolink(ob, link, HOEDOWN_AUTOLINK_NORMAL, &doc->data); + } + + popbuf(doc, BUFFER_SPAN); + return link_len; +} + +/* char_link • '[': parsing a link, a footnote or an image */ +static size_t +char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + int is_img = (offset && data[-1] == '!' && !is_escaped(data - offset, offset - 1)); + int is_footnote = (doc->ext_flags & HOEDOWN_EXT_FOOTNOTES && data[1] == '^'); + size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0; + hoedown_buffer *content = NULL; + hoedown_buffer *link = NULL; + hoedown_buffer *title = NULL; + hoedown_buffer *u_link = NULL; + size_t org_work_size = doc->work_bufs[BUFFER_SPAN].size; + int ret = 0, in_title = 0, qtype = 0; + + /* checking whether the correct renderer exists */ + if ((is_footnote && !doc->md.footnote_ref) || (is_img && !doc->md.image) + || (!is_img && !is_footnote && !doc->md.link)) + goto cleanup; + + /* looking for the matching closing bracket */ + i += find_emph_char(data + i, size - i, ']'); + txt_e = i; + + if (i < size && data[i] == ']') i++; + else goto cleanup; + + /* footnote link */ + if (is_footnote) { + hoedown_buffer id = { NULL, 0, 0, 0, NULL, NULL, NULL }; + struct footnote_ref *fr; + + if (txt_e < 3) + goto cleanup; + + id.data = data + 2; + id.size = txt_e - 2; + + fr = find_footnote_ref(&doc->footnotes_found, id.data, id.size); + + /* mark footnote used */ + if (fr && !fr->is_used) { + if(!add_footnote_ref(&doc->footnotes_used, fr)) + goto cleanup; + fr->is_used = 1; + fr->num = doc->footnotes_used.count; + + /* render */ + if (doc->md.footnote_ref) + ret = doc->md.footnote_ref(ob, fr->num, &doc->data); + } + + goto cleanup; + } + + /* skip any amount of spacing */ + /* (this is much more laxist than original markdown syntax) */ + while (i < size && _isspace(data[i])) + i++; + + /* inline style link */ + if (i < size && data[i] == '(') { + size_t nb_p; + + /* skipping initial spacing */ + i++; + + while (i < size && _isspace(data[i])) + i++; + + link_b = i; + + /* looking for link end: ' " ) */ + /* Count the number of open parenthesis */ + nb_p = 0; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == '(' && i != 0) { + nb_p++; i++; + } + else if (data[i] == ')') { + if (nb_p == 0) break; + else nb_p--; i++; + } else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break; + else i++; + } + + if (i >= size) goto cleanup; + link_e = i; + + /* looking for title end if present */ + if (data[i] == '\'' || data[i] == '"') { + qtype = data[i]; + in_title = 1; + i++; + title_b = i; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == qtype) {in_title = 0; i++;} + else if ((data[i] == ')') && !in_title) break; + else i++; + } + + if (i >= size) goto cleanup; + + /* skipping spacing after title */ + title_e = i - 1; + while (title_e > title_b && _isspace(data[title_e])) + title_e--; + + /* checking for closing quote presence */ + if (data[title_e] != '\'' && data[title_e] != '"') { + title_b = title_e = 0; + link_e = i; + } + } + + /* remove spacing at the end of the link */ + while (link_e > link_b && _isspace(data[link_e - 1])) + link_e--; + + /* remove optional angle brackets around the link */ + if (data[link_b] == '<') link_b++; + if (data[link_e - 1] == '>') link_e--; + + /* building escaped link and title */ + if (link_e > link_b) { + link = newbuf(doc, BUFFER_SPAN); + hoedown_buffer_put(link, data + link_b, link_e - link_b); + } + + if (title_e > title_b) { + title = newbuf(doc, BUFFER_SPAN); + hoedown_buffer_put(title, data + title_b, title_e - title_b); + } + + i++; + } + + /* reference style link */ + else if (i < size && data[i] == '[') { + hoedown_buffer *id = newbuf(doc, BUFFER_SPAN); + struct link_ref *lr; + + /* looking for the id */ + i++; + link_b = i; + while (i < size && data[i] != ']') i++; + if (i >= size) goto cleanup; + link_e = i; + + /* finding the link_ref */ + if (link_b == link_e) + replace_spacing(id, data + 1, txt_e - 1); + else + hoedown_buffer_put(id, data + link_b, link_e - link_b); + + lr = find_link_ref(doc->refs, id->data, id->size); + if (!lr) + goto cleanup; + + /* keeping link and title from link_ref */ + link = lr->link; + title = lr->title; + i++; + } + + /* shortcut reference style link */ + else { + hoedown_buffer *id = newbuf(doc, BUFFER_SPAN); + struct link_ref *lr; + + /* crafting the id */ + replace_spacing(id, data + 1, txt_e - 1); + + /* finding the link_ref */ + lr = find_link_ref(doc->refs, id->data, id->size); + if (!lr) + goto cleanup; + + /* keeping link and title from link_ref */ + link = lr->link; + title = lr->title; + + /* rewinding the spacing */ + i = txt_e + 1; + } + + /* building content: img alt is kept, only link content is parsed */ + if (txt_e > 1) { + content = newbuf(doc, BUFFER_SPAN); + if (is_img) { + hoedown_buffer_put(content, data + 1, txt_e - 1); + } else { + /* disable autolinking when parsing inline the + * content of a link */ + doc->in_link_body = 1; + parse_inline(content, doc, data + 1, txt_e - 1); + doc->in_link_body = 0; + } + } + + if (link) { + u_link = newbuf(doc, BUFFER_SPAN); + unscape_text(u_link, link); + } + + /* calling the relevant rendering function */ + if (is_img) { + if (ob->size && ob->data[ob->size - 1] == '!') + ob->size -= 1; + + ret = doc->md.image(ob, u_link, title, content, &doc->data); + } else { + ret = doc->md.link(ob, content, u_link, title, &doc->data); + } + + /* cleanup */ +cleanup: + doc->work_bufs[BUFFER_SPAN].size = (int)org_work_size; + return ret ? i : 0; +} + +static size_t +char_superscript(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + size_t sup_start, sup_len; + hoedown_buffer *sup; + + if (!doc->md.superscript) + return 0; + + if (size < 2) + return 0; + + if (data[1] == '(') { + sup_start = 2; + sup_len = find_emph_char(data + 2, size - 2, ')') + 2; + + if (sup_len == size) + return 0; + } else { + sup_start = sup_len = 1; + + while (sup_len < size && !_isspace(data[sup_len])) + sup_len++; + } + + if (sup_len - sup_start == 0) + return (sup_start == 2) ? 3 : 0; + + sup = newbuf(doc, BUFFER_SPAN); + parse_inline(sup, doc, data + sup_start, sup_len - sup_start); + doc->md.superscript(ob, sup, &doc->data); + popbuf(doc, BUFFER_SPAN); + + return (sup_start == 2) ? sup_len + 1 : sup_len; +} + +static size_t +char_math(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offset, size_t size) +{ + /* double dollar */ + if (size > 1 && data[1] == '$') + return parse_math(ob, doc, data, offset, size, "$$", 2, 1); + + /* single dollar allowed only with MATH_EXPLICIT flag */ + if (doc->ext_flags & HOEDOWN_EXT_MATH_EXPLICIT) + return parse_math(ob, doc, data, offset, size, "$", 1, 0); + + return 0; +} + +/********************************* + * BLOCK-LEVEL PARSING FUNCTIONS * + *********************************/ + +/* is_empty • returns the line length when it is empty, 0 otherwise */ +static size_t +is_empty(const uint8_t *data, size_t size) +{ + size_t i; + + for (i = 0; i < size && data[i] != '\n'; i++) + if (data[i] != ' ') + return 0; + + return i + 1; +} + +/* is_hrule • returns whether a line is a horizontal rule */ +static int +is_hrule(uint8_t *data, size_t size) +{ + size_t i = 0, n = 0; + uint8_t c; + + /* skipping initial spaces */ + if (size < 3) return 0; + if (data[0] == ' ') { i++; + if (data[1] == ' ') { i++; + if (data[2] == ' ') { i++; } } } + + /* looking at the hrule uint8_t */ + if (i + 2 >= size + || (data[i] != '*' && data[i] != '-' && data[i] != '_')) + return 0; + c = data[i]; + + /* the whole line must be the char or space */ + while (i < size && data[i] != '\n') { + if (data[i] == c) n++; + else if (data[i] != ' ') + return 0; + + i++; + } + + return n >= 3; +} + +/* check if a line is a code fence; return the + * end of the code fence. if passed, width of + * the fence rule and character will be returned */ +static size_t +is_codefence(uint8_t *data, size_t size, size_t *width, uint8_t *chr) +{ + size_t i = 0, n = 1; + uint8_t c; + + /* skipping initial spaces */ + if (size < 3) + return 0; + + if (data[0] == ' ') { i++; + if (data[1] == ' ') { i++; + if (data[2] == ' ') { i++; } } } + + /* looking at the hrule uint8_t */ + c = data[i]; + if (i + 2 >= size || !(c=='~' || c=='`')) + return 0; + + /* the fence must be that same character */ + while (++i < size && data[i] == c) + ++n; + + if (n < 3) + return 0; + + if (width) *width = n; + if (chr) *chr = c; + return i; +} + +/* expects single line, checks if it's a codefence and extracts language */ +static size_t +parse_codefence(uint8_t *data, size_t size, hoedown_buffer *lang, size_t *width, uint8_t *chr) +{ + size_t i, w, lang_start; + + i = w = is_codefence(data, size, width, chr); + if (i == 0) + return 0; + + while (i < size && _isspace(data[i])) + i++; + + lang_start = i; + + while (i < size && !_isspace(data[i])) + i++; + + lang->data = data + lang_start; + lang->size = i - lang_start; + + /* Avoid parsing a codespan as a fence */ + i = lang_start + 2; + while (i < size && !(data[i] == *chr && data[i-1] == *chr && data[i-2] == *chr)) i++; + if (i < size) return 0; + + return w; +} + +/* is_atxheader • returns whether the line is a hash-prefixed header */ +static int +is_atxheader(hoedown_document *doc, uint8_t *data, size_t size) +{ + if (data[0] != '#') + return 0; + + if (doc->ext_flags & HOEDOWN_EXT_SPACE_HEADERS) { + size_t level = 0; + + while (level < size && level < 6 && data[level] == '#') + level++; + + if (level < size && data[level] != ' ') + return 0; + } + + return 1; +} + +/* is_headerline • returns whether the line is a setext-style hdr underline */ +static int +is_headerline(uint8_t *data, size_t size) +{ + size_t i = 0; + + /* test of level 1 header */ + if (data[i] == '=') { + for (i = 1; i < size && data[i] == '='; i++); + while (i < size && data[i] == ' ') i++; + return (i >= size || data[i] == '\n') ? 1 : 0; } + + /* test of level 2 header */ + if (data[i] == '-') { + for (i = 1; i < size && data[i] == '-'; i++); + while (i < size && data[i] == ' ') i++; + return (i >= size || data[i] == '\n') ? 2 : 0; } + + return 0; +} + +static int +is_next_headerline(uint8_t *data, size_t size) +{ + size_t i = 0; + + while (i < size && data[i] != '\n') + i++; + + if (++i >= size) + return 0; + + return is_headerline(data + i, size - i); +} + +/* prefix_quote • returns blockquote prefix length */ +static size_t +prefix_quote(uint8_t *data, size_t size) +{ + size_t i = 0; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i < size && data[i] == '>') { + if (i + 1 < size && data[i + 1] == ' ') + return i + 2; + + return i + 1; + } + + return 0; +} + +/* prefix_code • returns prefix length for block code*/ +static size_t +prefix_code(uint8_t *data, size_t size) +{ + if (size > 3 && data[0] == ' ' && data[1] == ' ' + && data[2] == ' ' && data[3] == ' ') return 4; + + return 0; +} + +/* prefix_oli • returns ordered list item prefix */ +static size_t +prefix_oli(uint8_t *data, size_t size) +{ + size_t i = 0; + + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i >= size || data[i] < '0' || data[i] > '9') + return 0; + + while (i < size && data[i] >= '0' && data[i] <= '9') + i++; + + if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ') + return 0; + + if (is_next_headerline(data + i, size - i)) + return 0; + + return i + 2; +} + +/* prefix_uli • returns ordered list item prefix */ +static size_t +prefix_uli(uint8_t *data, size_t size) +{ + size_t i = 0; + + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i + 1 >= size || + (data[i] != '*' && data[i] != '+' && data[i] != '-') || + data[i + 1] != ' ') + return 0; + + if (is_next_headerline(data + i, size - i)) + return 0; + + return i + 2; +} + + +/* parse_block • parsing of one block, returning next uint8_t to parse */ +static void parse_block(hoedown_buffer *ob, hoedown_document *doc, + uint8_t *data, size_t size); + + +/* parse_blockquote • handles parsing of a blockquote fragment */ +static size_t +parse_blockquote(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t beg, end = 0, pre, work_size = 0; + uint8_t *work_data = 0; + hoedown_buffer *out = 0; + + out = newbuf(doc, BUFFER_BLOCK); + beg = 0; + while (beg < size) { + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); + + pre = prefix_quote(data + beg, end - beg); + + if (pre) + beg += pre; /* skipping prefix */ + + /* empty line followed by non-quote line */ + else if (is_empty(data + beg, end - beg) && + (end >= size || (prefix_quote(data + end, size - end) == 0 && + !is_empty(data + end, size - end)))) + break; + + if (beg < end) { /* copy into the in-place working buffer */ + /* hoedown_buffer_put(work, data + beg, end - beg); */ + if (!work_data) + work_data = data + beg; + else if (data + beg != work_data + work_size) + memmove(work_data + work_size, data + beg, end - beg); + work_size += end - beg; + } + beg = end; + } + + parse_block(out, doc, work_data, work_size); + if (doc->md.blockquote) + doc->md.blockquote(ob, out, &doc->data); + popbuf(doc, BUFFER_BLOCK); + return end; +} + +static size_t +parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render); + +/* parse_blockquote • handles parsing of a regular paragraph */ +static size_t +parse_paragraph(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t i = 0, end = 0; + int level = 0; + + work.data = data; + + while (i < size) { + for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */; + + if (is_empty(data + i, size - i)) + break; + + if ((level = is_headerline(data + i, size - i)) != 0) + break; + + if (is_atxheader(doc, data + i, size - i) || + is_hrule(data + i, size - i) || + prefix_quote(data + i, size - i)) { + end = i; + break; + } + + i = end; + } + + work.size = i; + while (work.size && data[work.size - 1] == '\n') + work.size--; + + if (!level) { + hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK); + parse_inline(tmp, doc, work.data, work.size); + if (doc->md.paragraph) + doc->md.paragraph(ob, tmp, &doc->data); + popbuf(doc, BUFFER_BLOCK); + } else { + hoedown_buffer *header_work; + + if (work.size) { + size_t beg; + i = work.size; + work.size -= 1; + + while (work.size && data[work.size] != '\n') + work.size -= 1; + + beg = work.size + 1; + while (work.size && data[work.size - 1] == '\n') + work.size -= 1; + + if (work.size > 0) { + hoedown_buffer *tmp = newbuf(doc, BUFFER_BLOCK); + parse_inline(tmp, doc, work.data, work.size); + + if (doc->md.paragraph) + doc->md.paragraph(ob, tmp, &doc->data); + + popbuf(doc, BUFFER_BLOCK); + work.data += beg; + work.size = i - beg; + } + else work.size = i; + } + + header_work = newbuf(doc, BUFFER_SPAN); + parse_inline(header_work, doc, work.data, work.size); + + if (doc->md.header) + doc->md.header(ob, header_work, (int)level, &doc->data); + + popbuf(doc, BUFFER_SPAN); + } + + return end; +} + +/* parse_fencedcode • handles parsing of a block-level code fragment */ +static size_t +parse_fencedcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + hoedown_buffer text = { 0, 0, 0, 0, NULL, NULL, NULL }; + hoedown_buffer lang = { 0, 0, 0, 0, NULL, NULL, NULL }; + size_t i = 0, text_start, line_start; + size_t w, w2; + size_t width, width2; + uint8_t chr, chr2; + + /* parse codefence line */ + while (i < size && data[i] != '\n') + i++; + + w = parse_codefence(data, i, &lang, &width, &chr); + if (!w) + return 0; + + /* search for end */ + i++; + text_start = i; + while ((line_start = i) < size) { + while (i < size && data[i] != '\n') + i++; + + w2 = is_codefence(data + line_start, i - line_start, &width2, &chr2); + if (w == w2 && width == width2 && chr == chr2 && + is_empty(data + (line_start+w), i - (line_start+w))) + break; + + i++; + } + + text.data = data + text_start; + text.size = line_start - text_start; + + if (doc->md.blockcode) + doc->md.blockcode(ob, text.size ? &text : NULL, lang.size ? &lang : NULL, &doc->data); + + return i; +} + +static size_t +parse_blockcode(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t beg, end, pre; + hoedown_buffer *work = 0; + + work = newbuf(doc, BUFFER_BLOCK); + + beg = 0; + while (beg < size) { + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {}; + pre = prefix_code(data + beg, end - beg); + + if (pre) + beg += pre; /* skipping prefix */ + else if (!is_empty(data + beg, end - beg)) + /* non-empty non-prefixed line breaks the pre */ + break; + + if (beg < end) { + /* verbatim copy to the working buffer, + escaping entities */ + if (is_empty(data + beg, end - beg)) + hoedown_buffer_putc(work, '\n'); + else hoedown_buffer_put(work, data + beg, end - beg); + } + beg = end; + } + + while (work->size && work->data[work->size - 1] == '\n') + work->size -= 1; + + hoedown_buffer_putc(work, '\n'); + + if (doc->md.blockcode) + doc->md.blockcode(ob, work, NULL, &doc->data); + + popbuf(doc, BUFFER_BLOCK); + return beg; +} + +/* parse_listitem • parsing of a single list item */ +/* assuming initial prefix is already removed */ +static size_t +parse_listitem(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags *flags) +{ + hoedown_buffer *work = 0, *inter = 0; + size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; + int in_empty = 0, has_inside_empty = 0, in_fence = 0; + + /* keeping track of the first indentation prefix */ + while (orgpre < 3 && orgpre < size && data[orgpre] == ' ') + orgpre++; + + beg = prefix_uli(data, size); + if (!beg) + beg = prefix_oli(data, size); + + if (!beg) + return 0; + + /* skipping to the beginning of the following line */ + end = beg; + while (end < size && data[end - 1] != '\n') + end++; + + /* getting working buffers */ + work = newbuf(doc, BUFFER_SPAN); + inter = newbuf(doc, BUFFER_SPAN); + + /* putting the first line into the working buffer */ + hoedown_buffer_put(work, data + beg, end - beg); + beg = end; + + /* process the following lines */ + while (beg < size) { + size_t has_next_uli = 0, has_next_oli = 0; + + end++; + + while (end < size && data[end - 1] != '\n') + end++; + + /* process an empty line */ + if (is_empty(data + beg, end - beg)) { + in_empty = 1; + beg = end; + continue; + } + + /* calculating the indentation */ + i = 0; + while (i < 4 && beg + i < end && data[beg + i] == ' ') + i++; + + pre = i; + + if (doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) { + if (is_codefence(data + beg + i, end - beg - i, NULL, NULL)) + in_fence = !in_fence; + } + + /* Only check for new list items if we are **not** inside + * a fenced code block */ + if (!in_fence) { + has_next_uli = prefix_uli(data + beg + i, end - beg - i); + has_next_oli = prefix_oli(data + beg + i, end - beg - i); + } + + /* checking for a new item */ + if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) { + if (in_empty) + has_inside_empty = 1; + + /* the following item must have the same (or less) indentation */ + if (pre <= orgpre) { + /* if the following item has different list type, we end this list */ + if (in_empty && ( + ((*flags & HOEDOWN_LIST_ORDERED) && has_next_uli) || + (!(*flags & HOEDOWN_LIST_ORDERED) && has_next_oli))) + *flags |= HOEDOWN_LI_END; + + break; + } + + if (!sublist) + sublist = work->size; + } + /* joining only indented stuff after empty lines; + * note that now we only require 1 space of indentation + * to continue a list */ + else if (in_empty && pre == 0) { + *flags |= HOEDOWN_LI_END; + break; + } + + if (in_empty) { + hoedown_buffer_putc(work, '\n'); + has_inside_empty = 1; + in_empty = 0; + } + + /* adding the line without prefix into the working buffer */ + hoedown_buffer_put(work, data + beg + i, end - beg - i); + beg = end; + } + + /* render of li contents */ + if (has_inside_empty) + *flags |= HOEDOWN_LI_BLOCK; + + if (*flags & HOEDOWN_LI_BLOCK) { + /* intermediate render of block li */ + if (sublist && sublist < work->size) { + parse_block(inter, doc, work->data, sublist); + parse_block(inter, doc, work->data + sublist, work->size - sublist); + } + else + parse_block(inter, doc, work->data, work->size); + } else { + /* intermediate render of inline li */ + if (sublist && sublist < work->size) { + parse_inline(inter, doc, work->data, sublist); + parse_block(inter, doc, work->data + sublist, work->size - sublist); + } + else + parse_inline(inter, doc, work->data, work->size); + } + + /* render of li itself */ + if (doc->md.listitem) + doc->md.listitem(ob, inter, *flags, &doc->data); + + popbuf(doc, BUFFER_SPAN); + popbuf(doc, BUFFER_SPAN); + return beg; +} + + +/* parse_list • parsing ordered or unordered list block */ +static size_t +parse_list(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, hoedown_list_flags flags) +{ + hoedown_buffer *work = 0; + size_t i = 0, j; + + work = newbuf(doc, BUFFER_BLOCK); + + while (i < size) { + j = parse_listitem(work, doc, data + i, size - i, &flags); + i += j; + + if (!j || (flags & HOEDOWN_LI_END)) + break; + } + + if (doc->md.list) + doc->md.list(ob, work, flags, &doc->data); + popbuf(doc, BUFFER_BLOCK); + return i; +} + +/* parse_atxheader • parsing of atx-style headers */ +static size_t +parse_atxheader(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t level = 0; + size_t i, end, skip; + + while (level < size && level < 6 && data[level] == '#') + level++; + + for (i = level; i < size && data[i] == ' '; i++); + + for (end = i; end < size && data[end] != '\n'; end++); + skip = end; + + while (end && data[end - 1] == '#') + end--; + + while (end && data[end - 1] == ' ') + end--; + + if (end > i) { + hoedown_buffer *work = newbuf(doc, BUFFER_SPAN); + + parse_inline(work, doc, data + i, end - i); + + if (doc->md.header) + doc->md.header(ob, work, (int)level, &doc->data); + + popbuf(doc, BUFFER_SPAN); + } + + return skip; +} + +/* parse_footnote_def • parse a single footnote definition */ +static void +parse_footnote_def(hoedown_buffer *ob, hoedown_document *doc, unsigned int num, uint8_t *data, size_t size) +{ + hoedown_buffer *work = 0; + work = newbuf(doc, BUFFER_SPAN); + + parse_block(work, doc, data, size); + + if (doc->md.footnote_def) + doc->md.footnote_def(ob, work, num, &doc->data); + popbuf(doc, BUFFER_SPAN); +} + +/* parse_footnote_list • render the contents of the footnotes */ +static void +parse_footnote_list(hoedown_buffer *ob, hoedown_document *doc, struct footnote_list *footnotes) +{ + hoedown_buffer *work = 0; + struct footnote_item *item; + struct footnote_ref *ref; + + if (footnotes->count == 0) + return; + + work = newbuf(doc, BUFFER_BLOCK); + + item = footnotes->head; + while (item) { + ref = item->ref; + parse_footnote_def(work, doc, ref->num, ref->contents->data, ref->contents->size); + item = item->next; + } + + if (doc->md.footnotes) + doc->md.footnotes(ob, work, &doc->data); + popbuf(doc, BUFFER_BLOCK); +} + +/* htmlblock_is_end • check for end of HTML block : ( *)\n */ +/* returns tag length on match, 0 otherwise */ +/* assumes data starts with "<" */ +static size_t +htmlblock_is_end( + const char *tag, + size_t tag_len, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i = tag_len + 3, w; + + /* try to match the end tag */ + /* note: we're not considering tags like "" which are still valid */ + if (i > size || + data[1] != '/' || + strncasecmp((char *)data + 2, tag, tag_len) != 0 || + data[tag_len + 2] != '>') + return 0; + + /* rest of the line must be empty */ + if ((w = is_empty(data + i, size - i)) == 0 && i < size) + return 0; + + return i + w; +} + +/* htmlblock_find_end • try to find HTML block ending tag */ +/* returns the length on match, 0 otherwise */ +static size_t +htmlblock_find_end( + const char *tag, + size_t tag_len, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i = 0, w; + + while (1) { + while (i < size && data[i] != '<') i++; + if (i >= size) return 0; + + w = htmlblock_is_end(tag, tag_len, doc, data + i, size - i); + if (w) return i + w; + i++; + } +} + +/* htmlblock_find_end_strict • try to find end of HTML block in strict mode */ +/* (it must be an unindented line, and have a blank line afterwads) */ +/* returns the length on match, 0 otherwise */ +static size_t +htmlblock_find_end_strict( + const char *tag, + size_t tag_len, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i = 0, mark; + + while (1) { + mark = i; + while (i < size && data[i] != '\n') i++; + if (i < size) i++; + if (i == mark) return 0; + + if (data[mark] == ' ' && mark > 0) continue; + mark += htmlblock_find_end(tag, tag_len, doc, data + mark, i - mark); + if (mark == i && (is_empty(data + i, size - i) || i >= size)) break; + } + + return i; +} + +/* parse_htmlblock • parsing of inline HTML block */ +static size_t +parse_htmlblock(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size, int do_render) +{ + hoedown_buffer work = { NULL, 0, 0, 0, NULL, NULL, NULL }; + size_t i, j = 0, tag_len, tag_end; + const char *curtag = NULL; + + work.data = data; + + /* identification of the opening tag */ + if (size < 2 || data[0] != '<') + return 0; + + i = 1; + while (i < size && data[i] != '>' && data[i] != ' ') + i++; + + if (i < size) + curtag = hoedown_find_block_tag((char *)data + 1, (int)i - 1); + + /* handling of special cases */ + if (!curtag) { + + /* HTML comment, laxist form */ + if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') { + i = 5; + + while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) + i++; + + i++; + + if (i < size) + j = is_empty(data + i, size - i); + + if (j) { + work.size = i + j; + if (do_render && doc->md.blockhtml) + doc->md.blockhtml(ob, &work, &doc->data); + return work.size; + } + } + + /* HR, which is the only self-closing block tag considered */ + if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) { + i = 3; + while (i < size && data[i] != '>') + i++; + + if (i + 1 < size) { + i++; + j = is_empty(data + i, size - i); + if (j) { + work.size = i + j; + if (do_render && doc->md.blockhtml) + doc->md.blockhtml(ob, &work, &doc->data); + return work.size; + } + } + } + + /* no special case recognised */ + return 0; + } + + /* looking for a matching closing tag in strict mode */ + tag_len = strlen(curtag); + tag_end = htmlblock_find_end_strict(curtag, tag_len, doc, data, size); + + /* if not found, trying a second pass looking for indented match */ + /* but not if tag is "ins" or "del" (following original Markdown.pl) */ + if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) + tag_end = htmlblock_find_end(curtag, tag_len, doc, data, size); + + if (!tag_end) + return 0; + + /* the end of the block has been found */ + work.size = tag_end; + if (do_render && doc->md.blockhtml) + doc->md.blockhtml(ob, &work, &doc->data); + + return tag_end; +} + +static void +parse_table_row( + hoedown_buffer *ob, + hoedown_document *doc, + uint8_t *data, + size_t size, + size_t columns, + hoedown_table_flags *col_data, + hoedown_table_flags header_flag) +{ + size_t i = 0, col, len; + hoedown_buffer *row_work = 0; + + if (!doc->md.table_cell || !doc->md.table_row) + return; + + row_work = newbuf(doc, BUFFER_SPAN); + + if (i < size && data[i] == '|') + i++; + + for (col = 0; col < columns && i < size; ++col) { + size_t cell_start, cell_end; + hoedown_buffer *cell_work; + + cell_work = newbuf(doc, BUFFER_SPAN); + + while (i < size && _isspace(data[i])) + i++; + + cell_start = i; + + len = find_emph_char(data + i, size - i, '|'); + i += len ? len : size - i; + + cell_end = i - 1; + + while (cell_end > cell_start && _isspace(data[cell_end])) + cell_end--; + + parse_inline(cell_work, doc, data + cell_start, 1 + cell_end - cell_start); + doc->md.table_cell(row_work, cell_work, col_data[col] | header_flag, &doc->data); + + popbuf(doc, BUFFER_SPAN); + i++; + } + + for (; col < columns; ++col) { + hoedown_buffer empty_cell = { 0, 0, 0, 0, NULL, NULL, NULL }; + doc->md.table_cell(row_work, &empty_cell, col_data[col] | header_flag, &doc->data); + } + + doc->md.table_row(ob, row_work, &doc->data); + + popbuf(doc, BUFFER_SPAN); +} + +static size_t +parse_table_header( + hoedown_buffer *ob, + hoedown_document *doc, + uint8_t *data, + size_t size, + size_t *columns, + hoedown_table_flags **column_data) +{ + int pipes; + size_t i = 0, col, header_end, under_end; + + pipes = 0; + while (i < size && data[i] != '\n') + if (data[i++] == '|') + pipes++; + + if (i == size || pipes == 0) + return 0; + + header_end = i; + + while (header_end > 0 && _isspace(data[header_end - 1])) + header_end--; + + if (data[0] == '|') + pipes--; + + if (header_end && data[header_end - 1] == '|') + pipes--; + + if (pipes < 0) + return 0; + + *columns = pipes + 1; + *column_data = hoedown_calloc(*columns, sizeof(hoedown_table_flags)); + + /* Parse the header underline */ + i++; + if (i < size && data[i] == '|') + i++; + + under_end = i; + while (under_end < size && data[under_end] != '\n') + under_end++; + + for (col = 0; col < *columns && i < under_end; ++col) { + size_t dashes = 0; + + while (i < under_end && data[i] == ' ') + i++; + + if (data[i] == ':') { + i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_LEFT; + dashes++; + } + + while (i < under_end && data[i] == '-') { + i++; dashes++; + } + + if (i < under_end && data[i] == ':') { + i++; (*column_data)[col] |= HOEDOWN_TABLE_ALIGN_RIGHT; + dashes++; + } + + while (i < under_end && data[i] == ' ') + i++; + + if (i < under_end && data[i] != '|' && data[i] != '+') + break; + + if (dashes < 3) + break; + + i++; + } + + if (col < *columns) + return 0; + + parse_table_row( + ob, doc, data, + header_end, + *columns, + *column_data, + HOEDOWN_TABLE_HEADER + ); + + return under_end + 1; +} + +static size_t +parse_table( + hoedown_buffer *ob, + hoedown_document *doc, + uint8_t *data, + size_t size) +{ + size_t i; + + hoedown_buffer *work = 0; + hoedown_buffer *header_work = 0; + hoedown_buffer *body_work = 0; + + size_t columns; + hoedown_table_flags *col_data = NULL; + + work = newbuf(doc, BUFFER_BLOCK); + header_work = newbuf(doc, BUFFER_SPAN); + body_work = newbuf(doc, BUFFER_BLOCK); + + i = parse_table_header(header_work, doc, data, size, &columns, &col_data); + if (i > 0) { + + while (i < size) { + size_t row_start; + int pipes = 0; + + row_start = i; + + while (i < size && data[i] != '\n') + if (data[i++] == '|') + pipes++; + + if (pipes == 0 || i == size) { + i = row_start; + break; + } + + parse_table_row( + body_work, + doc, + data + row_start, + i - row_start, + columns, + col_data, 0 + ); + + i++; + } + + if (doc->md.table_header) + doc->md.table_header(work, header_work, &doc->data); + + if (doc->md.table_body) + doc->md.table_body(work, body_work, &doc->data); + + if (doc->md.table) + doc->md.table(ob, work, &doc->data); + } + + free(col_data); + popbuf(doc, BUFFER_SPAN); + popbuf(doc, BUFFER_BLOCK); + popbuf(doc, BUFFER_BLOCK); + return i; +} + +/* parse_block • parsing of one block, returning next uint8_t to parse */ +static void +parse_block(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t size) +{ + size_t beg, end, i; + uint8_t *txt_data; + beg = 0; + + if (doc->work_bufs[BUFFER_SPAN].size + + doc->work_bufs[BUFFER_BLOCK].size > doc->max_nesting) + return; + + while (beg < size) { + txt_data = data + beg; + end = size - beg; + + if (is_atxheader(doc, txt_data, end)) + beg += parse_atxheader(ob, doc, txt_data, end); + + else if (data[beg] == '<' && doc->md.blockhtml && + (i = parse_htmlblock(ob, doc, txt_data, end, 1)) != 0) + beg += i; + + else if ((i = is_empty(txt_data, end)) != 0) + beg += i; + + else if (is_hrule(txt_data, end)) { + if (doc->md.hrule) + doc->md.hrule(ob, &doc->data); + + while (beg < size && data[beg] != '\n') + beg++; + + beg++; + } + + else if ((doc->ext_flags & HOEDOWN_EXT_FENCED_CODE) != 0 && + (i = parse_fencedcode(ob, doc, txt_data, end)) != 0) + beg += i; + + else if ((doc->ext_flags & HOEDOWN_EXT_TABLES) != 0 && + (i = parse_table(ob, doc, txt_data, end)) != 0) + beg += i; + + else if (prefix_quote(txt_data, end)) + beg += parse_blockquote(ob, doc, txt_data, end); + + else if (!(doc->ext_flags & HOEDOWN_EXT_DISABLE_INDENTED_CODE) && prefix_code(txt_data, end)) + beg += parse_blockcode(ob, doc, txt_data, end); + + else if (prefix_uli(txt_data, end)) + beg += parse_list(ob, doc, txt_data, end, 0); + + else if (prefix_oli(txt_data, end)) + beg += parse_list(ob, doc, txt_data, end, HOEDOWN_LIST_ORDERED); + + else + beg += parse_paragraph(ob, doc, txt_data, end); + } +} + + + +/********************* + * REFERENCE PARSING * + *********************/ + +/* is_footnote • returns whether a line is a footnote definition or not */ +static int +is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, struct footnote_list *list) +{ + size_t i = 0; + hoedown_buffer *contents = 0; + size_t ind = 0; + int in_empty = 0; + size_t start = 0; + + size_t id_offset, id_end; + + /* up to 3 optional leading spaces */ + if (beg + 3 >= end) return 0; + if (data[beg] == ' ') { i = 1; + if (data[beg + 1] == ' ') { i = 2; + if (data[beg + 2] == ' ') { i = 3; + if (data[beg + 3] == ' ') return 0; } } } + i += beg; + + /* id part: caret followed by anything between brackets */ + if (data[i] != '[') return 0; + i++; + if (i >= end || data[i] != '^') return 0; + i++; + id_offset = i; + while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') + i++; + if (i >= end || data[i] != ']') return 0; + id_end = i; + + /* spacer: colon (space | tab)* newline? (space | tab)* */ + i++; + if (i >= end || data[i] != ':') return 0; + i++; + + /* getting content buffer */ + contents = hoedown_buffer_new(64); + + start = i; + + /* process lines similar to a list item */ + while (i < end) { + while (i < end && data[i] != '\n' && data[i] != '\r') i++; + + /* process an empty line */ + if (is_empty(data + start, i - start)) { + in_empty = 1; + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++; + } + start = i; + continue; + } + + /* calculating the indentation */ + ind = 0; + while (ind < 4 && start + ind < end && data[start + ind] == ' ') + ind++; + + /* joining only indented stuff after empty lines; + * note that now we only require 1 space of indentation + * to continue, just like lists */ + if (ind == 0) { + if (start == id_end + 2 && data[start] == '\t') {} + else break; + } + else if (in_empty) { + hoedown_buffer_putc(contents, '\n'); + } + + in_empty = 0; + + /* adding the line into the content buffer */ + hoedown_buffer_put(contents, data + start + ind, i - start - ind); + /* add carriage return */ + if (i < end) { + hoedown_buffer_putc(contents, '\n'); + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\n' && data[i - 1] == '\r') i++; + } + } + start = i; + } + + if (last) + *last = start; + + if (list) { + struct footnote_ref *ref; + ref = create_footnote_ref(list, data + id_offset, id_end - id_offset); + if (!ref) + return 0; + if (!add_footnote_ref(list, ref)) { + free_footnote_ref(ref); + return 0; + } + ref->contents = contents; + } + + return 1; +} + +/* is_ref • returns whether a line is a reference or not */ +static int +is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs) +{ +/* int n; */ + size_t i = 0; + size_t id_offset, id_end; + size_t link_offset, link_end; + size_t title_offset, title_end; + size_t line_end; + + /* up to 3 optional leading spaces */ + if (beg + 3 >= end) return 0; + if (data[beg] == ' ') { i = 1; + if (data[beg + 1] == ' ') { i = 2; + if (data[beg + 2] == ' ') { i = 3; + if (data[beg + 3] == ' ') return 0; } } } + i += beg; + + /* id part: anything but a newline between brackets */ + if (data[i] != '[') return 0; + i++; + id_offset = i; + while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') + i++; + if (i >= end || data[i] != ']') return 0; + id_end = i; + + /* spacer: colon (space | tab)* newline? (space | tab)* */ + i++; + if (i >= end || data[i] != ':') return 0; + i++; + while (i < end && data[i] == ' ') i++; + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; } + while (i < end && data[i] == ' ') i++; + if (i >= end) return 0; + + /* link: spacing-free sequence, optionally between angle brackets */ + if (data[i] == '<') + i++; + + link_offset = i; + + while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r') + i++; + + if (data[i - 1] == '>') link_end = i - 1; + else link_end = i; + + /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */ + while (i < end && data[i] == ' ') i++; + if (i < end && data[i] != '\n' && data[i] != '\r' + && data[i] != '\'' && data[i] != '"' && data[i] != '(') + return 0; + line_end = 0; + /* computing end-of-line */ + if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i; + if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') + line_end = i + 1; + + /* optional (space|tab)* spacer after a newline */ + if (line_end) { + i = line_end + 1; + while (i < end && data[i] == ' ') i++; } + + /* optional title: any non-newline sequence enclosed in '"() + alone on its line */ + title_offset = title_end = 0; + if (i + 1 < end + && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) { + i++; + title_offset = i; + /* looking for EOL */ + while (i < end && data[i] != '\n' && data[i] != '\r') i++; + if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') + title_end = i + 1; + else title_end = i; + /* stepping back */ + i -= 1; + while (i > title_offset && data[i] == ' ') + i -= 1; + if (i > title_offset + && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) { + line_end = title_end; + title_end = i; } } + + if (!line_end || link_end == link_offset) + return 0; /* garbage after the link empty link */ + + /* a valid ref has been found, filling-in return structures */ + if (last) + *last = line_end; + + if (refs) { + struct link_ref *ref; + + ref = add_link_ref(refs, data + id_offset, id_end - id_offset); + if (!ref) + return 0; + + ref->link = hoedown_buffer_new(link_end - link_offset); + hoedown_buffer_put(ref->link, data + link_offset, link_end - link_offset); + + if (title_end > title_offset) { + ref->title = hoedown_buffer_new(title_end - title_offset); + hoedown_buffer_put(ref->title, data + title_offset, title_end - title_offset); + } + } + + return 1; +} + +static void expand_tabs(hoedown_buffer *ob, const uint8_t *line, size_t size) +{ + /* This code makes two assumptions: + * - Input is valid UTF-8. (Any byte with top two bits 10 is skipped, + * whether or not it is a valid UTF-8 continuation byte.) + * - Input contains no combining characters. (Combining characters + * should be skipped but are not.) + */ + size_t i = 0, tab = 0; + + while (i < size) { + size_t org = i; + + while (i < size && line[i] != '\t') { + /* ignore UTF-8 continuation bytes */ + if ((line[i] & 0xc0) != 0x80) + tab++; + i++; + } + + if (i > org) + hoedown_buffer_put(ob, line + org, i - org); + + if (i >= size) + break; + + do { + hoedown_buffer_putc(ob, ' '); tab++; + } while (tab % 4); + + i++; + } +} + +/********************** + * EXPORTED FUNCTIONS * + **********************/ + +hoedown_document * +hoedown_document_new( + const hoedown_renderer *renderer, + hoedown_extensions extensions, + size_t max_nesting) +{ + hoedown_document *doc = NULL; + + assert(max_nesting > 0 && renderer); + + doc = hoedown_malloc(sizeof(hoedown_document)); + memcpy(&doc->md, renderer, sizeof(hoedown_renderer)); + + doc->data.opaque = renderer->opaque; + + hoedown_stack_init(&doc->work_bufs[BUFFER_BLOCK], 4); + hoedown_stack_init(&doc->work_bufs[BUFFER_SPAN], 8); + + memset(doc->active_char, 0x0, 256); + + if (extensions & HOEDOWN_EXT_UNDERLINE && doc->md.underline) { + doc->active_char['_'] = MD_CHAR_EMPHASIS; + } + + if (doc->md.emphasis || doc->md.double_emphasis || doc->md.triple_emphasis) { + doc->active_char['*'] = MD_CHAR_EMPHASIS; + doc->active_char['_'] = MD_CHAR_EMPHASIS; + if (extensions & HOEDOWN_EXT_STRIKETHROUGH) + doc->active_char['~'] = MD_CHAR_EMPHASIS; + if (extensions & HOEDOWN_EXT_HIGHLIGHT) + doc->active_char['='] = MD_CHAR_EMPHASIS; + } + + if (doc->md.codespan) + doc->active_char['`'] = MD_CHAR_CODESPAN; + + if (doc->md.linebreak) + doc->active_char['\n'] = MD_CHAR_LINEBREAK; + + if (doc->md.image || doc->md.link || doc->md.footnotes || doc->md.footnote_ref) + doc->active_char['['] = MD_CHAR_LINK; + + doc->active_char['<'] = MD_CHAR_LANGLE; + doc->active_char['\\'] = MD_CHAR_ESCAPE; + doc->active_char['&'] = MD_CHAR_ENTITY; + + if (extensions & HOEDOWN_EXT_AUTOLINK) { + doc->active_char[':'] = MD_CHAR_AUTOLINK_URL; + doc->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL; + doc->active_char['w'] = MD_CHAR_AUTOLINK_WWW; + } + + if (extensions & HOEDOWN_EXT_SUPERSCRIPT) + doc->active_char['^'] = MD_CHAR_SUPERSCRIPT; + + if (extensions & HOEDOWN_EXT_QUOTE) + doc->active_char['"'] = MD_CHAR_QUOTE; + + if (extensions & HOEDOWN_EXT_MATH) + doc->active_char['$'] = MD_CHAR_MATH; + + /* Extension data */ + doc->ext_flags = extensions; + doc->max_nesting = max_nesting; + doc->in_link_body = 0; + + return doc; +} + +void +hoedown_document_render(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + static const uint8_t UTF8_BOM[] = {0xEF, 0xBB, 0xBF}; + + hoedown_buffer *text; + size_t beg, end; + + int footnotes_enabled; + + text = hoedown_buffer_new(64); + + /* Preallocate enough space for our buffer to avoid expanding while copying */ + hoedown_buffer_grow(text, size); + + /* reset the references table */ + memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); + + footnotes_enabled = doc->ext_flags & HOEDOWN_EXT_FOOTNOTES; + + /* reset the footnotes lists */ + if (footnotes_enabled) { + memset(&doc->footnotes_found, 0x0, sizeof(doc->footnotes_found)); + memset(&doc->footnotes_used, 0x0, sizeof(doc->footnotes_used)); + } + + /* first pass: looking for references, copying everything else */ + beg = 0; + + /* Skip a possible UTF-8 BOM, even though the Unicode standard + * discourages having these in UTF-8 documents */ + if (size >= 3 && memcmp(data, UTF8_BOM, 3) == 0) + beg += 3; + + while (beg < size) /* iterating over lines */ + if (footnotes_enabled && is_footnote(data, beg, size, &end, &doc->footnotes_found)) + beg = end; + else if (is_ref(data, beg, size, &end, doc->refs)) + beg = end; + else { /* skipping to the next line */ + end = beg; + while (end < size && data[end] != '\n' && data[end] != '\r') + end++; + + /* adding the line body if present */ + if (end > beg) + expand_tabs(text, data + beg, end - beg); + + while (end < size && (data[end] == '\n' || data[end] == '\r')) { + /* add one \n per newline */ + if (data[end] == '\n' || (end + 1 < size && data[end + 1] != '\n')) + hoedown_buffer_putc(text, '\n'); + end++; + } + + beg = end; + } + + /* pre-grow the output buffer to minimize allocations */ + hoedown_buffer_grow(ob, text->size + (text->size >> 1)); + + /* second pass: actual rendering */ + if (doc->md.doc_header) + doc->md.doc_header(ob, 0, &doc->data); + + if (text->size) { + /* adding a final newline if not already present */ + if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') + hoedown_buffer_putc(text, '\n'); + + parse_block(ob, doc, text->data, text->size); + } + + /* footnotes */ + if (footnotes_enabled) + parse_footnote_list(ob, doc, &doc->footnotes_used); + + if (doc->md.doc_footer) + doc->md.doc_footer(ob, 0, &doc->data); + + /* clean-up */ + hoedown_buffer_free(text); + free_link_refs(doc->refs); + if (footnotes_enabled) { + free_footnote_list(&doc->footnotes_found, 1); + free_footnote_list(&doc->footnotes_used, 0); + } + + assert(doc->work_bufs[BUFFER_SPAN].size == 0); + assert(doc->work_bufs[BUFFER_BLOCK].size == 0); +} + +void +hoedown_document_render_inline(hoedown_document *doc, hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + size_t i = 0, mark; + hoedown_buffer *text = hoedown_buffer_new(64); + + /* reset the references table */ + memset(&doc->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); + + /* first pass: expand tabs and process newlines */ + hoedown_buffer_grow(text, size); + while (1) { + mark = i; + while (i < size && data[i] != '\n' && data[i] != '\r') + i++; + + expand_tabs(text, data + mark, i - mark); + + if (i >= size) + break; + + while (i < size && (data[i] == '\n' || data[i] == '\r')) { + /* add one \n per newline */ + if (data[i] == '\n' || (i + 1 < size && data[i + 1] != '\n')) + hoedown_buffer_putc(text, '\n'); + i++; + } + } + + /* second pass: actual rendering */ + hoedown_buffer_grow(ob, text->size + (text->size >> 1)); + + if (doc->md.doc_header) + doc->md.doc_header(ob, 1, &doc->data); + + parse_inline(ob, doc, text->data, text->size); + + if (doc->md.doc_footer) + doc->md.doc_footer(ob, 1, &doc->data); + + /* clean-up */ + hoedown_buffer_free(text); + + assert(doc->work_bufs[BUFFER_SPAN].size == 0); + assert(doc->work_bufs[BUFFER_BLOCK].size == 0); +} + +void +hoedown_document_free(hoedown_document *doc) +{ + size_t i; + + for (i = 0; i < (size_t)doc->work_bufs[BUFFER_SPAN].asize; ++i) + hoedown_buffer_free(doc->work_bufs[BUFFER_SPAN].item[i]); + + for (i = 0; i < (size_t)doc->work_bufs[BUFFER_BLOCK].asize; ++i) + hoedown_buffer_free(doc->work_bufs[BUFFER_BLOCK].item[i]); + + hoedown_stack_uninit(&doc->work_bufs[BUFFER_SPAN]); + hoedown_stack_uninit(&doc->work_bufs[BUFFER_BLOCK]); + + free(doc); +} diff --git a/libraries/hoedown/src/escape.c b/libraries/hoedown/src/escape.c new file mode 100644 index 00000000..b4a275ba --- /dev/null +++ b/libraries/hoedown/src/escape.c @@ -0,0 +1,188 @@ +#include "hoedown/escape.h" + +#include +#include +#include + + +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + + +/* + * The following characters will not be escaped: + * + * -_.+!*'(),%#@?=;:/,+&$ alphanum + * + * Note that this character set is the addition of: + * + * - The characters which are safe to be in an URL + * - The characters which are *not* safe to be in + * an URL because they are RESERVED characters. + * + * We assume (lazily) that any RESERVED char that + * appears inside an URL is actually meant to + * have its native function (i.e. as an URL + * component/separator) and hence needs no escaping. + * + * There are two exceptions: the chacters & (amp) + * and ' (single quote) do not appear in the table. + * They are meant to appear in the URL as components, + * yet they require special HTML-entity escaping + * to generate valid HTML markup. + * + * All other characters will be escaped to %XX. + * + */ +static const uint8_t HREF_SAFE[UINT8_MAX+1] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void +hoedown_escape_href(hoedown_buffer *ob, const uint8_t *data, size_t size) +{ + static const char hex_chars[] = "0123456789ABCDEF"; + size_t i = 0, mark; + char hex_str[3]; + + hex_str[0] = '%'; + + while (i < size) { + mark = i; + while (i < size && HREF_SAFE[data[i]]) i++; + + /* Optimization for cases where there's nothing to escape */ + if (mark == 0 && i >= size) { + hoedown_buffer_put(ob, data, size); + return; + } + + if (likely(i > mark)) { + hoedown_buffer_put(ob, data + mark, i - mark); + } + + /* escaping */ + if (i >= size) + break; + + switch (data[i]) { + /* amp appears all the time in URLs, but needs + * HTML-entity escaping to be inside an href */ + case '&': + HOEDOWN_BUFPUTSL(ob, "&"); + break; + + /* the single quote is a valid URL character + * according to the standard; it needs HTML + * entity escaping too */ + case '\'': + HOEDOWN_BUFPUTSL(ob, "'"); + break; + + /* the space can be escaped to %20 or a plus + * sign. we're going with the generic escape + * for now. the plus thing is more commonly seen + * when building GET strings */ +#if 0 + case ' ': + hoedown_buffer_putc(ob, '+'); + break; +#endif + + /* every other character goes with a %XX escaping */ + default: + hex_str[1] = hex_chars[(data[i] >> 4) & 0xF]; + hex_str[2] = hex_chars[data[i] & 0xF]; + hoedown_buffer_put(ob, (uint8_t *)hex_str, 3); + } + + i++; + } +} + + +/** + * According to the OWASP rules: + * + * & --> & + * < --> < + * > --> > + * " --> " + * ' --> ' ' is not recommended + * / --> / forward slash is included as it helps end an HTML entity + * + */ +static const uint8_t HTML_ESCAPE_TABLE[UINT8_MAX+1] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const char *HTML_ESCAPES[] = { + "", + """, + "&", + "'", + "/", + "<", + ">" +}; + +void +hoedown_escape_html(hoedown_buffer *ob, const uint8_t *data, size_t size, int secure) +{ + size_t i = 0, mark; + + while (1) { + mark = i; + while (i < size && HTML_ESCAPE_TABLE[data[i]] == 0) i++; + + /* Optimization for cases where there's nothing to escape */ + if (mark == 0 && i >= size) { + hoedown_buffer_put(ob, data, size); + return; + } + + if (likely(i > mark)) + hoedown_buffer_put(ob, data + mark, i - mark); + + if (i >= size) break; + + /* The forward slash is only escaped in secure mode */ + if (!secure && data[i] == '/') { + hoedown_buffer_putc(ob, '/'); + } else { + hoedown_buffer_puts(ob, HTML_ESCAPES[HTML_ESCAPE_TABLE[data[i]]]); + } + + i++; + } +} diff --git a/libraries/hoedown/src/html.c b/libraries/hoedown/src/html.c new file mode 100644 index 00000000..4b18d804 --- /dev/null +++ b/libraries/hoedown/src/html.c @@ -0,0 +1,754 @@ +#include "hoedown/html.h" + +#include +#include +#include +#include + +#include "hoedown/escape.h" + +#define USE_XHTML(opt) (opt->flags & HOEDOWN_HTML_USE_XHTML) + +hoedown_html_tag +hoedown_html_is_tag(const uint8_t *data, size_t size, const char *tagname) +{ + size_t i; + int closed = 0; + + if (size < 3 || data[0] != '<') + return HOEDOWN_HTML_TAG_NONE; + + i = 1; + + if (data[i] == '/') { + closed = 1; + i++; + } + + for (; i < size; ++i, ++tagname) { + if (*tagname == 0) + break; + + if (data[i] != *tagname) + return HOEDOWN_HTML_TAG_NONE; + } + + if (i == size) + return HOEDOWN_HTML_TAG_NONE; + + if (isspace(data[i]) || data[i] == '>') + return closed ? HOEDOWN_HTML_TAG_CLOSE : HOEDOWN_HTML_TAG_OPEN; + + return HOEDOWN_HTML_TAG_NONE; +} + +static void escape_html(hoedown_buffer *ob, const uint8_t *source, size_t length) +{ + hoedown_escape_html(ob, source, length, 0); +} + +static void escape_href(hoedown_buffer *ob, const uint8_t *source, size_t length) +{ + hoedown_escape_href(ob, source, length); +} + +/******************** + * GENERIC RENDERER * + ********************/ +static int +rndr_autolink(hoedown_buffer *ob, const hoedown_buffer *link, hoedown_autolink_type type, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (!link || !link->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, "data, link->size); + + if (state->link_attributes) { + hoedown_buffer_putc(ob, '\"'); + state->link_attributes(ob, link, data); + hoedown_buffer_putc(ob, '>'); + } else { + HOEDOWN_BUFPUTSL(ob, "\">"); + } + + /* + * Pretty printing: if we get an email address as + * an actual URI, e.g. `mailto:foo@bar.com`, we don't + * want to print the `mailto:` prefix + */ + if (hoedown_buffer_prefix(link, "mailto:") == 0) { + escape_html(ob, link->data + 7, link->size - 7); + } else { + escape_html(ob, link->data, link->size); + } + + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static void +rndr_blockcode(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + + if (lang) { + HOEDOWN_BUFPUTSL(ob, "
    data, lang->size);
    +		HOEDOWN_BUFPUTSL(ob, "\">");
    +	} else {
    +		HOEDOWN_BUFPUTSL(ob, "
    ");
    +	}
    +
    +	if (text)
    +		escape_html(ob, text->data, text->size);
    +
    +	HOEDOWN_BUFPUTSL(ob, "
    \n"); +} + +static void +rndr_blockquote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "
    \n"); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "
    \n"); +} + +static int +rndr_codespan(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) +{ + HOEDOWN_BUFPUTSL(ob, ""); + if (text) escape_html(ob, text->data, text->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_strikethrough(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_double_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) return 0; + HOEDOWN_BUFPUTSL(ob, ""); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_underline(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_highlight(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_quote(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) + return 0; + + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_linebreak(hoedown_buffer *ob, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); + return 1; +} + +static void +rndr_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (ob->size) + hoedown_buffer_putc(ob, '\n'); + + if (level <= state->toc_data.nesting_level) + hoedown_buffer_printf(ob, "", level, state->toc_data.header_count++); + else + hoedown_buffer_printf(ob, "", level); + + if (content) hoedown_buffer_put(ob, content->data, content->size); + hoedown_buffer_printf(ob, "\n", level); +} + +static int +rndr_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + HOEDOWN_BUFPUTSL(ob, "size) + escape_href(ob, link->data, link->size); + + if (title && title->size) { + HOEDOWN_BUFPUTSL(ob, "\" title=\""); + escape_html(ob, title->data, title->size); + } + + if (state->link_attributes) { + hoedown_buffer_putc(ob, '\"'); + state->link_attributes(ob, link, data); + hoedown_buffer_putc(ob, '>'); + } else { + HOEDOWN_BUFPUTSL(ob, "\">"); + } + + if (content && content->size) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_list(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "
      \n" : "
        \n"), 5); + if (content) hoedown_buffer_put(ob, content->data, content->size); + hoedown_buffer_put(ob, (const uint8_t *)(flags & HOEDOWN_LIST_ORDERED ? "
    \n" : "\n"), 6); +} + +static void +rndr_listitem(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_list_flags flags, const hoedown_renderer_data *data) +{ + HOEDOWN_BUFPUTSL(ob, "
  • "); + if (content) { + size_t size = content->size; + while (size && content->data[size - 1] == '\n') + size--; + + hoedown_buffer_put(ob, content->data, size); + } + HOEDOWN_BUFPUTSL(ob, "
  • \n"); +} + +static void +rndr_paragraph(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + size_t i = 0; + + if (ob->size) hoedown_buffer_putc(ob, '\n'); + + if (!content || !content->size) + return; + + while (i < content->size && isspace(content->data[i])) i++; + + if (i == content->size) + return; + + HOEDOWN_BUFPUTSL(ob, "

    "); + if (state->flags & HOEDOWN_HTML_HARD_WRAP) { + size_t org; + while (i < content->size) { + org = i; + while (i < content->size && content->data[i] != '\n') + i++; + + if (i > org) + hoedown_buffer_put(ob, content->data + org, i - org); + + /* + * do not insert a line break if this newline + * is the last character on the paragraph + */ + if (i >= content->size - 1) + break; + + rndr_linebreak(ob, data); + i++; + } + } else { + hoedown_buffer_put(ob, content->data + i, content->size - i); + } + HOEDOWN_BUFPUTSL(ob, "

    \n"); +} + +static void +rndr_raw_block(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) +{ + size_t org, sz; + + if (!text) + return; + + /* FIXME: Do we *really* need to trim the HTML? How does that make a difference? */ + sz = text->size; + while (sz > 0 && text->data[sz - 1] == '\n') + sz--; + + org = 0; + while (org < sz && text->data[org] == '\n') + org++; + + if (org >= sz) + return; + + if (ob->size) + hoedown_buffer_putc(ob, '\n'); + + hoedown_buffer_put(ob, text->data + org, sz - org); + hoedown_buffer_putc(ob, '\n'); +} + +static int +rndr_triple_emphasis(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) return 0; + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_hrule(hoedown_buffer *ob, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + if (ob->size) hoedown_buffer_putc(ob, '\n'); + hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); +} + +static int +rndr_image(hoedown_buffer *ob, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_buffer *alt, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + if (!link || !link->size) return 0; + + HOEDOWN_BUFPUTSL(ob, "data, link->size); + HOEDOWN_BUFPUTSL(ob, "\" alt=\""); + + if (alt && alt->size) + escape_html(ob, alt->data, alt->size); + + if (title && title->size) { + HOEDOWN_BUFPUTSL(ob, "\" title=\""); + escape_html(ob, title->data, title->size); } + + hoedown_buffer_puts(ob, USE_XHTML(state) ? "\"/>" : "\">"); + return 1; +} + +static int +rndr_raw_html(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + /* ESCAPE overrides SKIP_HTML. It doesn't look to see if + * there are any valid tags, just escapes all of them. */ + if((state->flags & HOEDOWN_HTML_ESCAPE) != 0) { + escape_html(ob, text->data, text->size); + return 1; + } + + if ((state->flags & HOEDOWN_HTML_SKIP_HTML) != 0) + return 1; + + hoedown_buffer_put(ob, text->data, text->size); + return 1; +} + +static void +rndr_table(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "\n"); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "
    \n"); +} + +static void +rndr_table_header(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "\n"); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); +} + +static void +rndr_table_body(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "\n"); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); +} + +static void +rndr_tablerow(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + HOEDOWN_BUFPUTSL(ob, "\n"); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); +} + +static void +rndr_tablecell(hoedown_buffer *ob, const hoedown_buffer *content, hoedown_table_flags flags, const hoedown_renderer_data *data) +{ + if (flags & HOEDOWN_TABLE_HEADER) { + HOEDOWN_BUFPUTSL(ob, ""); + break; + + case HOEDOWN_TABLE_ALIGN_LEFT: + HOEDOWN_BUFPUTSL(ob, " style=\"text-align: left\">"); + break; + + case HOEDOWN_TABLE_ALIGN_RIGHT: + HOEDOWN_BUFPUTSL(ob, " style=\"text-align: right\">"); + break; + + default: + HOEDOWN_BUFPUTSL(ob, ">"); + } + + if (content) + hoedown_buffer_put(ob, content->data, content->size); + + if (flags & HOEDOWN_TABLE_HEADER) { + HOEDOWN_BUFPUTSL(ob, "\n"); + } else { + HOEDOWN_BUFPUTSL(ob, "\n"); + } +} + +static int +rndr_superscript(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (!content || !content->size) return 0; + HOEDOWN_BUFPUTSL(ob, ""); + hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_normal_text(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + if (content) + escape_html(ob, content->data, content->size); +} + +static void +rndr_footnotes(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (ob->size) hoedown_buffer_putc(ob, '\n'); + HOEDOWN_BUFPUTSL(ob, "
    \n"); + hoedown_buffer_puts(ob, USE_XHTML(state) ? "
    \n" : "
    \n"); + HOEDOWN_BUFPUTSL(ob, "
      \n"); + + if (content) hoedown_buffer_put(ob, content->data, content->size); + + HOEDOWN_BUFPUTSL(ob, "\n
    \n
    \n"); +} + +static void +rndr_footnote_def(hoedown_buffer *ob, const hoedown_buffer *content, unsigned int num, const hoedown_renderer_data *data) +{ + size_t i = 0; + int pfound = 0; + + /* insert anchor at the end of first paragraph block */ + if (content) { + while ((i+3) < content->size) { + if (content->data[i++] != '<') continue; + if (content->data[i++] != '/') continue; + if (content->data[i++] != 'p' && content->data[i] != 'P') continue; + if (content->data[i] != '>') continue; + i -= 3; + pfound = 1; + break; + } + } + + hoedown_buffer_printf(ob, "\n
  • \n", num); + if (pfound) { + hoedown_buffer_put(ob, content->data, i); + hoedown_buffer_printf(ob, " ", num); + hoedown_buffer_put(ob, content->data + i, content->size - i); + } else if (content) { + hoedown_buffer_put(ob, content->data, content->size); + } + HOEDOWN_BUFPUTSL(ob, "
  • \n"); +} + +static int +rndr_footnote_ref(hoedown_buffer *ob, unsigned int num, const hoedown_renderer_data *data) +{ + hoedown_buffer_printf(ob, "%d", num, num, num); + return 1; +} + +static int +rndr_math(hoedown_buffer *ob, const hoedown_buffer *text, int displaymode, const hoedown_renderer_data *data) +{ + hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\[" : "\\("), 2); + escape_html(ob, text->data, text->size); + hoedown_buffer_put(ob, (const uint8_t *)(displaymode ? "\\]" : "\\)"), 2); + return 1; +} + +static void +toc_header(hoedown_buffer *ob, const hoedown_buffer *content, int level, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state = data->opaque; + + if (level <= state->toc_data.nesting_level) { + /* set the level offset if this is the first header + * we're parsing for the document */ + if (state->toc_data.current_level == 0) + state->toc_data.level_offset = level - 1; + + level -= state->toc_data.level_offset; + + if (level > state->toc_data.current_level) { + while (level > state->toc_data.current_level) { + HOEDOWN_BUFPUTSL(ob, "
      \n
    • \n"); + state->toc_data.current_level++; + } + } else if (level < state->toc_data.current_level) { + HOEDOWN_BUFPUTSL(ob, "
    • \n"); + while (level < state->toc_data.current_level) { + HOEDOWN_BUFPUTSL(ob, "
    \n
  • \n"); + state->toc_data.current_level--; + } + HOEDOWN_BUFPUTSL(ob,"
  • \n"); + } else { + HOEDOWN_BUFPUTSL(ob,"
  • \n
  • \n"); + } + + hoedown_buffer_printf(ob, "", state->toc_data.header_count++); + if (content) hoedown_buffer_put(ob, content->data, content->size); + HOEDOWN_BUFPUTSL(ob, "\n"); + } +} + +static int +toc_link(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_buffer *link, const hoedown_buffer *title, const hoedown_renderer_data *data) +{ + if (content && content->size) hoedown_buffer_put(ob, content->data, content->size); + return 1; +} + +static void +toc_finalize(hoedown_buffer *ob, int inline_render, const hoedown_renderer_data *data) +{ + hoedown_html_renderer_state *state; + + if (inline_render) + return; + + state = data->opaque; + + while (state->toc_data.current_level > 0) { + HOEDOWN_BUFPUTSL(ob, "
  • \n\n"); + state->toc_data.current_level--; + } + + state->toc_data.header_count = 0; +} + +hoedown_renderer * +hoedown_html_toc_renderer_new(int nesting_level) +{ + static const hoedown_renderer cb_default = { + NULL, + + NULL, + NULL, + toc_header, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + NULL, + rndr_codespan, + rndr_double_emphasis, + rndr_emphasis, + rndr_underline, + rndr_highlight, + rndr_quote, + NULL, + NULL, + toc_link, + rndr_triple_emphasis, + rndr_strikethrough, + rndr_superscript, + NULL, + NULL, + NULL, + + NULL, + rndr_normal_text, + + NULL, + toc_finalize + }; + + hoedown_html_renderer_state *state; + hoedown_renderer *renderer; + + /* Prepare the state pointer */ + state = hoedown_malloc(sizeof(hoedown_html_renderer_state)); + memset(state, 0x0, sizeof(hoedown_html_renderer_state)); + + state->toc_data.nesting_level = nesting_level; + + /* Prepare the renderer */ + renderer = hoedown_malloc(sizeof(hoedown_renderer)); + memcpy(renderer, &cb_default, sizeof(hoedown_renderer)); + + renderer->opaque = state; + return renderer; +} + +hoedown_renderer * +hoedown_html_renderer_new(hoedown_html_flags render_flags, int nesting_level) +{ + static const hoedown_renderer cb_default = { + NULL, + + rndr_blockcode, + rndr_blockquote, + rndr_header, + rndr_hrule, + rndr_list, + rndr_listitem, + rndr_paragraph, + rndr_table, + rndr_table_header, + rndr_table_body, + rndr_tablerow, + rndr_tablecell, + rndr_footnotes, + rndr_footnote_def, + rndr_raw_block, + + rndr_autolink, + rndr_codespan, + rndr_double_emphasis, + rndr_emphasis, + rndr_underline, + rndr_highlight, + rndr_quote, + rndr_image, + rndr_linebreak, + rndr_link, + rndr_triple_emphasis, + rndr_strikethrough, + rndr_superscript, + rndr_footnote_ref, + rndr_math, + rndr_raw_html, + + NULL, + rndr_normal_text, + + NULL, + NULL + }; + + hoedown_html_renderer_state *state; + hoedown_renderer *renderer; + + /* Prepare the state pointer */ + state = hoedown_malloc(sizeof(hoedown_html_renderer_state)); + memset(state, 0x0, sizeof(hoedown_html_renderer_state)); + + state->flags = render_flags; + state->toc_data.nesting_level = nesting_level; + + /* Prepare the renderer */ + renderer = hoedown_malloc(sizeof(hoedown_renderer)); + memcpy(renderer, &cb_default, sizeof(hoedown_renderer)); + + if (render_flags & HOEDOWN_HTML_SKIP_HTML || render_flags & HOEDOWN_HTML_ESCAPE) + renderer->blockhtml = NULL; + + renderer->opaque = state; + return renderer; +} + +void +hoedown_html_renderer_free(hoedown_renderer *renderer) +{ + free(renderer->opaque); + free(renderer); +} diff --git a/libraries/hoedown/src/html_blocks.c b/libraries/hoedown/src/html_blocks.c new file mode 100644 index 00000000..f5e9dce9 --- /dev/null +++ b/libraries/hoedown/src/html_blocks.c @@ -0,0 +1,240 @@ +/* ANSI-C code produced by gperf version 3.0.3 */ +/* Command-line: gperf -L ANSI-C -N hoedown_find_block_tag -c -C -E -S 1 --ignore-case -m100 html_block_names.gperf */ +/* Computed positions: -k'1-2' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +/* maximum key range = 24, duplicates = 0 */ + +#ifndef GPERF_DOWNCASE +#define GPERF_DOWNCASE 1 +static unsigned char gperf_downcase[256] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 + }; +#endif + +#ifndef GPERF_CASE_STRNCMP +#define GPERF_CASE_STRNCMP 1 +static int +gperf_case_strncmp (register const char *s1, register const char *s2, register unsigned int n) +{ + for (; n > 0;) + { + unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; + unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; + if (c1 != 0 && c1 == c2) + { + n--; + continue; + } + return (int)c1 - (int)c2; + } + return 0; +} +#endif + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 22, 21, 19, 18, 16, 0, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 1, 25, 0, 25, + 1, 0, 0, 13, 0, 25, 25, 11, 2, 1, + 0, 25, 25, 5, 0, 2, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 1, 25, + 0, 25, 1, 0, 0, 13, 0, 25, 25, 11, + 2, 1, 0, 25, 25, 5, 0, 2, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25 + }; + register int hval = (int)len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[1]+1]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +#ifdef __GNUC__ +__inline +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const char * +hoedown_find_block_tag (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 24, + MIN_WORD_LENGTH = 1, + MAX_WORD_LENGTH = 10, + MIN_HASH_VALUE = 1, + MAX_HASH_VALUE = 24 + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE) + { + register const char *resword; + + switch (key - 1) + { + case 0: + resword = "p"; + goto compare; + case 1: + resword = "h6"; + goto compare; + case 2: + resword = "div"; + goto compare; + case 3: + resword = "del"; + goto compare; + case 4: + resword = "form"; + goto compare; + case 5: + resword = "table"; + goto compare; + case 6: + resword = "figure"; + goto compare; + case 7: + resword = "pre"; + goto compare; + case 8: + resword = "fieldset"; + goto compare; + case 9: + resword = "noscript"; + goto compare; + case 10: + resword = "script"; + goto compare; + case 11: + resword = "style"; + goto compare; + case 12: + resword = "dl"; + goto compare; + case 13: + resword = "ol"; + goto compare; + case 14: + resword = "ul"; + goto compare; + case 15: + resword = "math"; + goto compare; + case 16: + resword = "ins"; + goto compare; + case 17: + resword = "h5"; + goto compare; + case 18: + resword = "iframe"; + goto compare; + case 19: + resword = "h4"; + goto compare; + case 20: + resword = "h3"; + goto compare; + case 21: + resword = "blockquote"; + goto compare; + case 22: + resword = "h2"; + goto compare; + case 23: + resword = "h1"; + goto compare; + } + return 0; + compare: + if ((((unsigned char)*str ^ (unsigned char)*resword) & ~32) == 0 && !gperf_case_strncmp (str, resword, len) && resword[len] == '\0') + return resword; + } + } + return 0; +} diff --git a/libraries/hoedown/src/html_smartypants.c b/libraries/hoedown/src/html_smartypants.c new file mode 100644 index 00000000..e24b6bf0 --- /dev/null +++ b/libraries/hoedown/src/html_smartypants.c @@ -0,0 +1,435 @@ +#include "hoedown/html.h" + +#include +#include +#include +#include + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +struct smartypants_data { + int in_squote; + int in_dquote; +}; + +static size_t smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); + +static size_t (*smartypants_cb_ptrs[]) + (hoedown_buffer *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) = +{ + NULL, /* 0 */ + smartypants_cb__dash, /* 1 */ + smartypants_cb__parens, /* 2 */ + smartypants_cb__squote, /* 3 */ + smartypants_cb__dquote, /* 4 */ + smartypants_cb__amp, /* 5 */ + smartypants_cb__period, /* 6 */ + smartypants_cb__number, /* 7 */ + smartypants_cb__ltag, /* 8 */ + smartypants_cb__backtick, /* 9 */ + smartypants_cb__escape, /* 10 */ +}; + +static const uint8_t smartypants_cb_chars[UINT8_MAX+1] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0, + 0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static int +word_boundary(uint8_t c) +{ + return c == 0 || isspace(c) || ispunct(c); +} + +/* + If 'text' begins with any kind of single quote (e.g. "'" or "'" etc.), + returns the length of the sequence of characters that makes up the single- + quote. Otherwise, returns zero. +*/ +static size_t +squote_len(const uint8_t *text, size_t size) +{ + static char* single_quote_list[] = { "'", "'", "'", "'", NULL }; + char** p; + + for (p = single_quote_list; *p; ++p) { + size_t len = strlen(*p); + if (size >= len && memcmp(text, *p, len) == 0) { + return len; + } + } + + return 0; +} + +/* Converts " or ' at very beginning or end of a word to left or right quote */ +static int +smartypants_quotes(hoedown_buffer *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open) +{ + char ent[8]; + + if (*is_open && !word_boundary(next_char)) + return 0; + + if (!(*is_open) && !word_boundary(previous_char)) + return 0; + + snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote); + *is_open = !(*is_open); + hoedown_buffer_puts(ob, ent); + return 1; +} + +/* + Converts ' to left or right single quote; but the initial ' might be in + different forms, e.g. ' or ' or '. + 'squote_text' points to the original single quote, and 'squote_size' is its length. + 'text' points at the last character of the single-quote, e.g. ' or ; +*/ +static size_t +smartypants_squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size, + const uint8_t *squote_text, size_t squote_size) +{ + if (size >= 2) { + uint8_t t1 = tolower(text[1]); + size_t next_squote_len = squote_len(text+1, size-1); + + /* convert '' to “ or ” */ + if (next_squote_len > 0) { + uint8_t next_char = (size > 1+next_squote_len) ? text[1+next_squote_len] : 0; + if (smartypants_quotes(ob, previous_char, next_char, 'd', &smrt->in_dquote)) + return next_squote_len; + } + + /* Tom's, isn't, I'm, I'd */ + if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && + (size == 3 || word_boundary(text[2]))) { + HOEDOWN_BUFPUTSL(ob, "’"); + return 0; + } + + /* you're, you'll, you've */ + if (size >= 3) { + uint8_t t2 = tolower(text[2]); + + if (((t1 == 'r' && t2 == 'e') || + (t1 == 'l' && t2 == 'l') || + (t1 == 'v' && t2 == 'e')) && + (size == 4 || word_boundary(text[3]))) { + HOEDOWN_BUFPUTSL(ob, "’"); + return 0; + } + } + } + + if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote)) + return 0; + + hoedown_buffer_put(ob, squote_text, squote_size); + return 0; +} + +/* Converts ' to left or right single quote. */ +static size_t +smartypants_cb__squote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + return smartypants_squote(ob, smrt, previous_char, text, size, text, 1); +} + +/* Converts (c), (r), (tm) */ +static size_t +smartypants_cb__parens(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3) { + uint8_t t1 = tolower(text[1]); + uint8_t t2 = tolower(text[2]); + + if (t1 == 'c' && t2 == ')') { + HOEDOWN_BUFPUTSL(ob, "©"); + return 2; + } + + if (t1 == 'r' && t2 == ')') { + HOEDOWN_BUFPUTSL(ob, "®"); + return 2; + } + + if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') { + HOEDOWN_BUFPUTSL(ob, "™"); + return 3; + } + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts "--" to em-dash, etc. */ +static size_t +smartypants_cb__dash(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3 && text[1] == '-' && text[2] == '-') { + HOEDOWN_BUFPUTSL(ob, "—"); + return 2; + } + + if (size >= 2 && text[1] == '-') { + HOEDOWN_BUFPUTSL(ob, "–"); + return 1; + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts " etc. */ +static size_t +smartypants_cb__amp(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + size_t len; + if (size >= 6 && memcmp(text, """, 6) == 0) { + if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote)) + return 5; + } + + len = squote_len(text, size); + if (len > 0) { + return (len-1) + smartypants_squote(ob, smrt, previous_char, text+(len-1), size-(len-1), text, len); + } + + if (size >= 4 && memcmp(text, "�", 4) == 0) + return 3; + + hoedown_buffer_putc(ob, '&'); + return 0; +} + +/* Converts "..." to ellipsis */ +static size_t +smartypants_cb__period(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3 && text[1] == '.' && text[2] == '.') { + HOEDOWN_BUFPUTSL(ob, "…"); + return 2; + } + + if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') { + HOEDOWN_BUFPUTSL(ob, "…"); + return 4; + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts `` to opening double quote */ +static size_t +smartypants_cb__backtick(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 2 && text[1] == '`') { + if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) + return 1; + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts 1/2, 1/4, 3/4 */ +static size_t +smartypants_cb__number(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (word_boundary(previous_char) && size >= 3) { + if (text[0] == '1' && text[1] == '/' && text[2] == '2') { + if (size == 3 || word_boundary(text[3])) { + HOEDOWN_BUFPUTSL(ob, "½"); + return 2; + } + } + + if (text[0] == '1' && text[1] == '/' && text[2] == '4') { + if (size == 3 || word_boundary(text[3]) || + (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) { + HOEDOWN_BUFPUTSL(ob, "¼"); + return 2; + } + } + + if (text[0] == '3' && text[1] == '/' && text[2] == '4') { + if (size == 3 || word_boundary(text[3]) || + (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) { + HOEDOWN_BUFPUTSL(ob, "¾"); + return 2; + } + } + } + + hoedown_buffer_putc(ob, text[0]); + return 0; +} + +/* Converts " to left or right double quote */ +static size_t +smartypants_cb__dquote(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote)) + HOEDOWN_BUFPUTSL(ob, """); + + return 0; +} + +static size_t +smartypants_cb__ltag(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + static const char *skip_tags[] = { + "pre", "code", "var", "samp", "kbd", "math", "script", "style" + }; + static const size_t skip_tags_count = 8; + + size_t tag, i = 0; + + /* This is a comment. Copy everything verbatim until --> or EOF is seen. */ + if (i + 4 < size && memcmp(text, "", 3) != 0) + i++; + i += 3; + hoedown_buffer_put(ob, text, i + 1); + return i; + } + + while (i < size && text[i] != '>') + i++; + + for (tag = 0; tag < skip_tags_count; ++tag) { + if (hoedown_html_is_tag(text, size, skip_tags[tag]) == HOEDOWN_HTML_TAG_OPEN) + break; + } + + if (tag < skip_tags_count) { + for (;;) { + while (i < size && text[i] != '<') + i++; + + if (i == size) + break; + + if (hoedown_html_is_tag(text + i, size - i, skip_tags[tag]) == HOEDOWN_HTML_TAG_CLOSE) + break; + + i++; + } + + while (i < size && text[i] != '>') + i++; + } + + hoedown_buffer_put(ob, text, i + 1); + return i; +} + +static size_t +smartypants_cb__escape(hoedown_buffer *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size < 2) + return 0; + + switch (text[1]) { + case '\\': + case '"': + case '\'': + case '.': + case '-': + case '`': + hoedown_buffer_putc(ob, text[1]); + return 1; + + default: + hoedown_buffer_putc(ob, '\\'); + return 0; + } +} + +#if 0 +static struct { + uint8_t c0; + const uint8_t *pattern; + const uint8_t *entity; + int skip; +} smartypants_subs[] = { + { '\'', "'s>", "’", 0 }, + { '\'', "'t>", "’", 0 }, + { '\'', "'re>", "’", 0 }, + { '\'', "'ll>", "’", 0 }, + { '\'', "'ve>", "’", 0 }, + { '\'', "'m>", "’", 0 }, + { '\'', "'d>", "’", 0 }, + { '-', "--", "—", 1 }, + { '-', "<->", "–", 0 }, + { '.', "...", "…", 2 }, + { '.', ". . .", "…", 4 }, + { '(', "(c)", "©", 2 }, + { '(', "(r)", "®", 2 }, + { '(', "(tm)", "™", 3 }, + { '3', "<3/4>", "¾", 2 }, + { '3', "<3/4ths>", "¾", 2 }, + { '1', "<1/2>", "½", 2 }, + { '1', "<1/4>", "¼", 2 }, + { '1', "<1/4th>", "¼", 2 }, + { '&', "�", 0, 3 }, +}; +#endif + +void +hoedown_html_smartypants(hoedown_buffer *ob, const uint8_t *text, size_t size) +{ + size_t i; + struct smartypants_data smrt = {0, 0}; + + if (!text) + return; + + hoedown_buffer_grow(ob, size); + + for (i = 0; i < size; ++i) { + size_t org; + uint8_t action = 0; + + org = i; + while (i < size && (action = smartypants_cb_chars[text[i]]) == 0) + i++; + + if (i > org) + hoedown_buffer_put(ob, text + org, i - org); + + if (i < size) { + i += smartypants_cb_ptrs[(int)action] + (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i); + } + } +} diff --git a/libraries/hoedown/src/stack.c b/libraries/hoedown/src/stack.c new file mode 100644 index 00000000..46ead232 --- /dev/null +++ b/libraries/hoedown/src/stack.c @@ -0,0 +1,79 @@ +#include "hoedown/stack.h" + +#include "hoedown/buffer.h" + +#include +#include +#include + +void +hoedown_stack_init(hoedown_stack *st, size_t initial_size) +{ + assert(st); + + st->item = NULL; + st->size = st->asize = 0; + + if (!initial_size) + initial_size = 8; + + hoedown_stack_grow(st, initial_size); +} + +void +hoedown_stack_uninit(hoedown_stack *st) +{ + assert(st); + + free(st->item); +} + +void +hoedown_stack_grow(hoedown_stack *st, size_t neosz) +{ + assert(st); + + if (st->asize >= neosz) + return; + + st->item = hoedown_realloc(st->item, neosz * sizeof(void *)); + memset(st->item + st->asize, 0x0, (neosz - st->asize) * sizeof(void *)); + + st->asize = neosz; + + if (st->size > neosz) + st->size = neosz; +} + +void +hoedown_stack_push(hoedown_stack *st, void *item) +{ + assert(st); + + if (st->size >= st->asize) + hoedown_stack_grow(st, st->size * 2); + + st->item[st->size++] = item; +} + +void * +hoedown_stack_pop(hoedown_stack *st) +{ + assert(st); + + if (!st->size) + return NULL; + + return st->item[--st->size]; +} + +void * +hoedown_stack_top(const hoedown_stack *st) +{ + assert(st); + + if (!st->size) + return NULL; + + return st->item[st->size - 1]; +} diff --git a/libraries/hoedown/src/version.c b/libraries/hoedown/src/version.c new file mode 100644 index 00000000..625ed196 --- /dev/null +++ b/libraries/hoedown/src/version.c @@ -0,0 +1,9 @@ +#include "hoedown/version.h" + +void +hoedown_version(int *major, int *minor, int *revision) +{ + *major = HOEDOWN_VERSION_MAJOR; + *minor = HOEDOWN_VERSION_MINOR; + *revision = HOEDOWN_VERSION_REVISION; +} diff --git a/libraries/iconfix/CMakeLists.txt b/libraries/iconfix/CMakeLists.txt new file mode 100644 index 00000000..4dfc39a9 --- /dev/null +++ b/libraries/iconfix/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.1) +project(iconfix) + +find_package(Qt5Core REQUIRED QUIET) +find_package(Qt5Widgets REQUIRED QUIET) + +set(ICONFIX_SOURCES +xdgicon.h +xdgicon.cpp +internal/qhexstring_p.h +internal/qiconloader.cpp +internal/qiconloader_p.h +) + +add_library(iconfix STATIC ${ICONFIX_SOURCES}) +target_include_directories(iconfix PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +qt5_use_modules(iconfix Core Widgets) diff --git a/libraries/iconfix/internal/qhexstring_p.h b/libraries/iconfix/internal/qhexstring_p.h new file mode 100644 index 00000000..f01b4cdd --- /dev/null +++ b/libraries/iconfix/internal/qhexstring_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// internal helper. Converts an integer value to an unique string token +template struct HexString +{ + inline HexString(const T t) : val(t) + { + } + + inline void write(QChar *&dest) const + { + const ushort hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + const char *c = reinterpret_cast(&val); + for (uint i = 0; i < sizeof(T); ++i) + { + *dest++ = hexChars[*c & 0xf]; + *dest++ = hexChars[(*c & 0xf0) >> 4]; + ++c; + } + } + const T val; +}; + +// specialization to enable fast concatenating of our string tokens to a string +template struct QConcatenable> +{ + typedef HexString type; + enum + { + ExactSize = true + }; + static int size(const HexString &) + { + return sizeof(T) * 2; + } + static inline void appendTo(const HexString &str, QChar *&out) + { + str.write(out); + } + typedef QString ConvertTo; +}; diff --git a/libraries/iconfix/internal/qiconloader.cpp b/libraries/iconfix/internal/qiconloader.cpp new file mode 100644 index 00000000..b1195893 --- /dev/null +++ b/libraries/iconfix/internal/qiconloader.cpp @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qiconloader_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qhexstring_p.h" + +namespace QtXdg +{ + +Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance) + +/* Theme to use in last resort, if the theme does not have the icon, neither the parents */ + +static QString fallbackTheme() +{ + return QString("hicolor"); +} + +QIconLoader::QIconLoader() : m_themeKey(1), m_supportsSvg(false), m_initialized(false) +{ +} + +// We lazily initialize the loader to make static icons +// work. Though we do not officially support this. + +static inline QString systemThemeName() +{ + return QIcon::themeName(); +} + +static inline QStringList systemIconSearchPaths() +{ + auto paths = QIcon::themeSearchPaths(); + paths.push_front(":/icons"); + return paths; +} + +void QIconLoader::ensureInitialized() +{ + if (!m_initialized) + { + m_initialized = true; + + Q_ASSERT(qApp); + + m_systemTheme = QIcon::themeName(); + + if (m_systemTheme.isEmpty()) + m_systemTheme = fallbackTheme(); + m_supportsSvg = true; + } +} + +QIconLoader *QIconLoader::instance() +{ + iconLoaderInstance()->ensureInitialized(); + return iconLoaderInstance(); +} + +// Queries the system theme and invalidates existing +// icons if the theme has changed. +void QIconLoader::updateSystemTheme() +{ + // Only change if this is not explicitly set by the user + if (m_userTheme.isEmpty()) + { + QString theme = systemThemeName(); + if (theme.isEmpty()) + theme = fallbackTheme(); + if (theme != m_systemTheme) + { + m_systemTheme = theme; + invalidateKey(); + } + } +} + +void QIconLoader::setThemeName(const QString &themeName) +{ + m_userTheme = themeName; + invalidateKey(); +} + +void QIconLoader::setThemeSearchPath(const QStringList &searchPaths) +{ + m_iconDirs = searchPaths; + themeList.clear(); + invalidateKey(); +} + +QStringList QIconLoader::themeSearchPaths() const +{ + if (m_iconDirs.isEmpty()) + { + m_iconDirs = systemIconSearchPaths(); + } + return m_iconDirs; +} + +QIconTheme::QIconTheme(const QString &themeName) : m_valid(false) +{ + QFile themeIndex; + + QStringList iconDirs = systemIconSearchPaths(); + for (int i = 0; i < iconDirs.size(); ++i) + { + QDir iconDir(iconDirs[i]); + QString themeDir = iconDir.path() + QLatin1Char('/') + themeName; + themeIndex.setFileName(themeDir + QLatin1String("/index.theme")); + if (themeIndex.exists()) + { + m_contentDir = themeDir; + m_valid = true; + + foreach (QString path, iconDirs) + { + if (QFileInfo(path).isDir()) + m_contentDirs.append(path + QLatin1Char('/') + themeName); + } + + break; + } + } + + // if there is no index file, abscond. + if (!themeIndex.exists()) + return; + + // otherwise continue reading index file + const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat); + QStringListIterator keyIterator(indexReader.allKeys()); + while (keyIterator.hasNext()) + { + const QString key = keyIterator.next(); + if (!key.endsWith(QLatin1String("/Size"))) + continue; + + // Note the QSettings ini-format does not accept + // slashes in key names, hence we have to cheat + int size = indexReader.value(key).toInt(); + if (!size) + continue; + + QString directoryKey = key.left(key.size() - 5); + QIconDirInfo dirInfo(directoryKey); + dirInfo.size = size; + QString type = + indexReader.value(directoryKey + QLatin1String("/Type")).toString(); + + if (type == QLatin1String("Fixed")) + dirInfo.type = QIconDirInfo::Fixed; + else if (type == QLatin1String("Scalable")) + dirInfo.type = QIconDirInfo::Scalable; + else + dirInfo.type = QIconDirInfo::Threshold; + + dirInfo.threshold = + indexReader.value(directoryKey + QLatin1String("/Threshold"), 2) + .toInt(); + + dirInfo.minSize = + indexReader.value(directoryKey + QLatin1String("/MinSize"), size) + .toInt(); + + dirInfo.maxSize = + indexReader.value(directoryKey + QLatin1String("/MaxSize"), size) + .toInt(); + m_keyList.append(dirInfo); + } + + // Parent themes provide fallbacks for missing icons + m_parents = indexReader.value(QLatin1String("Icon Theme/Inherits")).toStringList(); + m_parents.removeAll(QString()); + + // Ensure a default platform fallback for all themes + if (m_parents.isEmpty()) + { + const QString fallback = fallbackTheme(); + if (!fallback.isEmpty()) + m_parents.append(fallback); + } + + // Ensure that all themes fall back to hicolor + if (!m_parents.contains(QLatin1String("hicolor"))) + m_parents.append(QLatin1String("hicolor")); +} + +QThemeIconEntries QIconLoader::findIconHelper(const QString &themeName, const QString &iconName, + QStringList &visited) const +{ + QThemeIconEntries entries; + Q_ASSERT(!themeName.isEmpty()); + + QPixmap pixmap; + + // Used to protect against potential recursions + visited << themeName; + + QIconTheme theme = themeList.value(themeName); + if (!theme.isValid()) + { + theme = QIconTheme(themeName); + if (!theme.isValid()) + theme = QIconTheme(fallbackTheme()); + + themeList.insert(themeName, theme); + } + + QStringList contentDirs = theme.contentDirs(); + const QVector subDirs = theme.keyList(); + + const QString svgext(QLatin1String(".svg")); + const QString pngext(QLatin1String(".png")); + const QString xpmext(QLatin1String(".xpm")); + + // Add all relevant files + for (int i = 0; i < subDirs.size(); ++i) + { + const QIconDirInfo &dirInfo = subDirs.at(i); + QString subdir = dirInfo.path; + + foreach (QString contentDir, contentDirs) + { + QDir currentDir(contentDir + '/' + subdir); + + if (currentDir.exists(iconName + pngext)) + { + PixmapEntry *iconEntry = new PixmapEntry; + iconEntry->dir = dirInfo; + iconEntry->filename = currentDir.filePath(iconName + pngext); + // Notice we ensure that pixmap entries always come before + // scalable to preserve search order afterwards + entries.prepend(iconEntry); + } + else if (m_supportsSvg && currentDir.exists(iconName + svgext)) + { + ScalableEntry *iconEntry = new ScalableEntry; + iconEntry->dir = dirInfo; + iconEntry->filename = currentDir.filePath(iconName + svgext); + entries.append(iconEntry); + break; + } + else if (currentDir.exists(iconName + xpmext)) + { + PixmapEntry *iconEntry = new PixmapEntry; + iconEntry->dir = dirInfo; + iconEntry->filename = currentDir.filePath(iconName + xpmext); + // Notice we ensure that pixmap entries always come before + // scalable to preserve search order afterwards + entries.append(iconEntry); + break; + } + } + } + + if (entries.isEmpty()) + { + const QStringList parents = theme.parents(); + // Search recursively through inherited themes + for (int i = 0; i < parents.size(); ++i) + { + + const QString parentTheme = parents.at(i).trimmed(); + + if (!visited.contains(parentTheme)) // guard against recursion + entries = findIconHelper(parentTheme, iconName, visited); + + if (!entries.isEmpty()) // success + break; + } + } + +/********************************************************************* +Author: Kaitlin Rupert +Date: Aug 12, 2010 +Description: Make it so that the QIcon loader honors /usr/share/pixmaps + directory. This is a valid directory per the Freedesktop.org + icon theme specification. +Bug: https://bugreports.qt.nokia.com/browse/QTBUG-12874 + *********************************************************************/ +#ifdef Q_OS_LINUX + /* Freedesktop standard says to look in /usr/share/pixmaps last */ + if (entries.isEmpty()) + { + const QString pixmaps(QLatin1String("/usr/share/pixmaps")); + + QDir currentDir(pixmaps); + QIconDirInfo dirInfo(pixmaps); + if (currentDir.exists(iconName + pngext)) + { + PixmapEntry *iconEntry = new PixmapEntry; + iconEntry->dir = dirInfo; + iconEntry->filename = currentDir.filePath(iconName + pngext); + // Notice we ensure that pixmap entries always come before + // scalable to preserve search order afterwards + entries.prepend(iconEntry); + } + else if (m_supportsSvg && currentDir.exists(iconName + svgext)) + { + ScalableEntry *iconEntry = new ScalableEntry; + iconEntry->dir = dirInfo; + iconEntry->filename = currentDir.filePath(iconName + svgext); + entries.append(iconEntry); + } + else if (currentDir.exists(iconName + xpmext)) + { + PixmapEntry *iconEntry = new PixmapEntry; + iconEntry->dir = dirInfo; + iconEntry->filename = currentDir.filePath(iconName + xpmext); + // Notice we ensure that pixmap entries always come before + // scalable to preserve search order afterwards + entries.append(iconEntry); + } + } +#endif + + if (entries.isEmpty()) + { + // Search for unthemed icons in main dir of search paths + QStringList themeSearchPaths = QIcon::themeSearchPaths(); + foreach (QString contentDir, themeSearchPaths) + { + QDir currentDir(contentDir); + + if (currentDir.exists(iconName + pngext)) + { + PixmapEntry *iconEntry = new PixmapEntry; + iconEntry->filename = currentDir.filePath(iconName + pngext); + // Notice we ensure that pixmap entries always come before + // scalable to preserve search order afterwards + entries.prepend(iconEntry); + } + else if (m_supportsSvg && currentDir.exists(iconName + svgext)) + { + ScalableEntry *iconEntry = new ScalableEntry; + iconEntry->filename = currentDir.filePath(iconName + svgext); + entries.append(iconEntry); + break; + } + else if (currentDir.exists(iconName + xpmext)) + { + PixmapEntry *iconEntry = new PixmapEntry; + iconEntry->filename = currentDir.filePath(iconName + xpmext); + // Notice we ensure that pixmap entries always come before + // scalable to preserve search order afterwards + entries.append(iconEntry); + break; + } + } + } + return entries; +} + +QThemeIconEntries QIconLoader::loadIcon(const QString &name) const +{ + if (!themeName().isEmpty()) + { + QStringList visited; + return findIconHelper(themeName(), name, visited); + } + + return QThemeIconEntries(); +} + +// -------- Icon Loader Engine -------- // + +QIconLoaderEngineFixed::QIconLoaderEngineFixed(const QString &iconName) + : m_iconName(iconName), m_key(0) +{ +} + +QIconLoaderEngineFixed::~QIconLoaderEngineFixed() +{ + qDeleteAll(m_entries); +} + +QIconLoaderEngineFixed::QIconLoaderEngineFixed(const QIconLoaderEngineFixed &other) + : QIconEngine(other), m_iconName(other.m_iconName), m_key(0) +{ +} + +QIconEngine *QIconLoaderEngineFixed::clone() const +{ + return new QIconLoaderEngineFixed(*this); +} + +bool QIconLoaderEngineFixed::read(QDataStream &in) +{ + in >> m_iconName; + return true; +} + +bool QIconLoaderEngineFixed::write(QDataStream &out) const +{ + out << m_iconName; + return true; +} + +bool QIconLoaderEngineFixed::hasIcon() const +{ + return !(m_entries.isEmpty()); +} + +// Lazily load the icon +void QIconLoaderEngineFixed::ensureLoaded() +{ + if (!(QIconLoader::instance()->themeKey() == m_key)) + { + + qDeleteAll(m_entries); + + m_entries = QIconLoader::instance()->loadIcon(m_iconName); + m_key = QIconLoader::instance()->themeKey(); + } +} + +void QIconLoaderEngineFixed::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, + QIcon::State state) +{ + QSize pixmapSize = rect.size(); +#if defined(Q_WS_MAC) + pixmapSize *= qt_mac_get_scalefactor(); +#endif + painter->drawPixmap(rect, pixmap(pixmapSize, mode, state)); +} + +/* + * This algorithm is defined by the freedesktop spec: + * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html + */ +static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize) +{ + if (dir.type == QIconDirInfo::Fixed) + { + return dir.size == iconsize; + } + else if (dir.type == QIconDirInfo::Scalable) + { + return dir.size <= dir.maxSize && iconsize >= dir.minSize; + } + else if (dir.type == QIconDirInfo::Threshold) + { + return iconsize >= dir.size - dir.threshold && iconsize <= dir.size + dir.threshold; + } + + Q_ASSERT(1); // Not a valid value + return false; +} + +/* + * This algorithm is defined by the freedesktop spec: + * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html + */ +static int directorySizeDistance(const QIconDirInfo &dir, int iconsize) +{ + if (dir.type == QIconDirInfo::Fixed) + { + return qAbs(dir.size - iconsize); + } + else if (dir.type == QIconDirInfo::Scalable) + { + if (iconsize < dir.minSize) + return dir.minSize - iconsize; + else if (iconsize > dir.maxSize) + return iconsize - dir.maxSize; + else + return 0; + } + else if (dir.type == QIconDirInfo::Threshold) + { + if (iconsize < dir.size - dir.threshold) + return dir.minSize - iconsize; + else if (iconsize > dir.size + dir.threshold) + return iconsize - dir.maxSize; + else + return 0; + } + + Q_ASSERT(1); // Not a valid value + return INT_MAX; +} + +QIconLoaderEngineEntry *QIconLoaderEngineFixed::entryForSize(const QSize &size) +{ + int iconsize = qMin(size.width(), size.height()); + + // Note that m_entries are sorted so that png-files + // come first + + const int numEntries = m_entries.size(); + + // Search for exact matches first + for (int i = 0; i < numEntries; ++i) + { + QIconLoaderEngineEntry *entry = m_entries.at(i); + if (directoryMatchesSize(entry->dir, iconsize)) + { + return entry; + } + } + + // Find the minimum distance icon + int minimalSize = INT_MAX; + QIconLoaderEngineEntry *closestMatch = 0; + for (int i = 0; i < numEntries; ++i) + { + QIconLoaderEngineEntry *entry = m_entries.at(i); + int distance = directorySizeDistance(entry->dir, iconsize); + if (distance < minimalSize) + { + minimalSize = distance; + closestMatch = entry; + } + } + return closestMatch; +} + +/* + * Returns the actual icon size. For scalable svg's this is equivalent + * to the requested size. Otherwise the closest match is returned but + * we can never return a bigger size than the requested size. + * + */ +QSize QIconLoaderEngineFixed::actualSize(const QSize &size, QIcon::Mode mode, + QIcon::State state) +{ + ensureLoaded(); + + QIconLoaderEngineEntry *entry = entryForSize(size); + if (entry) + { + const QIconDirInfo &dir = entry->dir; + if (dir.type == QIconDirInfo::Scalable) + return size; + else + { + int result = qMin(dir.size, qMin(size.width(), size.height())); + return QSize(result, result); + } + } + return QIconEngine::actualSize(size, mode, state); +} + +QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + Q_UNUSED(state); + + // Ensure that basePixmap is lazily initialized before generating the + // key, otherwise the cache key is not unique + if (basePixmap.isNull()) + basePixmap.load(filename); + + QSize actualSize = basePixmap.size(); + if (!actualSize.isNull() && + (actualSize.width() > size.width() || actualSize.height() > size.height())) + actualSize.scale(size, Qt::KeepAspectRatio); + + QString key = QLatin1String("$qt_theme_") % HexString(basePixmap.cacheKey()) % + HexString(mode) % + HexString(QGuiApplication::palette().cacheKey()) % + HexString(actualSize.width()) % HexString(actualSize.height()); + + QPixmap cachedPixmap; + if (QPixmapCache::find(key, &cachedPixmap)) + { + return cachedPixmap; + } + else + { + if (basePixmap.size() != actualSize) + { + cachedPixmap = basePixmap.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + else + { + cachedPixmap = basePixmap; + } + QPixmapCache::insert(key, cachedPixmap); + } + return cachedPixmap; +} + +QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + if (svgIcon.isNull()) + { + svgIcon = QIcon(filename); + } + + // Simply reuse svg icon engine + return svgIcon.pixmap(size, mode, state); +} + +QPixmap QIconLoaderEngineFixed::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + ensureLoaded(); + + QIconLoaderEngineEntry *entry = entryForSize(size); + if (entry) + { + return entry->pixmap(size, mode, state); + } + + return QPixmap(); +} + +QString QIconLoaderEngineFixed::key() const +{ + return QLatin1String("QIconLoaderEngineFixed"); +} + +void QIconLoaderEngineFixed::virtual_hook(int id, void *data) +{ + ensureLoaded(); + + switch (id) + { + case QIconEngine::AvailableSizesHook: + { + QIconEngine::AvailableSizesArgument &arg = + *reinterpret_cast(data); + const int N = m_entries.size(); + QList sizes; + sizes.reserve(N); + + // Gets all sizes from the DirectoryInfo entries + for (int i = 0; i < N; ++i) + { + int size = m_entries.at(i)->dir.size; + sizes.append(QSize(size, size)); + } + arg.sizes.swap(sizes); // commit + } + break; + case QIconEngine::IconNameHook: + { + QString &name = *reinterpret_cast(data); + name = m_iconName; + } + break; + default: + QIconEngine::virtual_hook(id, data); + } +} + +} // QtXdg diff --git a/libraries/iconfix/internal/qiconloader_p.h b/libraries/iconfix/internal/qiconloader_p.h new file mode 100644 index 00000000..b71bdd83 --- /dev/null +++ b/libraries/iconfix/internal/qiconloader_p.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + + +namespace QtXdg +{ + +class QIconLoader; + +struct QIconDirInfo +{ + enum Type + { + Fixed, + Scalable, + Threshold + }; + QIconDirInfo(const QString &_path = QString()) + : path(_path), size(0), maxSize(0), minSize(0), threshold(0), type(Threshold) + { + } + QString path; + short size; + short maxSize; + short minSize; + short threshold; + Type type : 4; +}; + +class QIconLoaderEngineEntry +{ +public: + virtual ~QIconLoaderEngineEntry() + { + } + virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) = 0; + QString filename; + QIconDirInfo dir; + static int count; +}; + +struct ScalableEntry : public QIconLoaderEngineEntry +{ + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; + QIcon svgIcon; +}; + +struct PixmapEntry : public QIconLoaderEngineEntry +{ + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; + QPixmap basePixmap; +}; + +typedef QList QThemeIconEntries; + +// class QIconLoaderEngine : public QIconEngine +class QIconLoaderEngineFixed : public QIconEngine +{ +public: + QIconLoaderEngineFixed(const QString &iconName = QString()); + ~QIconLoaderEngineFixed(); + + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state); + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state); + QIconEngine *clone() const; + bool read(QDataStream &in); + bool write(QDataStream &out) const; + +private: + QString key() const; + bool hasIcon() const; + void ensureLoaded(); + void virtual_hook(int id, void *data); + QIconLoaderEngineEntry *entryForSize(const QSize &size); + QIconLoaderEngineFixed(const QIconLoaderEngineFixed &other); + QThemeIconEntries m_entries; + QString m_iconName; + uint m_key; + + friend class QIconLoader; +}; + +class QIconTheme +{ +public: + QIconTheme(const QString &name); + QIconTheme() : m_valid(false) + { + } + QStringList parents() + { + return m_parents; + } + QVector keyList() + { + return m_keyList; + } + QString contentDir() + { + return m_contentDir; + } + QStringList contentDirs() + { + return m_contentDirs; + } + bool isValid() + { + return m_valid; + } + +private: + QString m_contentDir; + QStringList m_contentDirs; + QVector m_keyList; + QStringList m_parents; + bool m_valid; +}; + +class QIconLoader +{ +public: + QIconLoader(); + QThemeIconEntries loadIcon(const QString &iconName) const; + uint themeKey() const + { + return m_themeKey; + } + + QString themeName() const + { + return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; + } + void setThemeName(const QString &themeName); + QIconTheme theme() + { + return themeList.value(themeName()); + } + void setThemeSearchPath(const QStringList &searchPaths); + QStringList themeSearchPaths() const; + QIconDirInfo dirInfo(int dirindex); + static QIconLoader *instance(); + void updateSystemTheme(); + void invalidateKey() + { + m_themeKey++; + } + void ensureInitialized(); + +private: + QThemeIconEntries findIconHelper(const QString &themeName, const QString &iconName, + QStringList &visited) const; + uint m_themeKey; + bool m_supportsSvg; + bool m_initialized; + + mutable QString m_userTheme; + mutable QString m_systemTheme; + mutable QStringList m_iconDirs; + mutable QHash themeList; +}; + +} // QtXdg + +// Note: class template specialization of 'QTypeInfo' must occur at +// global scope +Q_DECLARE_TYPEINFO(QtXdg::QIconDirInfo, Q_MOVABLE_TYPE); diff --git a/libraries/iconfix/xdgicon.cpp b/libraries/iconfix/xdgicon.cpp new file mode 100644 index 00000000..a36d80a9 --- /dev/null +++ b/libraries/iconfix/xdgicon.cpp @@ -0,0 +1,152 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * Razor - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2010-2011 Razor team + * Authors: + * Alexander Sokoloff + * + * This program or library 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.1 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "xdgicon.h" + +#include +#include +#include +#include +#include +#include +#include "internal/qiconloader_p.h" +#include + +/************************************************ + + ************************************************/ +static void qt_cleanup_icon_cache(); +typedef QCache IconCache; + +namespace +{ +struct QtIconCache : public IconCache +{ + QtIconCache() + { + qAddPostRoutine(qt_cleanup_icon_cache); + } +}; +} +Q_GLOBAL_STATIC(IconCache, qtIconCache); + +static void qt_cleanup_icon_cache() +{ + qtIconCache()->clear(); +} + +/************************************************ + + ************************************************/ +XdgIcon::XdgIcon() +{ +} + +/************************************************ + + ************************************************/ +XdgIcon::~XdgIcon() +{ +} + +/************************************************ + Returns the name of the current icon theme. + ************************************************/ +QString XdgIcon::themeName() +{ + return QIcon::themeName(); +} + +/************************************************ + Sets the current icon theme to name. + ************************************************/ +void XdgIcon::setThemeName(const QString &themeName) +{ + QIcon::setThemeName(themeName); + QtXdg::QIconLoader::instance()->updateSystemTheme(); +} + +/************************************************ + Returns the QIcon corresponding to name in the current icon theme. If no such icon + is found in the current theme fallback is return instead. + ************************************************/ +QIcon XdgIcon::fromTheme(const QString &iconName, const QIcon &fallback) +{ + if (iconName.isEmpty()) + return fallback; + + bool isAbsolute = (iconName[0] == '/'); + + QString name = QFileInfo(iconName).fileName(); + if (name.endsWith(".png", Qt::CaseInsensitive) || + name.endsWith(".svg", Qt::CaseInsensitive) || + name.endsWith(".xpm", Qt::CaseInsensitive)) + { + name.truncate(name.length() - 4); + } + + QIcon icon; + + if (qtIconCache()->contains(name)) + { + icon = *qtIconCache()->object(name); + } + else + { + QIcon *cachedIcon; + if (!isAbsolute) + cachedIcon = new QIcon(new QtXdg::QIconLoaderEngineFixed(name)); + else + cachedIcon = new QIcon(iconName); + qtIconCache()->insert(name, cachedIcon); + icon = *cachedIcon; + } + + // Note the qapp check is to allow lazy loading of static icons + // Supporting fallbacks will not work for this case. + if (qApp && !isAbsolute && icon.availableSizes().isEmpty()) + { + return fallback; + } + return icon; +} + +/************************************************ + Returns the QIcon corresponding to names in the current icon theme. If no such icon + is found in the current theme fallback is return instead. + ************************************************/ +QIcon XdgIcon::fromTheme(const QStringList &iconNames, const QIcon &fallback) +{ + foreach (QString iconName, iconNames) + { + QIcon icon = fromTheme(iconName); + if (!icon.isNull()) + return icon; + } + + return fallback; +} diff --git a/libraries/iconfix/xdgicon.h b/libraries/iconfix/xdgicon.h new file mode 100644 index 00000000..9c11683a --- /dev/null +++ b/libraries/iconfix/xdgicon.h @@ -0,0 +1,46 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * Razor - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2010-2011 Razor team + * Authors: + * Alexander Sokoloff + * + * This program or library 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.1 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#pragma once + +#include +#include +#include + +class XdgIcon +{ +public: + static QIcon fromTheme(const QString &iconName, const QIcon &fallback = QIcon()); + static QIcon fromTheme(const QStringList &iconNames, const QIcon &fallback = QIcon()); + + static QString themeName(); + static void setThemeName(const QString &themeName); + +protected: + explicit XdgIcon(); + virtual ~XdgIcon(); +}; diff --git a/libraries/javacheck/.gitignore b/libraries/javacheck/.gitignore new file mode 100644 index 00000000..cc1c52bf --- /dev/null +++ b/libraries/javacheck/.gitignore @@ -0,0 +1,6 @@ +.idea +*.iml +out +.classpath +.idea +.project diff --git a/libraries/javacheck/CMakeLists.txt b/libraries/javacheck/CMakeLists.txt new file mode 100644 index 00000000..9768650e --- /dev/null +++ b/libraries/javacheck/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.1) +project(launcher Java) +find_package(Java 1.6 REQUIRED COMPONENTS Development) + +include(UseJava) +set(CMAKE_JAVA_JAR_ENTRY_POINT JavaCheck) +set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked) + +set(SRC + JavaCheck.java +) + +add_jar(JavaCheck ${SRC}) diff --git a/libraries/javacheck/JavaCheck.java b/libraries/javacheck/JavaCheck.java new file mode 100644 index 00000000..11420b86 --- /dev/null +++ b/libraries/javacheck/JavaCheck.java @@ -0,0 +1,24 @@ +import java.lang.Integer; + +public class JavaCheck +{ + private static final String[] keys = {"os.arch", "java.version"}; + public static void main (String [] args) + { + int ret = 0; + for(String key : keys) + { + String property = System.getProperty(key); + if(property != null) + { + System.out.println(key + "=" + property); + } + else + { + ret = 1; + } + } + + System.exit(ret); + } +} diff --git a/libraries/launcher/.gitignore b/libraries/launcher/.gitignore new file mode 100644 index 00000000..cc1c52bf --- /dev/null +++ b/libraries/launcher/.gitignore @@ -0,0 +1,6 @@ +.idea +*.iml +out +.classpath +.idea +.project diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt new file mode 100644 index 00000000..b62805e0 --- /dev/null +++ b/libraries/launcher/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.1) +project(launcher Java) +find_package(Java 1.6 REQUIRED COMPONENTS Development) + +include(UseJava) +set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint) +set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked) + +set(SRC + # OSX things + org/simplericity/macify/eawt/Application.java + org/simplericity/macify/eawt/ApplicationAdapter.java + org/simplericity/macify/eawt/ApplicationEvent.java + org/simplericity/macify/eawt/ApplicationListener.java + org/simplericity/macify/eawt/DefaultApplication.java + + # legacy applet wrapper thing. + # The launcher has to be there for silly FML/Forge relauncher. + net/minecraft/Launcher.java + org/multimc/legacy/LegacyLauncher.java + org/multimc/LegacyFrame.java + + # onesix launcher + org/multimc/onesix/OneSixLauncher.java + + # generic launcher + org/multimc/EntryPoint.java + org/multimc/Launcher.java + org/multimc/ParseException.java + org/multimc/Utils.java + org/multimc/IconLoader.java +) +add_jar(NewLaunch ${SRC}) + diff --git a/libraries/launcher/net/minecraft/Launcher.java b/libraries/launcher/net/minecraft/Launcher.java new file mode 100644 index 00000000..a53501ec --- /dev/null +++ b/libraries/launcher/net/minecraft/Launcher.java @@ -0,0 +1,165 @@ +/* + * Copyright 2012-2014 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. + */ + +package net.minecraft; + +import java.util.TreeMap; +import java.util.Map; +import java.net.URL; +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.applet.Applet; +import java.applet.AppletStub; +import java.net.MalformedURLException; + +public class Launcher extends Applet implements AppletStub +{ + private Applet wrappedApplet; + private URL documentBase; + private boolean active = false; + private final Map params; + + public Launcher(Applet applet, URL documentBase) + { + params = new TreeMap(); + + this.setLayout(new BorderLayout()); + this.add(applet, "Center"); + this.wrappedApplet = applet; + this.documentBase = documentBase; + } + + public void setParameter(String name, String value) + { + params.put(name, value); + } + + public void replace(Applet applet) + { + this.wrappedApplet = applet; + + applet.setStub(this); + applet.setSize(getWidth(), getHeight()); + + this.setLayout(new BorderLayout()); + this.add(applet, "Center"); + + applet.init(); + active = true; + applet.start(); + validate(); + } + + @Override + public String getParameter(String name) + { + String param = params.get(name); + if (param != null) + return param; + try + { + return super.getParameter(name); + } catch (Exception ignore){} + return null; + } + + @Override + public boolean isActive() + { + return active; + } + + @Override + public void appletResize(int width, int height) + { + wrappedApplet.resize(width, height); + } + + @Override + public void resize(int width, int height) + { + wrappedApplet.resize(width, height); + } + + @Override + public void resize(Dimension d) + { + wrappedApplet.resize(d); + } + + @Override + public void init() + { + if (wrappedApplet != null) + { + wrappedApplet.init(); + } + } + + @Override + public void start() + { + wrappedApplet.start(); + active = true; + } + + @Override + public void stop() + { + wrappedApplet.stop(); + active = false; + } + + public void destroy() + { + wrappedApplet.destroy(); + } + + @Override + public URL getCodeBase() { + try { + return new URL("http://www.minecraft.net/game/"); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public URL getDocumentBase() + { + try { + return new URL("http://www.minecraft.net/game/"); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public void setVisible(boolean b) + { + super.setVisible(b); + wrappedApplet.setVisible(b); + } + public void update(Graphics paramGraphics) + { + } + public void paint(Graphics paramGraphics) + { + } +} \ No newline at end of file diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java new file mode 100644 index 00000000..d1fc54a8 --- /dev/null +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -0,0 +1,178 @@ +package org.multimc;/* + * Copyright 2012-2014 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. + */ + +import org.multimc.legacy.LegacyLauncher; +import org.multimc.onesix.OneSixLauncher; +import org.simplericity.macify.eawt.Application; +import org.simplericity.macify.eawt.DefaultApplication; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.charset.Charset; + +public class EntryPoint +{ + private enum Action + { + Proceed, + Launch, + Abort + } + + public static void main(String[] args) + { + // Set the OSX application icon first, if we are on OSX. + Application application = new DefaultApplication(); + if(application.isMac()) + { + try + { + BufferedImage image = ImageIO.read(new File("icon.png")); + application.setApplicationIconImage(image); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + EntryPoint listener = new EntryPoint(); + int retCode = listener.listen(); + if (retCode != 0) + { + System.out.println("Exiting with " + retCode); + System.exit(retCode); + } + } + + private Action parseLine(String inData) throws ParseException + { + String[] pair = inData.split(" ", 2); + + if(pair.length == 1) + { + String command = pair[0]; + if (pair[0].equals("launch")) + return Action.Launch; + + else if (pair[0].equals("abort")) + return Action.Abort; + + else throw new ParseException(); + } + + if(pair.length != 2) + throw new ParseException(); + + String command = pair[0]; + String param = pair[1]; + + if(command.equals("launcher")) + { + if(param.equals("legacy")) + { + m_launcher = new LegacyLauncher(); + Utils.log("Using legacy launcher."); + Utils.log(); + return Action.Proceed; + } + if(param.equals("onesix")) + { + m_launcher = new OneSixLauncher(); + Utils.log("Using onesix launcher."); + Utils.log(); + return Action.Proceed; + } + else + throw new ParseException(); + } + + m_params.add(command, param); + //System.out.println(command + " : " + param); + return Action.Proceed; + } + + public int listen() + { + BufferedReader buffer; + try + { + buffer = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); + } catch (UnsupportedEncodingException e) + { + System.err.println("For some reason, your java does not support UTF-8. Consider living in the current century."); + e.printStackTrace(); + return 1; + } + boolean isListening = true; + boolean isAborted = false; + // Main loop + while (isListening) + { + String inData; + try + { + // Read from the pipe one line at a time + inData = buffer.readLine(); + if (inData != null) + { + Action a = parseLine(inData); + if(a == Action.Abort) + { + isListening = false; + isAborted = true; + } + if(a == Action.Launch) + { + isListening = false; + } + } + else + { + isListening = false; + isAborted = true; + } + } + catch (IOException e) + { + System.err.println("Launcher ABORT due to IO exception:"); + e.printStackTrace(); + return 1; + } + catch (ParseException e) + { + System.err.println("Launcher ABORT due to PARSE exception:"); + e.printStackTrace(); + return 1; + } + } + if(isAborted) + { + System.err.println("Launch aborted by MultiMC."); + return 1; + } + if(m_launcher != null) + { + return m_launcher.launch(m_params); + } + System.err.println("No valid launcher implementation specified."); + return 1; + } + + private ParamBucket m_params = new ParamBucket(); + private org.multimc.Launcher m_launcher; +} diff --git a/libraries/launcher/org/multimc/IconLoader.java b/libraries/launcher/org/multimc/IconLoader.java new file mode 100644 index 00000000..f1638f3a --- /dev/null +++ b/libraries/launcher/org/multimc/IconLoader.java @@ -0,0 +1,132 @@ +package org.multimc; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +/***************************************************************************** + * A convenience class for loading icons from images. + * + * Icons loaded from this class are formatted to fit within the required + * dimension (16x16, 32x32, or 128x128). If the source image is larger than the + * target dimension, it is shrunk down to the minimum size that will fit. If it + * is smaller, then it is only scaled up if the new scale can be a per-pixel + * linear scale (i.e., x2, x3, x4, etc). In both cases, the image's width/height + * ratio is kept the same as the source image. + * + * @author Chris Molini + *****************************************************************************/ +public class IconLoader +{ + /************************************************************************* + * Loads an icon in ByteBuffer form. + * + * @param filepath + * The location of the Image to use as an icon. + * + * @return An array of ByteBuffers containing the pixel data for the icon in + * various sizes (as recommended by the OS). + *************************************************************************/ + public static ByteBuffer[] load(String filepath) + { + BufferedImage image; + try { + image = ImageIO.read ( new File( filepath ) ); + } catch ( IOException e ) { + e.printStackTrace(); + return new ByteBuffer[0]; + } + ByteBuffer[] buffers; + buffers = new ByteBuffer[1]; + buffers[0] = loadInstance(image, 128); + return buffers; + } + + /************************************************************************* + * Copies the supplied image into a square icon at the indicated size. + * + * @param image + * The image to place onto the icon. + * @param dimension + * The desired size of the icon. + * + * @return A ByteBuffer of pixel data at the indicated size. + *************************************************************************/ + private static ByteBuffer loadInstance(BufferedImage image, int dimension) + { + BufferedImage scaledIcon = new BufferedImage(dimension, dimension, + BufferedImage.TYPE_INT_ARGB_PRE); + Graphics2D g = scaledIcon.createGraphics(); + double ratio = getIconRatio(image, scaledIcon); + double width = image.getWidth() * ratio; + double height = image.getHeight() * ratio; + g.drawImage(image, (int) ((scaledIcon.getWidth() - width) / 2), + (int) ((scaledIcon.getHeight() - height) / 2), (int) (width), + (int) (height), null); + g.dispose(); + + return convertToByteBuffer(scaledIcon); + } + + /************************************************************************* + * Gets the width/height ratio of the icon. This is meant to simplify + * scaling the icon to a new dimension. + * + * @param src + * The base image that will be placed onto the icon. + * @param icon + * The icon that will have the image placed on it. + * + * @return The amount to scale the source image to fit it onto the icon + * appropriately. + *************************************************************************/ + private static double getIconRatio(BufferedImage src, BufferedImage icon) + { + double ratio = 1; + if (src.getWidth() > icon.getWidth()) + ratio = (double) (icon.getWidth()) / src.getWidth(); + else + ratio = (int) (icon.getWidth() / src.getWidth()); + if (src.getHeight() > icon.getHeight()) + { + double r2 = (double) (icon.getHeight()) / src.getHeight(); + if (r2 < ratio) + ratio = r2; + } + else + { + double r2 = (int) (icon.getHeight() / src.getHeight()); + if (r2 < ratio) + ratio = r2; + } + return ratio; + } + + /************************************************************************* + * Converts a BufferedImage into a ByteBuffer of pixel data. + * + * @param image + * The image to convert. + * + * @return A ByteBuffer that contains the pixel data of the supplied image. + *************************************************************************/ + public static ByteBuffer convertToByteBuffer(BufferedImage image) + { + byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4]; + int counter = 0; + for (int i = 0; i < image.getHeight(); i++) + for (int j = 0; j < image.getWidth(); j++) + { + int colorSpace = image.getRGB(j, i); + buffer[counter + 0] = (byte) ((colorSpace << 8) >> 24); + buffer[counter + 1] = (byte) ((colorSpace << 16) >> 24); + buffer[counter + 2] = (byte) ((colorSpace << 24) >> 24); + buffer[counter + 3] = (byte) (colorSpace >> 24); + counter += 4; + } + return ByteBuffer.wrap(buffer); + } +} \ No newline at end of file diff --git a/libraries/launcher/org/multimc/Launcher.java b/libraries/launcher/org/multimc/Launcher.java new file mode 100644 index 00000000..1aa2b21f --- /dev/null +++ b/libraries/launcher/org/multimc/Launcher.java @@ -0,0 +1,22 @@ +/* + * Copyright 2012-2014 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. + */ + +package org.multimc; + +public interface Launcher +{ + abstract int launch(ParamBucket params); +} diff --git a/libraries/launcher/org/multimc/LegacyFrame.java b/libraries/launcher/org/multimc/LegacyFrame.java new file mode 100644 index 00000000..a081f3ae --- /dev/null +++ b/libraries/launcher/org/multimc/LegacyFrame.java @@ -0,0 +1,112 @@ +package org.multimc;/* + * Copyright 2012-2014 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. + */ + +import net.minecraft.Launcher; + +import javax.imageio.ImageIO; +import java.applet.Applet; +import java.awt.*; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +public class LegacyFrame extends Frame implements WindowListener +{ + private Launcher appletWrap = null; + public LegacyFrame(String title) + { + super ( title ); + BufferedImage image; + try { + image = ImageIO.read ( new File ( "icon.png" ) ); + setIconImage ( image ); + } catch ( IOException e ) { + e.printStackTrace(); + } + this.addWindowListener ( this ); + } + + public void start ( Applet mcApplet, String user, String session, Dimension winSize, boolean maximize ) + { + try { + appletWrap = new Launcher( mcApplet, new URL ( "http://www.minecraft.net/game" ) ); + } catch ( MalformedURLException ignored ) {} + appletWrap.setParameter ( "username", user ); + appletWrap.setParameter ( "sessionid", session ); + appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button. + appletWrap.setParameter ( "demo", "false" ); + appletWrap.setParameter("fullscreen", "false"); + mcApplet.setStub(appletWrap); + this.add ( appletWrap ); + appletWrap.setPreferredSize ( winSize ); + this.pack(); + this.setLocationRelativeTo ( null ); + this.setResizable ( true ); + if ( maximize ) { + this.setExtendedState ( MAXIMIZED_BOTH ); + } + validate(); + appletWrap.init(); + appletWrap.start(); + setVisible ( true ); + } + + @Override + public void windowActivated ( WindowEvent e ) {} + + @Override + public void windowClosed ( WindowEvent e ) {} + + @Override + public void windowClosing ( WindowEvent e ) + { + new Thread() { + public void run() { + try { + Thread.sleep ( 30000L ); + } catch ( InterruptedException localInterruptedException ) { + localInterruptedException.printStackTrace(); + } + System.out.println ( "FORCING EXIT!" ); + System.exit ( 0 ); + } + } + .start(); + + if ( appletWrap != null ) { + appletWrap.stop(); + appletWrap.destroy(); + } + // old minecraft versions can hang without this >_< + System.exit ( 0 ); + } + + @Override + public void windowDeactivated ( WindowEvent e ) {} + + @Override + public void windowDeiconified ( WindowEvent e ) {} + + @Override + public void windowIconified ( WindowEvent e ) {} + + @Override + public void windowOpened ( WindowEvent e ) {} +} diff --git a/libraries/launcher/org/multimc/NotFoundException.java b/libraries/launcher/org/multimc/NotFoundException.java new file mode 100644 index 00000000..fe154a2f --- /dev/null +++ b/libraries/launcher/org/multimc/NotFoundException.java @@ -0,0 +1,21 @@ +/* + * Copyright 2012-2014 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. + */ + +package org.multimc; + +public class NotFoundException extends Exception +{ +} diff --git a/libraries/launcher/org/multimc/ParamBucket.java b/libraries/launcher/org/multimc/ParamBucket.java new file mode 100644 index 00000000..2e197d9f --- /dev/null +++ b/libraries/launcher/org/multimc/ParamBucket.java @@ -0,0 +1,86 @@ +/* + * Copyright 2012-2014 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. + */ + +package org.multimc; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class ParamBucket +{ + public void add(String key, String value) + { + List coll = null; + if(!m_params.containsKey(key)) + { + coll = new ArrayList(); + m_params.put(key, coll); + } + else + { + coll = m_params.get(key); + } + coll.add(value); + } + + public List all(String key) throws NotFoundException + { + if(!m_params.containsKey(key)) + throw new NotFoundException(); + return m_params.get(key); + } + + public List allSafe(String key, List def) + { + if(!m_params.containsKey(key) || m_params.get(key).size() < 1) + { + return def; + } + return m_params.get(key); + } + + public List allSafe(String key) + { + return allSafe(key, new ArrayList()); + } + + public String first(String key) throws NotFoundException + { + List list = all(key); + if(list.size() < 1) + { + throw new NotFoundException(); + } + return list.get(0); + } + + public String firstSafe(String key, String def) + { + if(!m_params.containsKey(key) || m_params.get(key).size() < 1) + { + return def; + } + return m_params.get(key).get(0); + } + + public String firstSafe(String key) + { + return firstSafe(key, ""); + } + + private HashMap> m_params = new HashMap>(); +} diff --git a/libraries/launcher/org/multimc/ParseException.java b/libraries/launcher/org/multimc/ParseException.java new file mode 100644 index 00000000..d9e8e53e --- /dev/null +++ b/libraries/launcher/org/multimc/ParseException.java @@ -0,0 +1,22 @@ +/* + * Copyright 2012-2014 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. + */ + +package org.multimc; + +public class ParseException extends java.lang.Exception +{ + +} diff --git a/libraries/launcher/org/multimc/Utils.java b/libraries/launcher/org/multimc/Utils.java new file mode 100644 index 00000000..32cf7919 --- /dev/null +++ b/libraries/launcher/org/multimc/Utils.java @@ -0,0 +1,280 @@ +/* + * Copyright 2012-2014 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. + */ + +package org.multimc; + +import java.io.*; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class Utils +{ + /** + * Combine two parts of a path. + * + * @param path1 + * @param path2 + * @return the paths, combined + */ + public static String combine(String path1, String path2) + { + File file1 = new File(path1); + File file2 = new File(file1, path2); + return file2.getPath(); + } + + /** + * Join a list of strings into a string using a separator! + * + * @param strings the string list to join + * @param separator the glue + * @return the result. + */ + public static String join(List strings, String separator) + { + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (String s : strings) + { + sb.append(sep).append(s); + sep = separator; + } + return sb.toString(); + } + + /** + * Adds the specified library to the classpath + * + * @param s the path to add + * @throws Exception + */ + public static void addToClassPath(String s) throws Exception + { + File f = new File(s); + URL u = f.toURI().toURL(); + URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); + Class urlClass = URLClassLoader.class; + Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class}); + method.setAccessible(true); + method.invoke(urlClassLoader, new Object[]{u}); + } + + /** + * Adds many libraries to the classpath + * + * @param jars the paths to add + */ + public static boolean addToClassPath(List jars) + { + boolean pure = true; + // initialize the class path + for (String jar : jars) + { + try + { + Utils.addToClassPath(jar); + } catch (Exception e) + { + System.err.println("Unable to load: " + jar); + e.printStackTrace(System.err); + pure = false; + } + } + return pure; + } + + /** + * Adds the specified path to the java library path + * + * @param pathToAdd the path to add + * @throws Exception + */ + @Deprecated + public static void addLibraryPath(String pathToAdd) throws Exception + { + final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths"); + usrPathsField.setAccessible(true); + + //get array of paths + final String[] paths = (String[]) usrPathsField.get(null); + + //check if the path to add is already present + for (String path : paths) + { + if (path.equals(pathToAdd)) + { + return; + } + } + + //add the new path + final String[] newPaths = Arrays.copyOf(paths, paths.length + 1); + newPaths[newPaths.length - 1] = pathToAdd; + usrPathsField.set(null, newPaths); + } + + /** + * Finds a field that looks like a Minecraft base folder in a supplied class + * + * @param mc the class to scan + */ + public static Field getMCPathField(Class mc) + { + Field[] fields = mc.getDeclaredFields(); + + for (Field f : fields) + { + if (f.getType() != File.class) + { + // Has to be File + continue; + } + if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC)) + { + // And Private Static. + continue; + } + return f; + } + return null; + } + + /** + * Log to the MultiMC console + * + * @param message A String containing the message + * @param level A String containing the level name. See MinecraftLauncher::getLevel() + */ + public static void log(String message, String level) + { + // Kinda dirty + String tag = "!![" + level + "]!"; + System.out.println(tag + message.replace("\n", "\n" + tag)); + } + + public static void log(String message) + { + log(message, "MultiMC"); + } + + public static void log() + { + System.out.println(); + } + + /** + * Pushes bytes from in to out. Closes both streams no matter what. + * @param in the input stream + * @param out the output stream + * @throws IOException + */ + private static void copyStream(InputStream in, OutputStream out) throws IOException + { + try + { + byte[] buffer = new byte[4096]; + int len; + + while((len = in.read(buffer)) >= 0) + out.write(buffer, 0, len); + } finally + { + in.close(); + out.close(); + } + } + + /** + * Replace a 'target' string 'suffix' with 'replacement' + */ + public static String replaceSuffix (String target, String suffix, String replacement) + { + if (!target.endsWith(suffix)) + { + return target; + } + String prefix = target.substring(0, target.length() - suffix.length()); + return prefix + replacement; + } + + /** + * Unzip zip file with natives 'source' into the folder 'targetFolder' + * + * Contains a hack for OSX. Yay. + * @param source + * @param targetFolder + * @throws IOException + */ + public static void unzipNatives(File source, File targetFolder) throws IOException + { + ZipFile zip = new ZipFile(source); + + boolean applyHacks = false; + String[] javaVersionElements = System.getProperty("java.version").split("[.\\-+]"); + int major = Integer.parseInt(javaVersionElements[0]); + if(major == 1) + { + major = Integer.parseInt(javaVersionElements[1]); + } + if (major >= 8) + { + applyHacks = true; + } + + try + { + Enumeration entries = zip.entries(); + + while (entries.hasMoreElements()) + { + ZipEntry entry = (ZipEntry) entries.nextElement(); + + String entryName = entry.getName(); + String fileName = entryName; + if(applyHacks) + { + fileName = replaceSuffix(entryName, ".jnilib", ".dylib"); + } + File targetFile = new File(targetFolder, fileName); + if (targetFile.getParentFile() != null) + { + targetFile.getParentFile().mkdirs(); + } + + if (entry.isDirectory()) + continue; + + copyStream(zip.getInputStream(entry), new BufferedOutputStream(new FileOutputStream(targetFile))); + } + } finally + { + zip.close(); + } + } +} + diff --git a/libraries/launcher/org/multimc/legacy/LegacyLauncher.java b/libraries/launcher/org/multimc/legacy/LegacyLauncher.java new file mode 100644 index 00000000..347bb1a2 --- /dev/null +++ b/libraries/launcher/org/multimc/legacy/LegacyLauncher.java @@ -0,0 +1,175 @@ +package org.multimc.legacy;/* + * Copyright 2012-2014 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. + */ + +import org.multimc.*; + +import java.applet.Applet; +import java.awt.*; +import java.io.File; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +public class LegacyLauncher implements Launcher +{ + @Override + public int launch(ParamBucket params) + { + String userName, sessionId, windowTitle, windowParams, lwjgl; + String mainClass = "net.minecraft.client.Minecraft"; + try + { + userName = params.first("userName"); + sessionId = params.first("sessionId"); + windowTitle = params.first("windowTitle"); + windowParams = params.first("windowParams"); + lwjgl = params.first("lwjgl"); + } catch (NotFoundException e) + { + System.err.println("Not enough arguments."); + return -1; + } + + String cwd = System.getProperty("user.dir"); + Dimension winSize = new Dimension(854, 480); + boolean maximize = false; + + String[] dimStrings = windowParams.split("x"); + + if (windowParams.equalsIgnoreCase("max")) + { + maximize = true; + } + else if (dimStrings.length == 2) + { + try + { + winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1])); + } catch (NumberFormatException ignored) {} + } + + File binDir = new File(cwd, "bin"); + File lwjglDir; + if (lwjgl.equalsIgnoreCase("Mojang")) + { + lwjglDir = binDir; + } + else + { + lwjglDir = new File(lwjgl); + } + + URL[] classpath; + { + try + { + classpath = new URL[] + { + new File(binDir, "minecraft.jar").toURI().toURL(), + new File(lwjglDir, "lwjgl.jar").toURI().toURL(), + new File(lwjglDir, "lwjgl_util.jar").toURI().toURL(), + new File(lwjglDir, "jinput.jar").toURI().toURL(), + }; + } catch (MalformedURLException e) + { + System.err.println("Class path entry is badly formed:"); + e.printStackTrace(System.err); + return -1; + } + } + + String nativesDir = new File(lwjglDir, "natives").toString(); + + System.setProperty("org.lwjgl.librarypath", nativesDir); + System.setProperty("net.java.games.input.librarypath", nativesDir); + + // print the pretty things + { + Utils.log("Main Class:"); + Utils.log(" " + mainClass); + Utils.log(); + + Utils.log("Class Path:"); + for (URL s : classpath) + { + Utils.log(" " + s); + } + Utils.log(); + + Utils.log("Native Path:"); + Utils.log(" " + nativesDir); + Utils.log(); + } + + URLClassLoader cl = new URLClassLoader(classpath, LegacyLauncher.class.getClassLoader()); + + // Get the Minecraft Class and set the base folder + Class mc; + try + { + mc = cl.loadClass(mainClass); + + Field f = Utils.getMCPathField(mc); + + if (f == null) + { + System.err.println("Could not find Minecraft path field. Launch failed."); + return -1; + } + + f.setAccessible(true); + f.set(null, new File(cwd)); + } catch (Exception e) + { + System.err.println("Could not set base folder. Failed to find/access Minecraft main class:"); + e.printStackTrace(System.err); + return -1; + } + + System.setProperty("minecraft.applet.TargetDirectory", cwd); + + String[] mcArgs = new String[2]; + mcArgs[0] = userName; + mcArgs[1] = sessionId; + + Utils.log("Launching with applet wrapper..."); + try + { + Class MCAppletClass = cl.loadClass("net.minecraft.client.MinecraftApplet"); + Applet mcappl = (Applet) MCAppletClass.newInstance(); + LegacyFrame mcWindow = new LegacyFrame(windowTitle); + mcWindow.start(mcappl, userName, sessionId, winSize, maximize); + } catch (Exception e) + { + Utils.log("Applet wrapper failed:", "Error"); + e.printStackTrace(System.err); + Utils.log(); + Utils.log("Falling back to compatibility mode."); + try + { + mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); + } catch (Exception e1) + { + Utils.log("Failed to invoke the Minecraft main class:", "Fatal"); + e1.printStackTrace(System.err); + return -1; + } + } + + return 0; + } +} diff --git a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java new file mode 100644 index 00000000..179df0ee --- /dev/null +++ b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java @@ -0,0 +1,367 @@ +/* Copyright 2012-2014 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. + */ + +package org.multimc.onesix; + +import org.multimc.*; + +import java.applet.Applet; +import java.io.File; +import java.awt.*; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class OneSixLauncher implements Launcher +{ + // parameters, separated from ParamBucket + private List libraries; + private List extlibs; + private List extlibs32; + private List extlibs64; + private List mcparams; + private List mods; + private List jarmods; + private List coremods; + private List traits; + private String appletClass; + private String mainClass; + private String nativePath; + private String userName, sessionId; + private String windowTitle; + private String windowParams; + + // secondary parameters + private Dimension winSize; + private boolean maximize; + private String cwd; + + // the much abused system classloader, for convenience (for further abuse) + private ClassLoader cl; + + private void processParams(ParamBucket params) throws NotFoundException + { + libraries = params.all("cp"); + extlibs = params.allSafe("ext", new ArrayList()); + extlibs32 = params.allSafe("ext32", new ArrayList()); + extlibs64 = params.allSafe("ext64", new ArrayList()); + + // Unify the extracted native libs according to actual system architecture + String property = System.getProperty("os.arch"); + boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64"); + if(is_64) + { + extlibs.addAll(extlibs64); + } + else + { + extlibs.addAll(extlibs32); + } + + mcparams = params.allSafe("param", new ArrayList() ); + mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); + appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); + mods = params.allSafe("mod", new ArrayList()); + jarmods = params.allSafe("jarmod", new ArrayList()); + coremods = params.allSafe("coremod", new ArrayList()); + traits = params.allSafe("traits", new ArrayList()); + nativePath = params.first("natives"); + + userName = params.first("userName"); + sessionId = params.first("sessionId"); + windowTitle = params.firstSafe("windowTitle", "Minecraft"); + windowParams = params.firstSafe("windowParams", "854x480"); + + cwd = System.getProperty("user.dir"); + winSize = new Dimension(854, 480); + maximize = false; + + String[] dimStrings = windowParams.split("x"); + + if (windowParams.equalsIgnoreCase("max")) + { + maximize = true; + } + else if (dimStrings.length == 2) + { + try + { + winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1])); + } catch (NumberFormatException ignored) {} + } + } + + private void printStats() + { + Utils.log("Main Class:"); + Utils.log(" " + mainClass); + Utils.log(); + + Utils.log("Native path:"); + Utils.log(" " + nativePath); + Utils.log(); + + Utils.log("Traits:"); + Utils.log(" " + traits); + Utils.log(); + + Utils.log("Libraries:"); + for (String s : libraries) + { + File f = new File(s); + if (f.exists()) + { + Utils.log(" " + s); + } + else + { + Utils.log(" " + s + " (missing)", "Warning"); + } + } + Utils.log(); + + if(mods.size() > 0) + { + Utils.log("Mods:"); + for (String s : mods) + { + Utils.log(" " + s); + } + Utils.log(); + } + + if(coremods.size() > 0) + { + Utils.log("Core Mods:"); + for (String s : coremods) + { + Utils.log(" " + s); + } + Utils.log(); + } + + if(jarmods.size() > 0) + { + Utils.log("Jar Mods:"); + for (String s : jarmods) + { + Utils.log(" " + s); + } + Utils.log(); + } + + Utils.log("Params:"); + Utils.log(" " + mcparams.toString()); + Utils.log(); + if(maximize) + Utils.log("Window size: max (if available)"); + else + Utils.log("Window size: " + Integer.toString(winSize.width) + " x " + Integer.toString(winSize.height)); + Utils.log(); + } + + int legacyLaunch() + { + // Get the Minecraft Class and set the base folder + Class mc; + try + { + mc = cl.loadClass(mainClass); + + Field f = Utils.getMCPathField(mc); + + if (f == null) + { + System.err.println("Could not find Minecraft path field."); + } + else + { + f.setAccessible(true); + f.set(null, new File(cwd)); + } + } catch (Exception e) + { + System.err.println("Could not set base folder. Failed to find/access Minecraft main class:"); + e.printStackTrace(System.err); + return -1; + } + + System.setProperty("minecraft.applet.TargetDirectory", cwd); + + String[] mcArgs = new String[2]; + mcArgs[0] = userName; + mcArgs[1] = sessionId; + + Utils.log("Launching with applet wrapper..."); + try + { + Class MCAppletClass = cl.loadClass(appletClass); + Applet mcappl = (Applet) MCAppletClass.newInstance(); + LegacyFrame mcWindow = new LegacyFrame(windowTitle); + mcWindow.start(mcappl, userName, sessionId, winSize, maximize); + } catch (Exception e) + { + Utils.log("Applet wrapper failed:", "Error"); + e.printStackTrace(System.err); + Utils.log(); + Utils.log("Falling back to compatibility mode."); + try + { + mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); + } catch (Exception e1) + { + Utils.log("Failed to invoke the Minecraft main class:", "Fatal"); + e1.printStackTrace(System.err); + return -1; + } + } + return 0; + } + + int launchWithMainClass() + { + // window size, title and state, onesix + if (maximize) + { + // FIXME: there is no good way to maximize the minecraft window in onesix. + // the following often breaks linux screen setups + // mcparams.add("--fullscreen"); + } + else + { + mcparams.add("--width"); + mcparams.add(Integer.toString(winSize.width)); + mcparams.add("--height"); + mcparams.add(Integer.toString(winSize.height)); + } + + System.setProperty("minecraft.applet.TargetDirectory", cwd); + + // Get the Minecraft Class. + Class mc; + try + { + mc = cl.loadClass(mainClass); + } catch (ClassNotFoundException e) + { + System.err.println("Failed to find Minecraft main class:"); + e.printStackTrace(System.err); + return -1; + } + + // get the main method. + Method meth; + try + { + meth = mc.getMethod("main", String[].class); + } catch (NoSuchMethodException e) + { + System.err.println("Failed to acquire the main method:"); + e.printStackTrace(System.err); + return -1; + } + // init params for the main method to chomp on. + String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); + try + { + // static method doesn't have an instance + meth.invoke(null, (Object) paramsArray); + } catch (Exception e) + { + System.err.println("Failed to start Minecraft:"); + e.printStackTrace(System.err); + return -1; + } + return 0; + } + + @Override + public int launch(ParamBucket params) + { + // get and process the launch script params + try + { + processParams(params); + } catch (NotFoundException e) + { + System.err.println("Not enough arguments."); + e.printStackTrace(System.err); + return -1; + } + + // add libraries to classpath + if(!Utils.addToClassPath(libraries)) + { + System.err.println("Halting launch due to previous errors."); + return -1; + } + + // print the pretty things + printStats(); + + // extract native libs (depending on platform here... java!) + Utils.log("Preparing native libraries..."); + for(String extlib: extlibs) + { + try + { + File extlibf = new File(extlib); + Utils.log("Extracting " + extlibf.getName()); + Utils.unzipNatives(extlibf, new File(nativePath)); + } catch (IOException e) + { + System.err.println("Failed to extract native library:"); + e.printStackTrace(System.err); + return -1; + } + } + Utils.log(); + + // set the native libs path... the brute force way + try + { + System.setProperty("java.library.path", nativePath); + System.setProperty("org.lwjgl.librarypath", nativePath); + System.setProperty("net.java.games.input.librarypath", nativePath); + // by the power of reflection, initialize native libs again. DIRTY! + // this is SO BAD. imagine doing that to ld + Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); + fieldSysPath.setAccessible( true ); + fieldSysPath.set( null, null ); + } + catch (Exception e) + { + System.err.println("Failed to set the native library path:"); + e.printStackTrace(System.err); + System.err.println("Minecraft might fail to launch..."); + } + + // grab the system classloader and ... + cl = ClassLoader.getSystemClassLoader(); + + if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch") ) + { + // legacy launch uses the applet wrapper + return legacyLaunch(); + } + else + { + // normal launch just calls main() + return launchWithMainClass(); + } + } +} diff --git a/libraries/launcher/org/simplericity/macify/eawt/Application.java b/libraries/launcher/org/simplericity/macify/eawt/Application.java new file mode 100644 index 00000000..153bb9ee --- /dev/null +++ b/libraries/launcher/org/simplericity/macify/eawt/Application.java @@ -0,0 +1,176 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * 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. + */ + +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * The Macify Library API interface provides integration with the OS X platform for Java Applications. + * The API includes a facade to the + * + * Apple Java Extensions API + * . + * Additionally, it provides access to several useful methods in the Cocoa NSApplication API. + * + * The default implementation of this interface is {@link org.simplericity.macify.eawt.DefaultApplication}. + */ +public interface Application { + + static int REQUEST_USER_ATTENTION_TYPE_CRITICAL = 1 ; + static int REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL = 2 ; + + /** + * See + * + * Apple's API + * . + */ + void addAboutMenuItem(); + + /** + * See + * + * Apple's API + * . + */ + void addApplicationListener(ApplicationListener applicationListener); + + /** + * See + * + * Apple's API + * . + */ + void addPreferencesMenuItem(); + + /** + * See + * + * Apple's API + * . + */ + boolean getEnabledAboutMenu(); + + /** + * See + * + * Apple's API + * . + */ + boolean getEnabledPreferencesMenu(); + + /** + * See + * + * Apple's API + * . + */ + boolean isAboutMenuItemPresent(); + + /** + * See + * + * Apple's API + * . + */ + boolean isPreferencesMenuItemPresent(); + + /** + * See + * + * Apple's API + * . + */ + void removeAboutMenuItem(); + + /** + * See + * + * Apple's API + * . + */ + void removeApplicationListener(ApplicationListener applicationListener); + + /** + * See + * + * Apple's API + * . + */ + void removePreferencesMenuItem(); + + /** + * See + * + * Apple's API + * . + */ + void setEnabledAboutMenu(boolean enabled); + + /** + * See + * + * Apple's API + * . + */ + void setEnabledPreferencesMenu(boolean enabled); + + /** + * See + * + * Apple's API + * . + */ + Point getMouseLocationOnScreen(); + + /** + * See + * + * Apple's NSApplication Class Reference + * . + * @param type on of {@link #REQUEST_USER_ATTENTION_TYPE_CRITICAL} or {@link #REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL}. + */ + int requestUserAttention(int type); + + /** + * See + * + * Apple's NSApplication Class Reference + * + */ + void cancelUserAttentionRequest(int request); + + /** + * Update the application's icon image + * @param image + */ + void setApplicationIconImage(BufferedImage image); + + /** + * Get the application's icon image. + */ + BufferedImage getApplicationIconImage(); + + /** + * Determines whether the application is running on a Mac AND the Apple Extensions API classes are available. + * @return + */ + boolean isMac(); + + +} diff --git a/libraries/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java b/libraries/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java new file mode 100644 index 00000000..e9c3db7d --- /dev/null +++ b/libraries/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java @@ -0,0 +1,48 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * 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. + */ + +public class ApplicationAdapter implements ApplicationListener { + + public void handleQuit(ApplicationEvent event) { + + } + + public void handleAbout(ApplicationEvent event) { + + } + + public void handleOpenApplication(ApplicationEvent event) { + + } + + public void handleOpenFile(ApplicationEvent event) { + + } + + public void handlePreferences(ApplicationEvent event) { + + } + + public void handlePrintFile(ApplicationEvent event) { + + } + + public void handleReOpenApplication(ApplicationEvent event) { + + } +} diff --git a/libraries/launcher/org/simplericity/macify/eawt/ApplicationEvent.java b/libraries/launcher/org/simplericity/macify/eawt/ApplicationEvent.java new file mode 100644 index 00000000..78420355 --- /dev/null +++ b/libraries/launcher/org/simplericity/macify/eawt/ApplicationEvent.java @@ -0,0 +1,25 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * 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. + */ + +public interface ApplicationEvent { + String getFilename(); + boolean isHandled(); + void setHandled(boolean handled); + Object getSource(); + String toString(); +} diff --git a/libraries/launcher/org/simplericity/macify/eawt/ApplicationListener.java b/libraries/launcher/org/simplericity/macify/eawt/ApplicationListener.java new file mode 100644 index 00000000..a291bee4 --- /dev/null +++ b/libraries/launcher/org/simplericity/macify/eawt/ApplicationListener.java @@ -0,0 +1,27 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * 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. + */ + +public interface ApplicationListener { + void handleAbout(ApplicationEvent event); + void handleOpenApplication(ApplicationEvent event); + void handleOpenFile(ApplicationEvent event); + void handlePreferences(ApplicationEvent event); + void handlePrintFile(ApplicationEvent event); + void handleQuit(ApplicationEvent event); + void handleReOpenApplication(ApplicationEvent event); +} diff --git a/libraries/launcher/org/simplericity/macify/eawt/DefaultApplication.java b/libraries/launcher/org/simplericity/macify/eawt/DefaultApplication.java new file mode 100644 index 00000000..5752a350 --- /dev/null +++ b/libraries/launcher/org/simplericity/macify/eawt/DefaultApplication.java @@ -0,0 +1,418 @@ +package org.simplericity.macify.eawt; + +/* + * Copyright 2007 Eirik Bjorsnos. + * + * 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. + */ + + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.lang.reflect.*; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + + +/** + * Implements Application by calling the Mac OS X API through reflection. + * If this class is used on a non-OS X platform the operations will have no effect or they will simulate + * what the Apple API would do for those who manipulate state. ({@link #setEnabledAboutMenu(boolean)} etc.) + */ +@SuppressWarnings("unchecked") +public class DefaultApplication implements Application { + + private Object application; + private Class applicationListenerClass; + + Map listenerMap = Collections.synchronizedMap(new HashMap()); + private boolean enabledAboutMenu = true; + private boolean enabledPreferencesMenu; + private boolean aboutMenuItemPresent = true; + private boolean preferencesMenuItemPresent; + private ClassLoader classLoader; + + public DefaultApplication() { + try { + final File file = new File("/System/Library/Java"); + if (file.exists()) { + ClassLoader scl = ClassLoader.getSystemClassLoader(); + Class clc = scl.getClass(); + if (URLClassLoader.class.isAssignableFrom(clc)) { + Method addUrl = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); + addUrl.setAccessible(true); + addUrl.invoke(scl, new Object[]{file.toURI().toURL()}); + } + } + + Class appClass = Class.forName("com.apple.eawt.Application"); + application = appClass.getMethod("getApplication", new Class[0]).invoke(null, new Object[0]); + applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); + } catch (ClassNotFoundException e) { + application = null; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + } + + public boolean isMac() { + return application != null; + } + + public void addAboutMenuItem() { + if (isMac()) { + callMethod(application, "addAboutMenuItem"); + } else { + this.aboutMenuItemPresent = true; + } + } + + public void addApplicationListener(ApplicationListener applicationListener) { + + if (!Modifier.isPublic(applicationListener.getClass().getModifiers())) { + throw new IllegalArgumentException("ApplicationListener must be a public class"); + } + if (isMac()) { + Object listener = Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[]{applicationListenerClass}, + new ApplicationListenerInvocationHandler(applicationListener)); + + callMethod(application, "addApplicationListener", new Class[]{applicationListenerClass}, new Object[]{listener}); + listenerMap.put(applicationListener, listener); + } else { + listenerMap.put(applicationListener, applicationListener); + } + } + + public void addPreferencesMenuItem() { + if (isMac()) { + callMethod("addPreferencesMenuItem"); + } else { + this.preferencesMenuItemPresent = true; + } + } + + public boolean getEnabledAboutMenu() { + if (isMac()) { + return callMethod("getEnabledAboutMenu").equals(Boolean.TRUE); + } else { + return enabledAboutMenu; + } + } + + public boolean getEnabledPreferencesMenu() { + if (isMac()) { + Object result = callMethod("getEnabledPreferencesMenu"); + return result.equals(Boolean.TRUE); + } else { + return enabledPreferencesMenu; + } + } + + public Point getMouseLocationOnScreen() { + if (isMac()) { + try { + Method method = application.getClass().getMethod("getMouseLocationOnScreen", new Class[0]); + return (Point) method.invoke(null, new Object[0]); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + return new Point(0, 0); + } + } + + public boolean isAboutMenuItemPresent() { + if (isMac()) { + return callMethod("isAboutMenuItemPresent").equals(Boolean.TRUE); + } else { + return aboutMenuItemPresent; + } + } + + public boolean isPreferencesMenuItemPresent() { + if (isMac()) { + return callMethod("isPreferencesMenuItemPresent").equals(Boolean.TRUE); + } else { + return this.preferencesMenuItemPresent; + } + } + + public void removeAboutMenuItem() { + if (isMac()) { + callMethod("removeAboutMenuItem"); + } else { + this.aboutMenuItemPresent = false; + } + } + + public synchronized void removeApplicationListener(ApplicationListener applicationListener) { + if (isMac()) { + Object listener = listenerMap.get(applicationListener); + callMethod(application, "removeApplicationListener", new Class[]{applicationListenerClass}, new Object[]{listener}); + + } + listenerMap.remove(applicationListener); + } + + public void removePreferencesMenuItem() { + if (isMac()) { + callMethod("removeAboutMenuItem"); + } else { + this.preferencesMenuItemPresent = false; + } + } + + public void setEnabledAboutMenu(boolean enabled) { + if (isMac()) { + callMethod(application, "setEnabledAboutMenu", new Class[]{Boolean.TYPE}, new Object[]{Boolean.valueOf(enabled)}); + } else { + this.enabledAboutMenu = enabled; + } + } + + public void setEnabledPreferencesMenu(boolean enabled) { + if (isMac()) { + callMethod(application, "setEnabledPreferencesMenu", new Class[]{Boolean.TYPE}, new Object[]{Boolean.valueOf(enabled)}); + } else { + this.enabledPreferencesMenu = enabled; + } + + } + + public int requestUserAttention(int type) { + if (type != REQUEST_USER_ATTENTION_TYPE_CRITICAL && type != REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL) { + throw new IllegalArgumentException("Requested user attention type is not allowed: " + type); + } + try { + Object application = getNSApplication(); + Field critical = application.getClass().getField("UserAttentionRequestCritical"); + Field informational = application.getClass().getField("UserAttentionRequestInformational"); + Field actual = type == REQUEST_USER_ATTENTION_TYPE_CRITICAL ? critical : informational; + + return ((Integer) application.getClass().getMethod("requestUserAttention", new Class[]{Integer.TYPE}).invoke(application, new Object[]{actual.get(null)})).intValue(); + + } catch (ClassNotFoundException e) { + return -1; + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + public void cancelUserAttentionRequest(int request) { + try { + Object application = getNSApplication(); + application.getClass().getMethod("cancelUserAttentionRequest", new Class[]{Integer.TYPE}).invoke(application, new Object[]{new Integer(request)}); + } catch (ClassNotFoundException e) { + // Nada + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private Object getNSApplication() throws ClassNotFoundException { + try { + Class applicationClass = Class.forName("com.apple.cocoa.application.NSApplication"); + return applicationClass.getMethod("sharedApplication", new Class[0]).invoke(null, new Object[0]); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void setApplicationIconImage(BufferedImage image) { + if (isMac()) { + try { + Method setDockIconImage = application.getClass().getMethod("setDockIconImage", Image.class); + + try { + setDockIconImage.invoke(application, image); + } catch (IllegalAccessException e) { + + } catch (InvocationTargetException e) { + + } + } catch (NoSuchMethodException mnfe) { + + + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "png", stream); + } catch (IOException e) { + throw new RuntimeException(e); + } + + try { + Class nsDataClass = Class.forName("com.apple.cocoa.foundation.NSData"); + Constructor constructor = nsDataClass.getConstructor(new Class[]{new byte[0].getClass()}); + + Object nsData = constructor.newInstance(new Object[]{stream.toByteArray()}); + + Class nsImageClass = Class.forName("com.apple.cocoa.application.NSImage"); + Object nsImage = nsImageClass.getConstructor(new Class[]{nsDataClass}).newInstance(new Object[]{nsData}); + + Object application = getNSApplication(); + + application.getClass().getMethod("setApplicationIconImage", new Class[]{nsImageClass}).invoke(application, new Object[]{nsImage}); + + } catch (ClassNotFoundException e) { + + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } + + } + + } + } + + public BufferedImage getApplicationIconImage() { + if (isMac()) { + + try { + Method getDockIconImage = application.getClass().getMethod("getDockIconImage"); + try { + return (BufferedImage) getDockIconImage.invoke(application); + } catch (IllegalAccessException e) { + + } catch (InvocationTargetException e) { + + } + } catch (NoSuchMethodException nsme) { + + try { + Class nsDataClass = Class.forName("com.apple.cocoa.foundation.NSData"); + Class nsImageClass = Class.forName("com.apple.cocoa.application.NSImage"); + Object application = getNSApplication(); + Object nsImage = application.getClass().getMethod("applicationIconImage", new Class[0]).invoke(application, new Object[0]); + + Object nsData = nsImageClass.getMethod("TIFFRepresentation", new Class[0]).invoke(nsImage, new Object[0]); + + Integer length = (Integer) nsDataClass.getMethod("length", new Class[0]).invoke(nsData, new Object[0]); + byte[] bytes = (byte[]) nsDataClass.getMethod("bytes", new Class[]{Integer.TYPE, Integer.TYPE}).invoke(nsData, new Object[]{Integer.valueOf(0), length}); + + BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes)); + return image; + + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + } + + return null; + } + + private Object callMethod(String methodname) { + return callMethod(application, methodname, new Class[0], new Object[0]); + } + + private Object callMethod(Object object, String methodname) { + return callMethod(object, methodname, new Class[0], new Object[0]); + } + + private Object callMethod(Object object, String methodname, Class[] classes, Object[] arguments) { + try { + if (classes == null) { + classes = new Class[arguments.length]; + for (int i = 0; i < classes.length; i++) { + classes[i] = arguments[i].getClass(); + + } + } + Method addListnerMethod = object.getClass().getMethod(methodname, classes); + return addListnerMethod.invoke(object, arguments); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + class ApplicationListenerInvocationHandler implements InvocationHandler { + private ApplicationListener applicationListener; + + ApplicationListenerInvocationHandler(ApplicationListener applicationListener) { + this.applicationListener = applicationListener; + } + + public Object invoke(Object object, Method appleMethod, Object[] objects) throws Throwable { + + ApplicationEvent event = createApplicationEvent(objects[0]); + try { + Method method = applicationListener.getClass().getMethod(appleMethod.getName(), new Class[]{ApplicationEvent.class}); + return method.invoke(applicationListener, new Object[]{event}); + } catch (NoSuchMethodException e) { + if (appleMethod.getName().equals("equals") && objects.length == 1) { + return Boolean.valueOf(object == objects[0]); + } + return null; + } + } + } + + private ApplicationEvent createApplicationEvent(final Object appleApplicationEvent) { + return (ApplicationEvent) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ApplicationEvent.class}, new InvocationHandler() { + public Object invoke(Object o, Method method, Object[] objects) throws Throwable { + return appleApplicationEvent.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(appleApplicationEvent, objects); + } + }); + } +} diff --git a/libraries/libnbtplusplus b/libraries/libnbtplusplus new file mode 160000 index 00000000..5d0ffb50 --- /dev/null +++ b/libraries/libnbtplusplus @@ -0,0 +1 @@ +Subproject commit 5d0ffb50a526173ce58ae57136bf5d79a7e1920d diff --git a/libraries/logic/AbstractCommonModel.cpp b/libraries/logic/AbstractCommonModel.cpp deleted file mode 100644 index 71d75829..00000000 --- a/libraries/logic/AbstractCommonModel.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright 2015 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. - */ - -#include "AbstractCommonModel.h" - -BaseAbstractCommonModel::BaseAbstractCommonModel(const Qt::Orientation orientation, QObject *parent) - : QAbstractListModel(parent), m_orientation(orientation) -{ -} - -int BaseAbstractCommonModel::rowCount(const QModelIndex &parent) const -{ - return m_orientation == Qt::Horizontal ? entryCount() : size(); -} -int BaseAbstractCommonModel::columnCount(const QModelIndex &parent) const -{ - return m_orientation == Qt::Horizontal ? size() : entryCount(); -} -QVariant BaseAbstractCommonModel::data(const QModelIndex &index, int role) const -{ - if (!hasIndex(index.row(), index.column(), index.parent())) - { - return QVariant(); - } - const int i = m_orientation == Qt::Horizontal ? index.column() : index.row(); - const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column(); - return formatData(i, role, get(i, entry, role)); -} -QVariant BaseAbstractCommonModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != m_orientation && role == Qt::DisplayRole) - { - return entryTitle(section); - } - else - { - return QVariant(); - } -} -bool BaseAbstractCommonModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - const int i = m_orientation == Qt::Horizontal ? index.column() : index.row(); - const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column(); - const bool result = set(i, entry, role, sanetizeData(i, role, value)); - if (result) - { - emit dataChanged(index, index, QVector() << role); - } - return result; -} -Qt::ItemFlags BaseAbstractCommonModel::flags(const QModelIndex &index) const -{ - if (!hasIndex(index.row(), index.column(), index.parent())) - { - return Qt::NoItemFlags; - } - - const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column(); - if (canSet(entry)) - { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; - } - else - { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } -} - -void BaseAbstractCommonModel::notifyAboutToAddObject(const int at) -{ - if (m_orientation == Qt::Horizontal) - { - beginInsertColumns(QModelIndex(), at, at); - } - else - { - beginInsertRows(QModelIndex(), at, at); - } -} -void BaseAbstractCommonModel::notifyObjectAdded() -{ - if (m_orientation == Qt::Horizontal) - { - endInsertColumns(); - } - else - { - endInsertRows(); - } -} -void BaseAbstractCommonModel::notifyAboutToRemoveObject(const int at) -{ - if (m_orientation == Qt::Horizontal) - { - beginRemoveColumns(QModelIndex(), at, at); - } - else - { - beginRemoveRows(QModelIndex(), at, at); - } -} -void BaseAbstractCommonModel::notifyObjectRemoved() -{ - if (m_orientation == Qt::Horizontal) - { - endRemoveColumns(); - } - else - { - endRemoveRows(); - } -} - -void BaseAbstractCommonModel::notifyBeginReset() -{ - beginResetModel(); -} -void BaseAbstractCommonModel::notifyEndReset() -{ - endResetModel(); -} diff --git a/libraries/logic/AbstractCommonModel.h b/libraries/logic/AbstractCommonModel.h deleted file mode 100644 index 31b86a23..00000000 --- a/libraries/logic/AbstractCommonModel.h +++ /dev/null @@ -1,462 +0,0 @@ -/* Copyright 2015 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. - */ - -#pragma once - -#include -#include -#include -#include - -class BaseAbstractCommonModel : public QAbstractListModel -{ - Q_OBJECT -public: - explicit BaseAbstractCommonModel(const Qt::Orientation orientation, QObject *parent = nullptr); - - // begin QAbstractItemModel interface - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - Qt::ItemFlags flags(const QModelIndex &index) const override; - // end QAbstractItemModel interface - - virtual int size() const = 0; - virtual int entryCount() const = 0; - - virtual QVariant formatData(const int index, int role, const QVariant &data) const { return data; } - virtual QVariant sanetizeData(const int index, int role, const QVariant &data) const { return data; } - -protected: - virtual QVariant get(const int index, const int entry, const int role) const = 0; - virtual bool set(const int index, const int entry, const int role, const QVariant &value) = 0; - virtual bool canSet(const int entry) const = 0; - virtual QString entryTitle(const int entry) const = 0; - - void notifyAboutToAddObject(const int at); - void notifyObjectAdded(); - void notifyAboutToRemoveObject(const int at); - void notifyObjectRemoved(); - void notifyBeginReset(); - void notifyEndReset(); - - const Qt::Orientation m_orientation; -}; - -template -class AbstractCommonModel : public BaseAbstractCommonModel -{ -public: - explicit AbstractCommonModel(const Qt::Orientation orientation) - : BaseAbstractCommonModel(orientation) {} - virtual ~AbstractCommonModel() {} - - int size() const override { return m_objects.size(); } - int entryCount() const override { return m_entries.size(); } - - void append(const Object &object) - { - notifyAboutToAddObject(size()); - m_objects.append(object); - notifyObjectAdded(); - } - void prepend(const Object &object) - { - notifyAboutToAddObject(0); - m_objects.prepend(object); - notifyObjectAdded(); - } - void insert(const Object &object, const int index) - { - if (index >= size()) - { - prepend(object); - } - else if (index <= 0) - { - append(object); - } - else - { - notifyAboutToAddObject(index); - m_objects.insert(index, object); - notifyObjectAdded(); - } - } - void remove(const int index) - { - notifyAboutToRemoveObject(index); - m_objects.removeAt(index); - notifyObjectRemoved(); - } - Object get(const int index) const - { - return m_objects.at(index); - } - -private: - friend class CommonModel; - QVariant get(const int index, const int entry, const int role) const override - { - if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) - { - return QVariant(); - } - return m_entries[entry].second.value(role)->get(m_objects.at(index)); - } - bool set(const int index, const int entry, const int role, const QVariant &value) override - { - if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) - { - return false; - } - IEntry *e = m_entries[entry].second.value(role); - if (!e->canSet()) - { - return false; - } - e->set(m_objects[index], value); - return true; - } - bool canSet(const int entry) const override - { - if (m_entries.size() < entry || !m_entries[entry].second.contains(Qt::EditRole)) - { - return false; - } - IEntry *e = m_entries[entry].second.value(Qt::EditRole); - return e->canSet(); - } - - QString entryTitle(const int entry) const override - { - return m_entries.at(entry).first; - } - -private: - struct IEntry - { - virtual ~IEntry() {} - virtual void set(Object &object, const QVariant &value) = 0; - virtual QVariant get(const Object &object) const = 0; - virtual bool canSet() const = 0; - }; - template - struct VariableEntry : public IEntry - { - typedef T (Object::*Member); - - explicit VariableEntry(Member member) - : m_member(member) {} - - void set(Object &object, const QVariant &value) override - { - object.*m_member = value.value(); - } - QVariant get(const Object &object) const override - { - return QVariant::fromValue(object.*m_member); - } - bool canSet() const override { return true; } - - private: - Member m_member; - }; - template - struct FunctionEntry : public IEntry - { - typedef T (Object::*Getter)() const; - typedef void (Object::*Setter)(T); - - explicit FunctionEntry(Getter getter, Setter setter) - : m_getter(m_getter), m_setter(m_setter) {} - - void set(Object &object, const QVariant &value) override - { - object.*m_setter(value.value()); - } - QVariant get(const Object &object) const override - { - return QVariant::fromValue(object.*m_getter()); - } - bool canSet() const override { return !!m_setter; } - - private: - Getter m_getter; - Setter m_setter; - }; - - QList m_objects; - QVector>> m_entries; - - void addEntryInternal(IEntry *e, const int entry, const int role) - { - if (m_entries.size() <= entry) - { - m_entries.resize(entry + 1); - } - m_entries[entry].second.insert(role, e); - } - -protected: - template - typename std::enable_if::value && std::is_member_function_pointer::value, void>::type - addEntry(Getter getter, Setter setter, const int entry, const int role) - { - addEntryInternal(new FunctionEntry::type>(getter, setter), entry, role); - } - template - typename std::enable_if::value, void>::type - addEntry(Getter getter, const int entry, const int role) - { - addEntryInternal(new FunctionEntry::type>(getter, nullptr), entry, role); - } - template - typename std::enable_if::value, void>::type - addEntry(T (Object::*member), const int entry, const int role) - { - addEntryInternal(new VariableEntry(member), entry, role); - } - - void setEntryTitle(const int entry, const QString &title) - { - m_entries[entry].first = title; - } -}; -template -class AbstractCommonModel : public BaseAbstractCommonModel -{ -public: - explicit AbstractCommonModel(const Qt::Orientation orientation) - : BaseAbstractCommonModel(orientation) {} - virtual ~AbstractCommonModel() - { - qDeleteAll(m_objects); - } - - int size() const override { return m_objects.size(); } - int entryCount() const override { return m_entries.size(); } - - void append(Object *object) - { - notifyAboutToAddObject(size()); - m_objects.append(object); - notifyObjectAdded(); - } - void prepend(Object *object) - { - notifyAboutToAddObject(0); - m_objects.prepend(object); - notifyObjectAdded(); - } - void insert(Object *object, const int index) - { - if (index >= size()) - { - prepend(object); - } - else if (index <= 0) - { - append(object); - } - else - { - notifyAboutToAddObject(index); - m_objects.insert(index, object); - notifyObjectAdded(); - } - } - void remove(const int index) - { - notifyAboutToRemoveObject(index); - m_objects.removeAt(index); - notifyObjectRemoved(); - } - Object *get(const int index) const - { - return m_objects.at(index); - } - int find(Object * const obj) const - { - return m_objects.indexOf(obj); - } - - QList getAll() const - { - return m_objects; - } - -private: - friend class CommonModel; - QVariant get(const int index, const int entry, const int role) const override - { - if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) - { - return QVariant(); - } - return m_entries[entry].second.value(role)->get(m_objects.at(index)); - } - bool set(const int index, const int entry, const int role, const QVariant &value) override - { - if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) - { - return false; - } - IEntry *e = m_entries[entry].second.value(role); - if (!e->canSet()) - { - return false; - } - e->set(m_objects[index], value); - return true; - } - bool canSet(const int entry) const override - { - if (m_entries.size() < entry || !m_entries[entry].second.contains(Qt::EditRole)) - { - return false; - } - IEntry *e = m_entries[entry].second.value(Qt::EditRole); - return e->canSet(); - } - - QString entryTitle(const int entry) const override - { - return m_entries.at(entry).first; - } - -private: - struct IEntry - { - virtual ~IEntry() {} - virtual void set(Object *object, const QVariant &value) = 0; - virtual QVariant get(Object *object) const = 0; - virtual bool canSet() const = 0; - }; - template - struct VariableEntry : public IEntry - { - typedef T (Object::*Member); - - explicit VariableEntry(Member member) - : m_member(member) {} - - void set(Object *object, const QVariant &value) override - { - object->*m_member = value.value(); - } - QVariant get(Object *object) const override - { - return QVariant::fromValue(object->*m_member); - } - bool canSet() const override { return true; } - - private: - Member m_member; - }; - template - struct FunctionEntry : public IEntry - { - typedef T (Object::*Getter)() const; - typedef void (Object::*Setter)(T); - - explicit FunctionEntry(Getter getter, Setter setter) - : m_getter(getter), m_setter(setter) {} - - void set(Object *object, const QVariant &value) override - { - (object->*m_setter)(value.value()); - } - QVariant get(Object *object) const override - { - return QVariant::fromValue((object->*m_getter)()); - } - bool canSet() const override { return !!m_setter; } - - private: - Getter m_getter; - Setter m_setter; - }; - template - struct LambdaEntry : public IEntry - { - using Getter = std::function; - - explicit LambdaEntry(Getter getter) - : m_getter(getter) {} - - void set(Object *object, const QVariant &value) override {} - QVariant get(Object *object) const override - { - return QVariant::fromValue(m_getter(object)); - } - bool canSet() const override { return false; } - - private: - Getter m_getter; - }; - - QList m_objects; - QVector>> m_entries; - - void addEntryInternal(IEntry *e, const int entry, const int role) - { - if (m_entries.size() <= entry) - { - m_entries.resize(entry + 1); - } - m_entries[entry].second.insert(role, e); - } - -protected: - template - typename std::enable_if::value && std::is_member_function_pointer::value, void>::type - addEntry(const int entry, const int role, Getter getter, Setter setter) - { - addEntryInternal(new FunctionEntry::type>(getter, setter), entry, role); - } - template - typename std::enable_if::Getter>::value, void>::type - addEntry(const int entry, const int role, typename FunctionEntry::Getter getter) - { - addEntryInternal(new FunctionEntry(getter, nullptr), entry, role); - } - template - typename std::enable_if::value, void>::type - addEntry(const int entry, const int role, T (Object::*member)) - { - addEntryInternal(new VariableEntry(member), entry, role); - } - template - void addEntry(const int entry, const int role, typename LambdaEntry::Getter lambda) - { - addEntryInternal(new LambdaEntry(lambda), entry, role); - } - - void setEntryTitle(const int entry, const QString &title) - { - m_entries[entry].first = title; - } - - void setAll(const QList objects) - { - notifyBeginReset(); - qDeleteAll(m_objects); - m_objects = objects; - notifyEndReset(); - } -}; diff --git a/libraries/logic/BaseConfigObject.cpp b/libraries/logic/BaseConfigObject.cpp deleted file mode 100644 index 3040ac2e..00000000 --- a/libraries/logic/BaseConfigObject.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright 2015 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. - */ - -#include "BaseConfigObject.h" - -#include -#include -#include -#include - -#include "Exception.h" -#include "FileSystem.h" - -BaseConfigObject::BaseConfigObject(const QString &filename) - : m_filename(filename) -{ - m_saveTimer = new QTimer; - m_saveTimer->setSingleShot(true); - // cppcheck-suppress pureVirtualCall - QObject::connect(m_saveTimer, &QTimer::timeout, [this](){saveNow();}); - setSaveTimeout(250); - - m_initialReadTimer = new QTimer; - m_initialReadTimer->setSingleShot(true); - QObject::connect(m_initialReadTimer, &QTimer::timeout, [this]() - { - loadNow(); - m_initialReadTimer->deleteLater(); - m_initialReadTimer = 0; - }); - m_initialReadTimer->start(0); - - // cppcheck-suppress pureVirtualCall - m_appQuitConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this](){saveNow();}); -} -BaseConfigObject::~BaseConfigObject() -{ - delete m_saveTimer; - if (m_initialReadTimer) - { - delete m_initialReadTimer; - } - QObject::disconnect(m_appQuitConnection); -} - -void BaseConfigObject::setSaveTimeout(int msec) -{ - m_saveTimer->setInterval(msec); -} - -void BaseConfigObject::scheduleSave() -{ - m_saveTimer->stop(); - m_saveTimer->start(); -} -void BaseConfigObject::saveNow() -{ - if (m_saveTimer->isActive()) - { - m_saveTimer->stop(); - } - if (m_disableSaving) - { - return; - } - - try - { - FS::write(m_filename, doSave()); - } - catch (Exception & e) - { - qCritical() << e.cause(); - } -} -void BaseConfigObject::loadNow() -{ - if (m_saveTimer->isActive()) - { - saveNow(); - } - - try - { - doLoad(FS::read(m_filename)); - } - catch (Exception & e) - { - qWarning() << "Error loading" << m_filename << ":" << e.cause(); - } -} diff --git a/libraries/logic/BaseConfigObject.h b/libraries/logic/BaseConfigObject.h deleted file mode 100644 index 1c96b3d1..00000000 --- a/libraries/logic/BaseConfigObject.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright 2015 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. - */ - -#pragma once - -#include - -class QTimer; - -class BaseConfigObject -{ -public: - void setSaveTimeout(int msec); - -protected: - explicit BaseConfigObject(const QString &filename); - virtual ~BaseConfigObject(); - - // cppcheck-suppress pureVirtualCall - virtual QByteArray doSave() const = 0; - virtual void doLoad(const QByteArray &data) = 0; - - void setSavingDisabled(bool savingDisabled) { m_disableSaving = savingDisabled; } - - QString fileName() const { return m_filename; } - -public: - void scheduleSave(); - void saveNow(); - void loadNow(); - -private: - QTimer *m_saveTimer; - QTimer *m_initialReadTimer; - QString m_filename; - QMetaObject::Connection m_appQuitConnection; - bool m_disableSaving = false; -}; diff --git a/libraries/logic/BaseInstaller.cpp b/libraries/logic/BaseInstaller.cpp deleted file mode 100644 index cb762ebd..00000000 --- a/libraries/logic/BaseInstaller.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include - -#include "BaseInstaller.h" -#include "minecraft/onesix/OneSixInstance.h" - -BaseInstaller::BaseInstaller() -{ - -} - -bool BaseInstaller::isApplied(OneSixInstance *on) -{ - return QFile::exists(filename(on->instanceRoot())); -} - -bool BaseInstaller::add(OneSixInstance *to) -{ - if (!patchesDir(to->instanceRoot()).exists()) - { - QDir(to->instanceRoot()).mkdir("patches"); - } - - if (isApplied(to)) - { - if (!remove(to)) - { - return false; - } - } - - return true; -} - -bool BaseInstaller::remove(OneSixInstance *from) -{ - return QFile::remove(filename(from->instanceRoot())); -} - -QString BaseInstaller::filename(const QString &root) const -{ - return patchesDir(root).absoluteFilePath(id() + ".json"); -} -QDir BaseInstaller::patchesDir(const QString &root) const -{ - return QDir(root + "/patches/"); -} diff --git a/libraries/logic/BaseInstaller.h b/libraries/logic/BaseInstaller.h deleted file mode 100644 index a50c8cb1..00000000 --- a/libraries/logic/BaseInstaller.h +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include - -#include "multimc_logic_export.h" - -class OneSixInstance; -class QDir; -class QString; -class QObject; -class Task; -class BaseVersion; -typedef std::shared_ptr BaseVersionPtr; - -class MULTIMC_LOGIC_EXPORT BaseInstaller -{ -public: - BaseInstaller(); - virtual ~BaseInstaller(){}; - bool isApplied(OneSixInstance *on); - - virtual bool add(OneSixInstance *to); - virtual bool remove(OneSixInstance *from); - - virtual Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) = 0; - -protected: - virtual QString id() const = 0; - QString filename(const QString &root) const; - QDir patchesDir(const QString &root) const; -}; diff --git a/libraries/logic/BaseInstance.cpp b/libraries/logic/BaseInstance.cpp deleted file mode 100644 index ce55d5e4..00000000 --- a/libraries/logic/BaseInstance.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "BaseInstance.h" - -#include -#include - -#include "settings/INISettingsObject.h" -#include "settings/Setting.h" -#include "settings/OverrideSetting.h" - -#include "minecraft/MinecraftVersionList.h" -#include "FileSystem.h" -#include "Commandline.h" - -BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : QObject() -{ - m_settings = settings; - m_rootDir = rootDir; - - m_settings->registerSetting("name", "Unnamed Instance"); - m_settings->registerSetting("iconKey", "default"); - m_settings->registerSetting("notes", ""); - m_settings->registerSetting("lastLaunchTime", 0); - m_settings->registerSetting("totalTimePlayed", 0); - - // Custom Commands - auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false); - m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting); - m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); - m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); - - // Console - auto consoleSetting = m_settings->registerSetting("OverrideConsole", false); - m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting); - m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting); - m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting); -} - -QString BaseInstance::getPreLaunchCommand() -{ - return settings()->get("PreLaunchCommand").toString(); -} - -QString BaseInstance::getWrapperCommand() -{ - return settings()->get("WrapperCommand").toString(); -} - -QString BaseInstance::getPostExitCommand() -{ - return settings()->get("PostExitCommand").toString(); -} - -void BaseInstance::iconUpdated(QString key) -{ - if(iconKey() == key) - { - emit propertiesChanged(this); - } -} - -void BaseInstance::nuke() -{ - FS::deletePath(instanceRoot()); - emit nuked(this); -} - -QString BaseInstance::id() const -{ - return QFileInfo(instanceRoot()).fileName(); -} - -bool BaseInstance::isRunning() const -{ - return m_isRunning; -} - -void BaseInstance::setRunning(bool running) -{ - if(running && !m_isRunning) - { - m_timeStarted = QDateTime::currentDateTime(); - } - else if(!running && m_isRunning) - { - qint64 current = settings()->get("totalTimePlayed").toLongLong(); - QDateTime timeEnded = QDateTime::currentDateTime(); - settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded)); - emit propertiesChanged(this); - } - m_isRunning = running; -} - -int64_t BaseInstance::totalTimePlayed() const -{ - qint64 current = settings()->get("totalTimePlayed").toLongLong(); - if(m_isRunning) - { - QDateTime timeNow = QDateTime::currentDateTime(); - return current + m_timeStarted.secsTo(timeNow); - } - return current; -} - -void BaseInstance::resetTimePlayed() -{ - settings()->reset("totalTimePlayed"); -} - -QString BaseInstance::instanceType() const -{ - return m_settings->get("InstanceType").toString(); -} - -QString BaseInstance::instanceRoot() const -{ - return m_rootDir; -} - -InstancePtr BaseInstance::getSharedPtr() -{ - return shared_from_this(); -} - -SettingsObjectPtr BaseInstance::settings() const -{ - return m_settings; -} - -BaseInstance::InstanceFlags BaseInstance::flags() const -{ - return m_flags; -} - -void BaseInstance::setFlags(const InstanceFlags &flags) -{ - if (flags != m_flags) - { - m_flags = flags; - emit flagsChanged(); - emit propertiesChanged(this); - } -} - -void BaseInstance::setFlag(const BaseInstance::InstanceFlag flag) -{ - // nothing to set? - if(flag & m_flags) - return; - m_flags |= flag; - emit flagsChanged(); - emit propertiesChanged(this); -} - -void BaseInstance::unsetFlag(const BaseInstance::InstanceFlag flag) -{ - // nothing to unset? - if(!(flag & m_flags)) - return; - m_flags &= ~flag; - emit flagsChanged(); - emit propertiesChanged(this); -} - -bool BaseInstance::canLaunch() const -{ - return !(flags() & VersionBrokenFlag); -} - -bool BaseInstance::reload() -{ - return m_settings->reload(); -} - -qint64 BaseInstance::lastLaunch() const -{ - return m_settings->get("lastLaunchTime").value(); -} - -void BaseInstance::setLastLaunch(qint64 val) -{ - //FIXME: if no change, do not set. setting involves saving a file. - m_settings->set("lastLaunchTime", val); - emit propertiesChanged(this); -} - -void BaseInstance::setGroupInitial(QString val) -{ - if(m_group == val) - { - return; - } - m_group = val; - emit propertiesChanged(this); -} - -void BaseInstance::setGroupPost(QString val) -{ - if(m_group == val) - { - return; - } - setGroupInitial(val); - emit groupChanged(); -} - -QString BaseInstance::group() const -{ - return m_group; -} - -void BaseInstance::setNotes(QString val) -{ - //FIXME: if no change, do not set. setting involves saving a file. - m_settings->set("notes", val); -} - -QString BaseInstance::notes() const -{ - return m_settings->get("notes").toString(); -} - -void BaseInstance::setIconKey(QString val) -{ - //FIXME: if no change, do not set. setting involves saving a file. - m_settings->set("iconKey", val); - emit propertiesChanged(this); -} - -QString BaseInstance::iconKey() const -{ - return m_settings->get("iconKey").toString(); -} - -void BaseInstance::setName(QString val) -{ - //FIXME: if no change, do not set. setting involves saving a file. - m_settings->set("name", val); - emit propertiesChanged(this); -} - -QString BaseInstance::name() const -{ - return m_settings->get("name").toString(); -} - -QString BaseInstance::windowTitle() const -{ - return "MultiMC: " + name(); -} - -QStringList BaseInstance::extraArguments() const -{ - return Commandline::splitArgs(settings()->get("JvmArgs").toString()); -} diff --git a/libraries/logic/BaseInstance.h b/libraries/logic/BaseInstance.h deleted file mode 100644 index 5e587c48..00000000 --- a/libraries/logic/BaseInstance.h +++ /dev/null @@ -1,243 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include -#include - -#include "settings/SettingsObject.h" - -#include "settings/INIFile.h" -#include "BaseVersionList.h" -#include "minecraft/auth/MojangAccount.h" -#include "launch/MessageLevel.h" -#include "pathmatcher/IPathMatcher.h" - -#include "multimc_logic_export.h" - -class QDir; -class Task; -class LaunchTask; -class BaseInstance; - -// pointer for lazy people -typedef std::shared_ptr InstancePtr; - -/*! - * \brief Base class for instances. - * This class implements many functions that are common between instances and - * provides a standard interface for all instances. - * - * To create a new instance type, create a new class inheriting from this class - * and implement the pure virtual functions. - */ -class MULTIMC_LOGIC_EXPORT BaseInstance : public QObject, public std::enable_shared_from_this -{ - Q_OBJECT -protected: - /// no-touchy! - BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - -public: - /// virtual destructor to make sure the destruction is COMPLETE - virtual ~BaseInstance() {}; - - virtual void copy(const QDir &newDir) {} - - virtual void init() = 0; - - /// nuke thoroughly - deletes the instance contents, notifies the list/model which is - /// responsible of cleaning up the husk - void nuke(); - - /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to - /// be unique. - virtual QString id() const; - - void setRunning(bool running); - bool isRunning() const; - int64_t totalTimePlayed() const; - void resetTimePlayed(); - - /// get the type of this instance - QString instanceType() const; - - /// Path to the instance's root directory. - QString instanceRoot() const; - - QString name() const; - void setName(QString val); - - /// Value used for instance window titles - QString windowTitle() const; - - QString iconKey() const; - void setIconKey(QString val); - - QString notes() const; - void setNotes(QString val); - - QString group() const; - void setGroupInitial(QString val); - void setGroupPost(QString val); - - QString getPreLaunchCommand(); - QString getPostExitCommand(); - QString getWrapperCommand(); - - /// guess log level from a line of game log - virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) - { - return level; - }; - - virtual QStringList extraArguments() const; - - virtual QString intendedVersionId() const = 0; - virtual bool setIntendedVersionId(QString version) = 0; - - /*! - * The instance's current version. - * This value represents the instance's current version. If this value is - * different from the intendedVersion, the instance should be updated. - * \warning Don't change this value unless you know what you're doing. - */ - virtual QString currentVersionId() const = 0; - - /*! - * Whether or not 'the game' should be downloaded when the instance is launched. - */ - virtual bool shouldUpdate() const = 0; - virtual void setShouldUpdate(bool val) = 0; - - /// Traits. Normally inside the version, depends on instance implementation. - virtual QSet traits() = 0; - - /** - * Gets the time that the instance was last launched. - * Stored in milliseconds since epoch. - */ - qint64 lastLaunch() const; - /// Sets the last launched time to 'val' milliseconds since epoch - void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); - - InstancePtr getSharedPtr(); - - /*! - * \brief Gets a pointer to this instance's version list. - * \return A pointer to the available version list for this instance. - */ - virtual std::shared_ptr versionList() const = 0; - - /*! - * \brief Gets this instance's settings object. - * This settings object stores instance-specific settings. - * \return A pointer to this instance's settings object. - */ - virtual SettingsObjectPtr settings() const; - - /// returns a valid update task - virtual std::shared_ptr createUpdateTask() = 0; - - /// returns a valid launcher (task container) - virtual std::shared_ptr createLaunchTask(AuthSessionPtr account) = 0; - - /*! - * Returns a task that should be done right before launch - * This task should do any extra preparations needed - */ - virtual std::shared_ptr createJarModdingTask() = 0; - - /*! - * Create envrironment variables for running the instance - */ - virtual QProcessEnvironment createEnvironment() = 0; - - /*! - * Returns a matcher that can maps relative paths within the instance to whether they are 'log files' - */ - virtual IPathMatcher::Ptr getLogFileMatcher() = 0; - - /*! - * Returns the root folder to use for looking up log files - */ - virtual QString getLogFileRoot() = 0; - - /*! - * does any necessary cleanups after the instance finishes. also runs before\ - * TODO: turn into a task that can run asynchronously - */ - virtual void cleanupAfterRun() = 0; - - virtual QString getStatusbarDescription() = 0; - - /// FIXME: this really should be elsewhere... - virtual QString instanceConfigFolder() const = 0; - - /// get variables this instance exports - virtual QMap getVariables() const = 0; - - virtual QString typeName() const = 0; - - enum InstanceFlag - { - VersionBrokenFlag = 0x01, - UpdateAvailable = 0x02 - }; - Q_DECLARE_FLAGS(InstanceFlags, InstanceFlag) - InstanceFlags flags() const; - void setFlags(const InstanceFlags &flags); - void setFlag(const InstanceFlag flag); - void unsetFlag(const InstanceFlag flag); - - bool canLaunch() const; - virtual bool canExport() const = 0; - - virtual bool reload(); - -signals: - /*! - * \brief Signal emitted when properties relevant to the instance view change - */ - void propertiesChanged(BaseInstance *inst); - /*! - * \brief Signal emitted when groups are affected in any way - */ - void groupChanged(); - /*! - * \brief The instance just got nuked. Hurray! - */ - void nuked(BaseInstance *inst); - - void flagsChanged(); - -protected slots: - void iconUpdated(QString key); - -protected: - QString m_rootDir; - QString m_group; - SettingsObjectPtr m_settings; - InstanceFlags m_flags; - bool m_isRunning = false; - QDateTime m_timeStarted; -}; - -Q_DECLARE_METATYPE(std::shared_ptr) -Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) -Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) diff --git a/libraries/logic/BaseVersion.h b/libraries/logic/BaseVersion.h deleted file mode 100644 index 80767518..00000000 --- a/libraries/logic/BaseVersion.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -/*! - * An abstract base class for versions. - */ -class BaseVersion -{ -public: - virtual ~BaseVersion() {} - /*! - * A string used to identify this version in config files. - * This should be unique within the version list or shenanigans will occur. - */ - virtual QString descriptor() = 0; - - /*! - * The name of this version as it is displayed to the user. - * For example: "1.5.1" - */ - virtual QString name() = 0; - - /*! - * This should return a string that describes - * the kind of version this is (Stable, Beta, Snapshot, whatever) - */ - virtual QString typeString() const = 0; - - virtual bool operator<(BaseVersion &a) - { - return name() < a.name(); - }; - virtual bool operator>(BaseVersion &a) - { - return name() > a.name(); - }; -}; - -typedef std::shared_ptr BaseVersionPtr; - -Q_DECLARE_METATYPE(BaseVersionPtr) diff --git a/libraries/logic/BaseVersionList.cpp b/libraries/logic/BaseVersionList.cpp deleted file mode 100644 index b34f318c..00000000 --- a/libraries/logic/BaseVersionList.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "BaseVersionList.h" -#include "BaseVersion.h" - -BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent) -{ -} - -BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor) -{ - for (int i = 0; i < count(); i++) - { - if (at(i)->descriptor() == descriptor) - return at(i); - } - return BaseVersionPtr(); -} - -BaseVersionPtr BaseVersionList::getLatestStable() const -{ - if (count() <= 0) - return BaseVersionPtr(); - else - return at(0); -} - -BaseVersionPtr BaseVersionList::getRecommended() const -{ - return getLatestStable(); -} - -QVariant BaseVersionList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - BaseVersionPtr version = at(index.row()); - - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(version); - - case VersionRole: - return version->name(); - - case VersionIdRole: - return version->descriptor(); - - case TypeRole: - return version->typeString(); - - default: - return QVariant(); - } -} - -BaseVersionList::RoleList BaseVersionList::providesRoles() const -{ - return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole}; -} - -int BaseVersionList::rowCount(const QModelIndex &parent) const -{ - // Return count - return count(); -} - -int BaseVersionList::columnCount(const QModelIndex &parent) const -{ - return 1; -} - -QHash BaseVersionList::roleNames() const -{ - QHash roles = QAbstractListModel::roleNames(); - roles.insert(VersionRole, "version"); - roles.insert(VersionIdRole, "versionId"); - roles.insert(ParentGameVersionRole, "parentGameVersion"); - roles.insert(RecommendedRole, "recommended"); - roles.insert(LatestRole, "latest"); - roles.insert(TypeRole, "type"); - roles.insert(BranchRole, "branch"); - roles.insert(PathRole, "path"); - roles.insert(ArchitectureRole, "architecture"); - return roles; -} diff --git a/libraries/logic/BaseVersionList.h b/libraries/logic/BaseVersionList.h deleted file mode 100644 index 73d2ee1f..00000000 --- a/libraries/logic/BaseVersionList.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -#include "BaseVersion.h" -#include "tasks/Task.h" -#include "multimc_logic_export.h" - -/*! - * \brief Class that each instance type's version list derives from. - * Version lists are the lists that keep track of the available game versions - * for that instance. This list will not be loaded on startup. It will be loaded - * when the list's load function is called. Before using the version list, you - * should check to see if it has been loaded yet and if not, load the list. - * - * Note that this class also inherits from QAbstractListModel. Methods from that - * class determine how this version list shows up in a list view. Said methods - * all have a default implementation, but they can be overridden by plugins to - * change the behavior of the list. - */ -class MULTIMC_LOGIC_EXPORT BaseVersionList : public QAbstractListModel -{ - Q_OBJECT -public: - enum ModelRoles - { - VersionPointerRole = Qt::UserRole, - VersionRole, - VersionIdRole, - ParentGameVersionRole, - RecommendedRole, - LatestRole, - TypeRole, - BranchRole, - PathRole, - ArchitectureRole, - SortRole - }; - typedef QList RoleList; - - explicit BaseVersionList(QObject *parent = 0); - - /*! - * \brief Gets a task that will reload the version list. - * Simply execute the task to load the list. - * The task returned by this function should reset the model when it's done. - * \return A pointer to a task that reloads the version list. - */ - virtual Task *getLoadTask() = 0; - - //! Checks whether or not the list is loaded. If this returns false, the list should be - //loaded. - virtual bool isLoaded() = 0; - - //! Gets the version at the given index. - virtual const BaseVersionPtr at(int i) const = 0; - - //! Returns the number of versions in the list. - virtual int count() const = 0; - - //////// List Model Functions //////// - virtual QVariant data(const QModelIndex &index, int role) const; - virtual int rowCount(const QModelIndex &parent) const; - virtual int columnCount(const QModelIndex &parent) const; - virtual QHash roleNames() const override; - - //! which roles are provided by this version list? - virtual RoleList providesRoles() const; - - /*! - * \brief Finds a version by its descriptor. - * \param The descriptor of the version to find. - * \return A const pointer to the version with the given descriptor. NULL if - * one doesn't exist. - */ - virtual BaseVersionPtr findVersion(const QString &descriptor); - - /*! - * \brief Gets the latest stable version from this list - */ - virtual BaseVersionPtr getLatestStable() const; - - /*! - * \brief Gets the recommended version from this list - * If the list doesn't support recommended versions, this works exactly as getLatestStable - */ - virtual BaseVersionPtr getRecommended() const; - - /*! - * Sorts the version list. - */ - virtual void sortVersions() = 0; - -protected -slots: - /*! - * Updates this list with the given list of versions. - * This is done by copying each version in the given list and inserting it - * into this one. - * We need to do this so that we can set the parents of the versions are set to this - * version list. This can't be done in the load task, because the versions the load - * task creates are on the load task's thread and Qt won't allow their parents - * to be set to something created on another thread. - * To get around that problem, we invoke this method on the GUI thread, which - * then copies the versions and sets their parents correctly. - * \param versions List of versions whose parents should be set. - */ - virtual void updateListData(QList versions) = 0; -}; diff --git a/libraries/logic/CMakeLists.txt b/libraries/logic/CMakeLists.txt deleted file mode 100644 index 317627d5..00000000 --- a/libraries/logic/CMakeLists.txt +++ /dev/null @@ -1,344 +0,0 @@ -project(MultiMC_logic) - -set(LOGIC_SOURCES - # LOGIC - Base classes and infrastructure - BaseInstaller.h - BaseInstaller.cpp - BaseVersionList.h - BaseVersionList.cpp - InstanceList.h - InstanceList.cpp - BaseVersion.h - BaseInstance.h - BaseInstance.cpp - NullInstance.h - MMCZip.h - MMCZip.cpp - MMCStrings.h - MMCStrings.cpp - BaseConfigObject.h - BaseConfigObject.cpp - AbstractCommonModel.h - AbstractCommonModel.cpp - TypeMagic.h - - # Prefix tree where node names are strings between separators - SeparatorPrefixTree.h - - # WARNING: globals live here - Env.h - Env.cpp - - # JSON parsing helpers - Json.h - Json.cpp - - FileSystem.h - FileSystem.cpp - - Exception.h - - # RW lock protected map - RWStorage.h - - # A variable that has an implicit default value and keeps track of changes - DefaultVariable.h - - # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms - QObjectPtr.h - - # Resources - resources/Resource.cpp - resources/Resource.h - resources/ResourceHandler.cpp - resources/ResourceHandler.h - resources/ResourceObserver.cpp - resources/ResourceObserver.h - resources/ResourceProxyModel.h - resources/ResourceProxyModel.cpp - - # Path matchers - pathmatcher/FSTreeMatcher.h - pathmatcher/IPathMatcher.h - pathmatcher/MultiMatcher.h - pathmatcher/RegexpMatcher.h - - # Compression support - GZip.h - GZip.cpp - - # Command line parameter parsing - Commandline.h - Commandline.cpp - - # Version number string support - Version.h - Version.cpp - - # network stuffs - net/NetAction.h - net/MD5EtagDownload.h - net/MD5EtagDownload.cpp - net/ByteArrayDownload.h - net/ByteArrayDownload.cpp - net/CacheDownload.h - net/CacheDownload.cpp - net/NetJob.h - net/NetJob.cpp - net/HttpMetaCache.h - net/HttpMetaCache.cpp - net/PasteUpload.h - net/PasteUpload.cpp - net/URLConstants.h - net/URLConstants.cpp - - # Yggdrasil login stuff - minecraft/auth/AuthSession.h - minecraft/auth/AuthSession.cpp - minecraft/auth/MojangAccountList.h - minecraft/auth/MojangAccountList.cpp - minecraft/auth/MojangAccount.h - minecraft/auth/MojangAccount.cpp - minecraft/auth/YggdrasilTask.h - minecraft/auth/YggdrasilTask.cpp - minecraft/auth/flows/AuthenticateTask.h - minecraft/auth/flows/AuthenticateTask.cpp - minecraft/auth/flows/RefreshTask.cpp - minecraft/auth/flows/RefreshTask.cpp - minecraft/auth/flows/ValidateTask.h - minecraft/auth/flows/ValidateTask.cpp - - # Game launch logic - launch/steps/CheckJava.cpp - launch/steps/CheckJava.h - launch/steps/LaunchMinecraft.cpp - launch/steps/LaunchMinecraft.h - launch/steps/ModMinecraftJar.cpp - launch/steps/ModMinecraftJar.h - launch/steps/PostLaunchCommand.cpp - launch/steps/PostLaunchCommand.h - launch/steps/PreLaunchCommand.cpp - launch/steps/PreLaunchCommand.h - launch/steps/TextPrint.cpp - launch/steps/TextPrint.h - launch/steps/Update.cpp - launch/steps/Update.h - launch/LaunchStep.cpp - launch/LaunchStep.h - launch/LaunchTask.cpp - launch/LaunchTask.h - launch/LoggedProcess.cpp - launch/LoggedProcess.h - launch/MessageLevel.cpp - launch/MessageLevel.h - - # Update system - updater/GoUpdate.h - updater/GoUpdate.cpp - updater/UpdateChecker.h - updater/UpdateChecker.cpp - updater/DownloadTask.h - updater/DownloadTask.cpp - - # Notifications - short warning messages - notifications/NotificationChecker.h - notifications/NotificationChecker.cpp - - # News System - news/NewsChecker.h - news/NewsChecker.cpp - news/NewsEntry.h - news/NewsEntry.cpp - - # Status system - status/StatusChecker.h - status/StatusChecker.cpp - - # Minecraft support - minecraft/onesix/OneSixUpdate.h - minecraft/onesix/OneSixUpdate.cpp - minecraft/onesix/OneSixInstance.h - minecraft/onesix/OneSixInstance.cpp - minecraft/onesix/OneSixProfileStrategy.cpp - minecraft/onesix/OneSixProfileStrategy.h - minecraft/onesix/OneSixVersionFormat.cpp - minecraft/onesix/OneSixVersionFormat.h - minecraft/legacy/LegacyUpdate.h - minecraft/legacy/LegacyUpdate.cpp - minecraft/legacy/LegacyInstance.h - minecraft/legacy/LegacyInstance.cpp - minecraft/legacy/LwjglVersionList.h - minecraft/legacy/LwjglVersionList.cpp - minecraft/GradleSpecifier.h - minecraft/MinecraftProfile.cpp - minecraft/MinecraftProfile.h - minecraft/MojangVersionFormat.cpp - minecraft/MojangVersionFormat.h - minecraft/JarMod.h - minecraft/MinecraftInstance.cpp - minecraft/MinecraftInstance.h - minecraft/MinecraftVersion.cpp - minecraft/MinecraftVersion.h - minecraft/MinecraftVersionList.cpp - minecraft/MinecraftVersionList.h - minecraft/Rule.cpp - minecraft/Rule.h - minecraft/OpSys.cpp - minecraft/OpSys.h - minecraft/ParseUtils.cpp - minecraft/ParseUtils.h - minecraft/ProfileUtils.cpp - minecraft/ProfileUtils.h - minecraft/ProfileStrategy.h - minecraft/Library.cpp - minecraft/Library.h - minecraft/MojangDownloadInfo.h - minecraft/VersionBuildError.h - minecraft/VersionFile.cpp - minecraft/VersionFile.h - minecraft/ProfilePatch.h - minecraft/VersionFilterData.h - minecraft/VersionFilterData.cpp - minecraft/Mod.h - minecraft/Mod.cpp - minecraft/ModList.h - minecraft/ModList.cpp - minecraft/World.h - minecraft/World.cpp - minecraft/WorldList.h - minecraft/WorldList.cpp - - # FTB - minecraft/ftb/OneSixFTBInstance.h - minecraft/ftb/OneSixFTBInstance.cpp - minecraft/ftb/LegacyFTBInstance.h - minecraft/ftb/LegacyFTBInstance.cpp - minecraft/ftb/FTBProfileStrategy.h - minecraft/ftb/FTBProfileStrategy.cpp - minecraft/ftb/FTBPlugin.h - minecraft/ftb/FTBPlugin.cpp - - # A Recursive file system watcher - RecursiveFileSystemWatcher.h - RecursiveFileSystemWatcher.cpp - - # the screenshots feature - screenshots/Screenshot.h - screenshots/ImgurUpload.h - screenshots/ImgurUpload.cpp - screenshots/ImgurAlbumCreation.h - screenshots/ImgurAlbumCreation.cpp - - # Tasks - tasks/Task.h - tasks/Task.cpp - tasks/ThreadTask.h - tasks/ThreadTask.cpp - tasks/SequentialTask.h - tasks/SequentialTask.cpp - - # Settings - settings/INIFile.cpp - settings/INIFile.h - settings/INISettingsObject.cpp - settings/INISettingsObject.h - settings/OverrideSetting.cpp - settings/OverrideSetting.h - settings/PassthroughSetting.cpp - settings/PassthroughSetting.h - settings/Setting.cpp - settings/Setting.h - settings/SettingsObject.cpp - settings/SettingsObject.h - - # Java related code - java/JavaChecker.h - java/JavaChecker.cpp - java/JavaCheckerJob.h - java/JavaCheckerJob.cpp - java/JavaInstall.h - java/JavaInstall.cpp - java/JavaInstallList.h - java/JavaInstallList.cpp - java/JavaUtils.h - java/JavaUtils.cpp - java/JavaVersion.h - java/JavaVersion.cpp - - # Assets - minecraft/AssetsUtils.h - minecraft/AssetsUtils.cpp - - # Forge and all things forge related - minecraft/forge/ForgeVersion.h - minecraft/forge/ForgeVersion.cpp - minecraft/forge/ForgeVersionList.h - minecraft/forge/ForgeVersionList.cpp - minecraft/forge/ForgeXzDownload.h - minecraft/forge/ForgeXzDownload.cpp - minecraft/forge/LegacyForge.h - minecraft/forge/LegacyForge.cpp - minecraft/forge/ForgeInstaller.h - minecraft/forge/ForgeInstaller.cpp - - # Liteloader and related things - minecraft/liteloader/LiteLoaderInstaller.h - minecraft/liteloader/LiteLoaderInstaller.cpp - minecraft/liteloader/LiteLoaderVersionList.h - minecraft/liteloader/LiteLoaderVersionList.cpp - - # Translations - trans/TranslationDownloader.h - trans/TranslationDownloader.cpp - - # Tools - tools/BaseExternalTool.cpp - tools/BaseExternalTool.h - tools/BaseProfiler.cpp - tools/BaseProfiler.h - tools/JProfiler.cpp - tools/JProfiler.h - tools/JVisualVM.cpp - tools/JVisualVM.h - tools/MCEditTool.cpp - tools/MCEditTool.h - - # Wonko - wonko/tasks/BaseWonkoEntityRemoteLoadTask.cpp - wonko/tasks/BaseWonkoEntityRemoteLoadTask.h - wonko/tasks/BaseWonkoEntityLocalLoadTask.cpp - wonko/tasks/BaseWonkoEntityLocalLoadTask.h - wonko/format/WonkoFormatV1.cpp - wonko/format/WonkoFormatV1.h - wonko/format/WonkoFormat.cpp - wonko/format/WonkoFormat.h - wonko/BaseWonkoEntity.cpp - wonko/BaseWonkoEntity.h - wonko/WonkoVersionList.cpp - wonko/WonkoVersionList.h - wonko/WonkoVersion.cpp - wonko/WonkoVersion.h - wonko/WonkoIndex.cpp - wonko/WonkoIndex.h - wonko/WonkoUtil.cpp - wonko/WonkoUtil.h - wonko/WonkoReference.cpp - wonko/WonkoReference.h -) -################################ COMPILE ################################ - -# we need zlib -find_package(ZLIB REQUIRED) - -add_library(MultiMC_logic SHARED ${LOGIC_SOURCES}) -set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1) - -generate_export_header(MultiMC_logic) - -# Link -target_link_libraries(MultiMC_logic xz-embedded unpack200 ${QUAZIP_LIBRARIES} nbt++ ${ZLIB_LIBRARIES}) -qt5_use_modules(MultiMC_logic Core Xml Network Concurrent) -add_dependencies(MultiMC_logic QuaZIP) - -# Mark and export headers -target_include_directories(MultiMC_logic PUBLIC "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" PRIVATE "${ZLIB_INCLUDE_DIRS}") diff --git a/libraries/logic/Commandline.cpp b/libraries/logic/Commandline.cpp deleted file mode 100644 index 9a8ddbf1..00000000 --- a/libraries/logic/Commandline.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Authors: Orochimarufan - * - * 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. - */ - -#include "Commandline.h" - -/** - * @file libutil/src/cmdutils.cpp - */ - -namespace Commandline -{ - -// commandline splitter -QStringList splitArgs(QString args) -{ - QStringList argv; - QString current; - bool escape = false; - QChar inquotes; - for (int i = 0; i < args.length(); i++) - { - QChar cchar = args.at(i); - - // \ escaped - if (escape) - { - current += cchar; - escape = false; - // in "quotes" - } - else if (!inquotes.isNull()) - { - if (cchar == 0x5C) - escape = true; - else if (cchar == inquotes) - inquotes = 0; - else - current += cchar; - // otherwise - } - else - { - if (cchar == 0x20) - { - if (!current.isEmpty()) - { - argv << current; - current.clear(); - } - } - else if (cchar == 0x22 || cchar == 0x27) - inquotes = cchar; - else - current += cchar; - } - } - if (!current.isEmpty()) - argv << current; - return argv; -} - -Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle) -{ - m_flagStyle = flagStyle; - m_argStyle = argStyle; -} - -// styles setter/getter -void Parser::setArgumentStyle(ArgumentStyle::Enum style) -{ - m_argStyle = style; -} -ArgumentStyle::Enum Parser::argumentStyle() -{ - return m_argStyle; -} - -void Parser::setFlagStyle(FlagStyle::Enum style) -{ - m_flagStyle = style; -} -FlagStyle::Enum Parser::flagStyle() -{ - return m_flagStyle; -} - -// setup methods -void Parser::addSwitch(QString name, bool def) -{ - if (m_params.contains(name)) - throw "Name not unique"; - - OptionDef *param = new OptionDef; - param->type = otSwitch; - param->name = name; - param->metavar = QString("<%1>").arg(name); - param->def = def; - - m_options[name] = param; - m_params[name] = (CommonDef *)param; - m_optionList.append(param); -} - -void Parser::addOption(QString name, QVariant def) -{ - if (m_params.contains(name)) - throw "Name not unique"; - - OptionDef *param = new OptionDef; - param->type = otOption; - param->name = name; - param->metavar = QString("<%1>").arg(name); - param->def = def; - - m_options[name] = param; - m_params[name] = (CommonDef *)param; - m_optionList.append(param); -} - -void Parser::addArgument(QString name, bool required, QVariant def) -{ - if (m_params.contains(name)) - throw "Name not unique"; - - PositionalDef *param = new PositionalDef; - param->name = name; - param->def = def; - param->required = required; - param->metavar = name; - - m_positionals.append(param); - m_params[name] = (CommonDef *)param; -} - -void Parser::addDocumentation(QString name, QString doc, QString metavar) -{ - if (!m_params.contains(name)) - throw "Name does not exist"; - - CommonDef *param = m_params[name]; - param->doc = doc; - if (!metavar.isNull()) - param->metavar = metavar; -} - -void Parser::addShortOpt(QString name, QChar flag) -{ - if (!m_params.contains(name)) - throw "Name does not exist"; - if (!m_options.contains(name)) - throw "Name is not an Option or Swtich"; - - OptionDef *param = m_options[name]; - m_flags[flag] = param; - param->flag = flag; -} - -// help methods -QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags) -{ - QStringList help; - help << compileUsage(progName, useFlags) << "\r\n"; - - // positionals - if (!m_positionals.isEmpty()) - { - help << "\r\n"; - help << "Positional arguments:\r\n"; - QListIterator it2(m_positionals); - while (it2.hasNext()) - { - PositionalDef *param = it2.next(); - help << " " << param->metavar; - help << " " << QString(helpIndent - param->metavar.length() - 1, ' '); - help << param->doc << "\r\n"; - } - } - - // Options - if (!m_optionList.isEmpty()) - { - help << "\r\n"; - QString optPrefix, flagPrefix; - getPrefix(optPrefix, flagPrefix); - - help << "Options & Switches:\r\n"; - QListIterator it(m_optionList); - while (it.hasNext()) - { - OptionDef *option = it.next(); - help << " "; - int nameLength = optPrefix.length() + option->name.length(); - if (!option->flag.isNull()) - { - nameLength += 3 + flagPrefix.length(); - help << flagPrefix << option->flag << ", "; - } - help << optPrefix << option->name; - if (option->type == otOption) - { - QString arg = QString("%1%2").arg( - ((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar); - nameLength += arg.length(); - help << arg; - } - help << " " << QString(helpIndent - nameLength - 1, ' '); - help << option->doc << "\r\n"; - } - } - - return help.join(""); -} - -QString Parser::compileUsage(QString progName, bool useFlags) -{ - QStringList usage; - usage << "Usage: " << progName; - - QString optPrefix, flagPrefix; - getPrefix(optPrefix, flagPrefix); - - // options - QListIterator it(m_optionList); - while (it.hasNext()) - { - OptionDef *option = it.next(); - usage << " ["; - if (!option->flag.isNull() && useFlags) - usage << flagPrefix << option->flag; - else - usage << optPrefix << option->name; - if (option->type == otOption) - usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar; - usage << "]"; - } - - // arguments - QListIterator it2(m_positionals); - while (it2.hasNext()) - { - PositionalDef *param = it2.next(); - usage << " " << (param->required ? "<" : "["); - usage << param->metavar; - usage << (param->required ? ">" : "]"); - } - - return usage.join(""); -} - -// parsing -QHash Parser::parse(QStringList argv) -{ - QHash map; - - QStringListIterator it(argv); - QString programName = it.next(); - - QString optionPrefix; - QString flagPrefix; - QListIterator positionals(m_positionals); - QStringList expecting; - - getPrefix(optionPrefix, flagPrefix); - - while (it.hasNext()) - { - QString arg = it.next(); - - if (!expecting.isEmpty()) - // we were expecting an argument - { - QString name = expecting.first(); -/* - if (map.contains(name)) - throw ParsingError( - QString("Option %2%1 was given multiple times").arg(name, optionPrefix)); -*/ - map[name] = QVariant(arg); - - expecting.removeFirst(); - continue; - } - - if (arg.startsWith(optionPrefix)) - // we have an option - { - // qDebug("Found option %s", qPrintable(arg)); - - QString name = arg.mid(optionPrefix.length()); - QString equals; - - if ((m_argStyle == ArgumentStyle::Equals || - m_argStyle == ArgumentStyle::SpaceAndEquals) && - name.contains("=")) - { - int i = name.indexOf("="); - equals = name.mid(i + 1); - name = name.left(i); - } - - if (m_options.contains(name)) - { - /* - if (map.contains(name)) - throw ParsingError(QString("Option %2%1 was given multiple times") - .arg(name, optionPrefix)); -*/ - OptionDef *option = m_options[name]; - if (option->type == otSwitch) - map[name] = true; - else // if (option->type == otOption) - { - if (m_argStyle == ArgumentStyle::Space) - expecting.append(name); - else if (!equals.isNull()) - map[name] = equals; - else if (m_argStyle == ArgumentStyle::SpaceAndEquals) - expecting.append(name); - else - throw ParsingError(QString("Option %2%1 reqires an argument.") - .arg(name, optionPrefix)); - } - - continue; - } - - throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix)); - } - - if (arg.startsWith(flagPrefix)) - // we have (a) flag(s) - { - // qDebug("Found flags %s", qPrintable(arg)); - - QString flags = arg.mid(flagPrefix.length()); - QString equals; - - if ((m_argStyle == ArgumentStyle::Equals || - m_argStyle == ArgumentStyle::SpaceAndEquals) && - flags.contains("=")) - { - int i = flags.indexOf("="); - equals = flags.mid(i + 1); - flags = flags.left(i); - } - - for (int i = 0; i < flags.length(); i++) - { - QChar flag = flags.at(i); - - if (!m_flags.contains(flag)) - throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix)); - - OptionDef *option = m_flags[flag]; -/* - if (map.contains(option->name)) - throw ParsingError(QString("Option %2%1 was given multiple times") - .arg(option->name, optionPrefix)); -*/ - if (option->type == otSwitch) - map[option->name] = true; - else // if (option->type == otOption) - { - if (m_argStyle == ArgumentStyle::Space) - expecting.append(option->name); - else if (!equals.isNull()) - if (i == flags.length() - 1) - map[option->name] = equals; - else - throw ParsingError(QString("Flag %4%2 of Argument-requiring Option " - "%1 not last flag in %4%3") - .arg(option->name, flag, flags, flagPrefix)); - else if (m_argStyle == ArgumentStyle::SpaceAndEquals) - expecting.append(option->name); - else - throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)") - .arg(option->name, flag, flagPrefix)); - } - } - - continue; - } - - // must be a positional argument - if (!positionals.hasNext()) - throw ParsingError(QString("Don't know what to do with '%1'").arg(arg)); - - PositionalDef *param = positionals.next(); - - map[param->name] = arg; - } - - // check if we're missing something - if (!expecting.isEmpty()) - throw ParsingError(QString("Was still expecting arguments for %2%1").arg( - expecting.join(QString(", ") + optionPrefix), optionPrefix)); - - while (positionals.hasNext()) - { - PositionalDef *param = positionals.next(); - if (param->required) - throw ParsingError( - QString("Missing required positional argument '%1'").arg(param->name)); - else - map[param->name] = param->def; - } - - // fill out gaps - QListIterator iter(m_optionList); - while (iter.hasNext()) - { - OptionDef *option = iter.next(); - if (!map.contains(option->name)) - map[option->name] = option->def; - } - - return map; -} - -// clear defs -void Parser::clear() -{ - m_flags.clear(); - m_params.clear(); - m_options.clear(); - - QMutableListIterator it(m_optionList); - while (it.hasNext()) - { - OptionDef *option = it.next(); - it.remove(); - delete option; - } - - QMutableListIterator it2(m_positionals); - while (it2.hasNext()) - { - PositionalDef *arg = it2.next(); - it2.remove(); - delete arg; - } -} - -// Destructor -Parser::~Parser() -{ - clear(); -} - -// getPrefix -void Parser::getPrefix(QString &opt, QString &flag) -{ - if (m_flagStyle == FlagStyle::Windows) - opt = flag = "/"; - else if (m_flagStyle == FlagStyle::Unix) - opt = flag = "-"; - // else if (m_flagStyle == FlagStyle::GNU) - else - { - opt = "--"; - flag = "-"; - } -} - -// ParsingError -ParsingError::ParsingError(const QString &what) : std::runtime_error(what.toStdString()) -{ -} -} \ No newline at end of file diff --git a/libraries/logic/Commandline.h b/libraries/logic/Commandline.h deleted file mode 100644 index bee02bad..00000000 --- a/libraries/logic/Commandline.h +++ /dev/null @@ -1,252 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Authors: Orochimarufan - * - * 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. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include - -#include "multimc_logic_export.h" - -/** - * @file libutil/include/cmdutils.h - * @brief commandline parsing and processing utilities - */ - -namespace Commandline -{ - -/** - * @brief split a string into argv items like a shell would do - * @param args the argument string - * @return a QStringList containing all arguments - */ -MULTIMC_LOGIC_EXPORT QStringList splitArgs(QString args); - -/** - * @brief The FlagStyle enum - * Specifies how flags are decorated - */ - -namespace FlagStyle -{ -enum Enum -{ - GNU, /**< --option and -o (GNU Style) */ - Unix, /**< -option and -o (Unix Style) */ - Windows, /**< /option and /o (Windows Style) */ -#ifdef Q_OS_WIN32 - Default = Windows -#else - Default = GNU -#endif -}; -} - -/** - * @brief The ArgumentStyle enum - */ -namespace ArgumentStyle -{ -enum Enum -{ - Space, /**< --option=value */ - Equals, /**< --option value */ - SpaceAndEquals, /**< --option[= ]value */ -#ifdef Q_OS_WIN32 - Default = Equals -#else - Default = SpaceAndEquals -#endif -}; -} - -/** - * @brief The ParsingError class - */ -class MULTIMC_LOGIC_EXPORT ParsingError : public std::runtime_error -{ -public: - ParsingError(const QString &what); -}; - -/** - * @brief The Parser class - */ -class MULTIMC_LOGIC_EXPORT Parser -{ -public: - /** - * @brief Parser constructor - * @param flagStyle the FlagStyle to use in this Parser - * @param argStyle the ArgumentStyle to use in this Parser - */ - Parser(FlagStyle::Enum flagStyle = FlagStyle::Default, - ArgumentStyle::Enum argStyle = ArgumentStyle::Default); - - /** - * @brief set the flag style - * @param style - */ - void setFlagStyle(FlagStyle::Enum style); - - /** - * @brief get the flag style - * @return - */ - FlagStyle::Enum flagStyle(); - - /** - * @brief set the argument style - * @param style - */ - void setArgumentStyle(ArgumentStyle::Enum style); - - /** - * @brief get the argument style - * @return - */ - ArgumentStyle::Enum argumentStyle(); - - /** - * @brief define a boolean switch - * @param name the parameter name - * @param def the default value - */ - void addSwitch(QString name, bool def = false); - - /** - * @brief define an option that takes an additional argument - * @param name the parameter name - * @param def the default value - */ - void addOption(QString name, QVariant def = QVariant()); - - /** - * @brief define a positional argument - * @param name the parameter name - * @param required wether this argument is required - * @param def the default value - */ - void addArgument(QString name, bool required = true, QVariant def = QVariant()); - - /** - * @brief adds a flag to an existing parameter - * @param name the (existing) parameter name - * @param flag the flag character - * @see addSwitch addArgument addOption - * Note: any one parameter can only have one flag - */ - void addShortOpt(QString name, QChar flag); - - /** - * @brief adds documentation to a Parameter - * @param name the parameter name - * @param metavar a string to be displayed as placeholder for the value - * @param doc a QString containing the documentation - * Note: on positional arguments, metavar replaces the name as displayed. - * on options , metavar replaces the value placeholder - */ - void addDocumentation(QString name, QString doc, QString metavar = QString()); - - /** - * @brief generate a help message - * @param progName the program name to use in the help message - * @param helpIndent how much the parameter documentation should be indented - * @param flagsInUsage whether we should use flags instead of options in the usage - * @return a help message - */ - QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true); - - /** - * @brief generate a short usage message - * @param progName the program name to use in the usage message - * @param useFlags whether we should use flags instead of options - * @return a usage message - */ - QString compileUsage(QString progName, bool useFlags = true); - - /** - * @brief parse - * @param argv a QStringList containing the program ARGV - * @return a QHash mapping argument names to their values - */ - QHash parse(QStringList argv); - - /** - * @brief clear all definitions - */ - void clear(); - - ~Parser(); - -private: - FlagStyle::Enum m_flagStyle; - ArgumentStyle::Enum m_argStyle; - - enum OptionType - { - otSwitch, - otOption - }; - - // Important: the common part MUST BE COMMON ON ALL THREE structs - struct CommonDef - { - QString name; - QString doc; - QString metavar; - QVariant def; - }; - - struct OptionDef - { - // common - QString name; - QString doc; - QString metavar; - QVariant def; - // option - OptionType type; - QChar flag; - }; - - struct PositionalDef - { - // common - QString name; - QString doc; - QString metavar; - QVariant def; - // positional - bool required; - }; - - QHash m_options; - QHash m_flags; - QHash m_params; - QList m_positionals; - QList m_optionList; - - void getPrefix(QString &opt, QString &flag); -}; -} diff --git a/libraries/logic/DefaultVariable.h b/libraries/logic/DefaultVariable.h deleted file mode 100644 index 38d7ecc2..00000000 --- a/libraries/logic/DefaultVariable.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -template -class DefaultVariable -{ -public: - DefaultVariable(const T & value) - { - defaultValue = value; - } - DefaultVariable & operator =(const T & value) - { - currentValue = value; - is_default = currentValue == defaultValue; - is_explicit = true; - return *this; - } - operator const T &() const - { - return is_default ? defaultValue : currentValue; - } - bool isDefault() const - { - return is_default; - } - bool isExplicit() const - { - return is_explicit; - } -private: - T currentValue; - T defaultValue; - bool is_default = true; - bool is_explicit = false; -}; diff --git a/libraries/logic/Env.cpp b/libraries/logic/Env.cpp deleted file mode 100644 index cc0c5981..00000000 --- a/libraries/logic/Env.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "Env.h" -#include "net/HttpMetaCache.h" -#include "BaseVersion.h" -#include "BaseVersionList.h" -#include -#include -#include -#include -#include "tasks/Task.h" -#include "wonko/WonkoIndex.h" -#include - -/* - * The *NEW* global rat nest of an object. Handle with care. - */ - -Env::Env() -{ - m_qnam = std::make_shared(); -} - -void Env::destroy() -{ - m_metacache.reset(); - m_qnam.reset(); - m_versionLists.clear(); -} - -Env& Env::Env::getInstance() -{ - static Env instance; - return instance; -} - -std::shared_ptr< HttpMetaCache > Env::metacache() -{ - Q_ASSERT(m_metacache != nullptr); - return m_metacache; -} - -std::shared_ptr< QNetworkAccessManager > Env::qnam() -{ - return m_qnam; -} - -/* -class NullVersion : public BaseVersion -{ - Q_OBJECT -public: - virtual QString name() - { - return "null"; - } - virtual QString descriptor() - { - return "null"; - } - virtual QString typeString() const - { - return "Null"; - } -}; - -class NullTask: public Task -{ - Q_OBJECT -public: - virtual void executeTask() - { - emitFailed(tr("Nothing to do.")); - } -}; - -class NullVersionList: public BaseVersionList -{ - Q_OBJECT -public: - virtual const BaseVersionPtr at(int i) const - { - return std::make_shared(); - } - virtual int count() const - { - return 0; - }; - virtual Task* getLoadTask() - { - return new NullTask; - } - virtual bool isLoaded() - { - return false; - } - virtual void sort() - { - } - virtual void updateListData(QList< BaseVersionPtr >) - { - } -}; -*/ - -BaseVersionPtr Env::getVersion(QString component, QString version) -{ - auto list = getVersionList(component); - if(!list) - { - return nullptr; - } - return list->findVersion(version); -} - -std::shared_ptr< BaseVersionList > Env::getVersionList(QString component) -{ - auto iter = m_versionLists.find(component); - if(iter != m_versionLists.end()) - { - return *iter; - } - //return std::make_shared(); - return nullptr; -} - -void Env::registerVersionList(QString name, std::shared_ptr< BaseVersionList > vlist) -{ - m_versionLists[name] = vlist; -} - -std::shared_ptr Env::wonkoIndex() -{ - if (!m_wonkoIndex) - { - m_wonkoIndex = std::make_shared(); - } - return m_wonkoIndex; -} - - -void Env::initHttpMetaCache() -{ - 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("skins", QDir("accounts/skins").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("wonko", QDir("cache/wonko").absolutePath()); - m_metacache->Load(); -} - -void Env::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(); - if (m_qnam.get()) - m_qnam->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("%3@%1:%2 pass %4") - .arg(proxy.hostName()) - .arg(proxy.port()) - .arg(proxy.user()) - .arg(proxy.password()); - qDebug() << proxyDesc; -} - -#include "Env.moc" diff --git a/libraries/logic/Env.h b/libraries/logic/Env.h deleted file mode 100644 index 4d8945d7..00000000 --- a/libraries/logic/Env.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "multimc_logic_export.h" - -class QNetworkAccessManager; -class HttpMetaCache; -class BaseVersionList; -class BaseVersion; -class WonkoIndex; - -#if defined(ENV) - #undef ENV -#endif -#define ENV (Env::getInstance()) - -class MULTIMC_LOGIC_EXPORT Env -{ - friend class MultiMC; -private: - Env(); -public: - static Env& getInstance(); - - // call when Qt stuff is being torn down - void destroy(); - - std::shared_ptr qnam(); - - std::shared_ptr metacache(); - - /// init the cache. FIXME: possible future hook point - void initHttpMetaCache(); - - /// Updates the application proxy settings from the settings object. - void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); - - /// get a version list by name - std::shared_ptr getVersionList(QString component); - - /// get a version by list name and version name - std::shared_ptr getVersion(QString component, QString version); - - void registerVersionList(QString name, std::shared_ptr vlist); - - std::shared_ptr wonkoIndex(); - - QString wonkoRootUrl() const { return m_wonkoRootUrl; } - void setWonkoRootUrl(const QString &url) { m_wonkoRootUrl = url; } - -protected: - std::shared_ptr m_qnam; - std::shared_ptr m_metacache; - QMap> m_versionLists; - std::shared_ptr m_wonkoIndex; - QString m_wonkoRootUrl; -}; diff --git a/libraries/logic/Exception.h b/libraries/logic/Exception.h deleted file mode 100644 index 30c7aa45..00000000 --- a/libraries/logic/Exception.h +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed under the Apache-2.0 license. See README.md for details. - -#pragma once - -#include -#include -#include - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT Exception : public std::exception -{ -public: - Exception(const QString &message) : std::exception(), m_message(message) - { - qCritical() << "Exception:" << message; - } - Exception(const Exception &other) - : std::exception(), m_message(other.cause()) - { - } - virtual ~Exception() noexcept {} - const char *what() const noexcept - { - return m_message.toLatin1().constData(); - } - QString cause() const - { - return m_message; - } - -private: - QString m_message; -}; diff --git a/libraries/logic/FileSystem.cpp b/libraries/logic/FileSystem.cpp deleted file mode 100644 index 049f1e38..00000000 --- a/libraries/logic/FileSystem.cpp +++ /dev/null @@ -1,436 +0,0 @@ -// Licensed under the Apache-2.0 license. See README.md for details. - -#include "FileSystem.h" - -#include -#include -#include -#include -#include -#include - -namespace FS { - -void ensureExists(const QDir &dir) -{ - if (!QDir().mkpath(dir.absolutePath())) - { - throw FileSystemException("Unable to create directory " + dir.dirName() + " (" + - dir.absolutePath() + ")"); - } -} - -void write(const QString &filename, const QByteArray &data) -{ - ensureExists(QFileInfo(filename).dir()); - QSaveFile file(filename); - if (!file.open(QSaveFile::WriteOnly)) - { - throw FileSystemException("Couldn't open " + filename + " for writing: " + - file.errorString()); - } - if (data.size() != file.write(data)) - { - throw FileSystemException("Error writing data to " + filename + ": " + - file.errorString()); - } - if (!file.commit()) - { - throw FileSystemException("Error while committing data to " + filename + ": " + - file.errorString()); - } -} - -QByteArray read(const QString &filename) -{ - QFile file(filename); - if (!file.open(QFile::ReadOnly)) - { - throw FileSystemException("Unable to open " + filename + " for reading: " + - file.errorString()); - } - const qint64 size = file.size(); - QByteArray data(int(size), 0); - const qint64 ret = file.read(data.data(), size); - if (ret == -1 || ret != size) - { - throw FileSystemException("Error reading data from " + filename + ": " + - file.errorString()); - } - return data; -} - -bool ensureFilePathExists(QString filenamepath) -{ - QFileInfo a(filenamepath); - QDir dir; - QString ensuredPath = a.path(); - bool success = dir.mkpath(ensuredPath); - return success; -} - -bool ensureFolderPathExists(QString foldernamepath) -{ - QFileInfo a(foldernamepath); - QDir dir; - QString ensuredPath = a.filePath(); - bool success = dir.mkpath(ensuredPath); - return success; -} - -bool copy::operator()(const QString &offset) -{ - //NOTE always deep copy on windows. the alternatives are too messy. - #if defined Q_OS_WIN32 - m_followSymlinks = true; - #endif - - auto src = PathCombine(m_src.absolutePath(), offset); - auto dst = PathCombine(m_dst.absolutePath(), offset); - - QFileInfo currentSrc(src); - if (!currentSrc.exists()) - return false; - - if(!m_followSymlinks && currentSrc.isSymLink()) - { - qDebug() << "creating symlink" << src << " - " << dst; - if (!ensureFilePathExists(dst)) - { - qWarning() << "Cannot create path!"; - return false; - } - return QFile::link(currentSrc.symLinkTarget(), dst); - } - else if(currentSrc.isFile()) - { - qDebug() << "copying file" << src << " - " << dst; - if (!ensureFilePathExists(dst)) - { - qWarning() << "Cannot create path!"; - return false; - } - return QFile::copy(src, dst); - } - else if(currentSrc.isDir()) - { - qDebug() << "recursing" << offset; - if (!ensureFolderPathExists(dst)) - { - qWarning() << "Cannot create path!"; - return false; - } - QDir currentDir(src); - for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) - { - auto inner_offset = PathCombine(offset, f); - // ignore and skip stuff that matches the blacklist. - if(m_blacklist && m_blacklist->matches(inner_offset)) - { - continue; - } - if(!operator()(inner_offset)) - { - return false; - } - } - } - else - { - qCritical() << "Copy ERROR: Unknown filesystem object:" << src; - return false; - } - return true; -} - - -#if defined Q_OS_WIN32 -#include -#include -#endif -bool deletePath(QString path) -{ - bool OK = true; - QDir dir(path); - - if (!dir.exists()) - { - return OK; - } - auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | - QDir::AllDirs | QDir::Files, - QDir::DirsFirst); - - for(auto & info: allEntries) - { -#if defined Q_OS_WIN32 - QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath()); - auto wString = nativePath.toStdWString(); - DWORD dwAttrs = GetFileAttributesW(wString.c_str()); - // Windows: check for junctions, reparse points and other nasty things of that sort - if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT) - { - if (info.isFile()) - { - OK &= QFile::remove(info.absoluteFilePath()); - } - else if (info.isDir()) - { - OK &= dir.rmdir(info.absoluteFilePath()); - } - } -#else - // We do not trust Qt with reparse points, but do trust it with unix symlinks. - if(info.isSymLink()) - { - OK &= QFile::remove(info.absoluteFilePath()); - } -#endif - else if (info.isDir()) - { - OK &= deletePath(info.absoluteFilePath()); - } - else if (info.isFile()) - { - OK &= QFile::remove(info.absoluteFilePath()); - } - else - { - OK = false; - qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath(); - } - } - OK &= dir.rmdir(dir.absolutePath()); - return OK; -} - - -QString PathCombine(QString path1, QString path2) -{ - if(!path1.size()) - return path2; - if(!path2.size()) - return path1; - return QDir::cleanPath(path1 + QDir::separator() + path2); -} - -QString PathCombine(QString path1, QString path2, QString path3) -{ - return PathCombine(PathCombine(path1, path2), path3); -} - -QString AbsolutePath(QString path) -{ - return QFileInfo(path).absolutePath(); -} - -QString ResolveExecutable(QString path) -{ - if (path.isEmpty()) - { - return QString(); - } - if(!path.contains('/')) - { - path = QStandardPaths::findExecutable(path); - } - QFileInfo pathInfo(path); - if(!pathInfo.exists() || !pathInfo.isExecutable()) - { - return QString(); - } - return pathInfo.absoluteFilePath(); -} - -/** - * Normalize path - * - * Any paths inside the current directory will be normalized to relative paths (to current) - * Other paths will be made absolute - */ -QString NormalizePath(QString path) -{ - QDir a = QDir::currentPath(); - QString currentAbsolute = a.absolutePath(); - - QDir b(path); - QString newAbsolute = b.absolutePath(); - - if (newAbsolute.startsWith(currentAbsolute)) - { - return a.relativeFilePath(newAbsolute); - } - else - { - return newAbsolute; - } -} - -QString badFilenameChars = "\"\\/?<>:*|!"; - -QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) -{ - for (int i = 0; i < string.length(); i++) - { - if (badFilenameChars.contains(string[i])) - { - string[i] = replaceWith; - } - } - return string; -} - -QString DirNameFromString(QString string, QString inDir) -{ - int num = 0; - QString baseName = RemoveInvalidFilenameChars(string, '-'); - QString dirName; - do - { - if(num == 0) - { - dirName = baseName; - } - else - { - dirName = baseName + QString::number(num);; - } - - // If it's over 9000 - if (num > 9000) - return ""; - num++; - } while (QFileInfo(PathCombine(inDir, dirName)).exists()); - return dirName; -} - -// Does the directory path contain any '!'? If yes, return true, otherwise false. -// (This is a problem for Java) -bool checkProblemticPathJava(QDir folder) -{ - QString pathfoldername = folder.absolutePath(); - return pathfoldername.contains("!", Qt::CaseInsensitive); -} - -#include -#include -#include - -// Win32 crap -#if defined Q_OS_WIN - -#include -#include -#include -#include -#include -#include -#include - -bool called_coinit = false; - -HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args) -{ - HRESULT hres; - - if (!called_coinit) - { - hres = CoInitialize(NULL); - called_coinit = true; - - if (!SUCCEEDED(hres)) - { - qWarning("Failed to initialize COM. Error 0x%08X", hres); - return hres; - } - } - - IShellLink *link; - hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, - (LPVOID *)&link); - - if (SUCCEEDED(hres)) - { - IPersistFile *persistFile; - - link->SetPath(targetPath); - link->SetArguments(args); - - hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile); - if (SUCCEEDED(hres)) - { - WCHAR wstr[MAX_PATH]; - - MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH); - - hres = persistFile->Save(wstr, TRUE); - persistFile->Release(); - } - link->Release(); - } - return hres; -} - -#endif - -QString getDesktopDir() -{ - return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); -} - -// Cross-platform Shortcut creation -bool createShortCut(QString location, QString dest, QStringList args, QString name, - QString icon) -{ -#if defined Q_OS_LINUX - location = PathCombine(location, name + ".desktop"); - - QFile f(location); - f.open(QIODevice::WriteOnly | QIODevice::Text); - QTextStream stream(&f); - - QString argstring; - if (!args.empty()) - argstring = " '" + args.join("' '") + "'"; - - stream << "[Desktop Entry]" - << "\n"; - stream << "Type=Application" - << "\n"; - stream << "TryExec=" << dest.toLocal8Bit() << "\n"; - stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; - stream << "Name=" << name.toLocal8Bit() << "\n"; - stream << "Icon=" << icon.toLocal8Bit() << "\n"; - - stream.flush(); - f.close(); - - f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | - QFileDevice::ExeOther); - - return true; -#elif defined Q_OS_WIN - // TODO: Fix - // QFile file(PathCombine(location, name + ".lnk")); - // WCHAR *file_w; - // WCHAR *dest_w; - // WCHAR *args_w; - // file.fileName().toWCharArray(file_w); - // dest.toWCharArray(dest_w); - - // QString argStr; - // for (int i = 0; i < args.count(); i++) - // { - // argStr.append(args[i]); - // argStr.append(" "); - // } - // argStr.toWCharArray(args_w); - - // return SUCCEEDED(CreateLink(file_w, dest_w, args_w)); - return false; -#else - qWarning("Desktop Shortcuts not supported on your platform!"); - return false; -#endif -} -} diff --git a/libraries/logic/FileSystem.h b/libraries/logic/FileSystem.h deleted file mode 100644 index 80637f90..00000000 --- a/libraries/logic/FileSystem.h +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed under the Apache-2.0 license. See README.md for details. - -#pragma once - -#include "Exception.h" -#include "pathmatcher/IPathMatcher.h" - -#include "multimc_logic_export.h" -#include -#include - -namespace FS -{ - -class MULTIMC_LOGIC_EXPORT FileSystemException : public ::Exception -{ -public: - FileSystemException(const QString &message) : Exception(message) {} -}; - -/** - * write data to a file safely - */ -MULTIMC_LOGIC_EXPORT void write(const QString &filename, const QByteArray &data); - -/** - * read data from a file safely\ - */ -MULTIMC_LOGIC_EXPORT QByteArray read(const QString &filename); - -/** - * Creates all the folders in a path for the specified path - * last segment of the path is treated as a file name and is ignored! - */ -MULTIMC_LOGIC_EXPORT bool ensureFilePathExists(QString filenamepath); - -/** - * Creates all the folders in a path for the specified path - * last segment of the path is treated as a folder name and is created! - */ -MULTIMC_LOGIC_EXPORT bool ensureFolderPathExists(QString filenamepath); - -class MULTIMC_LOGIC_EXPORT copy -{ -public: - copy(const copy&) = delete; - copy(const QString & src, const QString & dst) - { - m_src = src; - m_dst = dst; - } - copy & followSymlinks(const bool follow) - { - m_followSymlinks = follow; - return *this; - } - copy & blacklist(const IPathMatcher * filter) - { - m_blacklist = filter; - return *this; - } - bool operator()() - { - return operator()(QString()); - } - -private: - bool operator()(const QString &offset); - -private: - bool m_followSymlinks = true; - const IPathMatcher * m_blacklist = nullptr; - QDir m_src; - QDir m_dst; -}; - -/** - * Delete a folder recursively - */ -MULTIMC_LOGIC_EXPORT bool deletePath(QString path); - -MULTIMC_LOGIC_EXPORT QString PathCombine(QString path1, QString path2); -MULTIMC_LOGIC_EXPORT QString PathCombine(QString path1, QString path2, QString path3); - -MULTIMC_LOGIC_EXPORT QString AbsolutePath(QString path); - -/** - * Resolve an executable - * - * Will resolve: - * single executable (by name) - * relative path - * absolute path - * - * @return absolute path to executable or null string - */ -MULTIMC_LOGIC_EXPORT QString ResolveExecutable(QString path); - -/** - * Normalize path - * - * Any paths inside the current directory will be normalized to relative paths (to current) - * Other paths will be made absolute - * - * Returns false if the path logic somehow filed (and normalizedPath in invalid) - */ -MULTIMC_LOGIC_EXPORT QString NormalizePath(QString path); - -MULTIMC_LOGIC_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-'); - -MULTIMC_LOGIC_EXPORT QString DirNameFromString(QString string, QString inDir = "."); - -/// Checks if the a given Path contains "!" -MULTIMC_LOGIC_EXPORT bool checkProblemticPathJava(QDir folder); - -// Get the Directory representing the User's Desktop -MULTIMC_LOGIC_EXPORT QString getDesktopDir(); - -// Create a shortcut at *location*, pointing to *dest* called with the arguments *args* -// call it *name* and assign it the icon *icon* -// return true if operation succeeded -MULTIMC_LOGIC_EXPORT bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation); -} diff --git a/libraries/logic/GZip.cpp b/libraries/logic/GZip.cpp deleted file mode 100644 index 38605df6..00000000 --- a/libraries/logic/GZip.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "GZip.h" -#include -#include - -bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes) -{ - if (compressedBytes.size() == 0) - { - uncompressedBytes = compressedBytes; - return true; - } - - unsigned uncompLength = compressedBytes.size(); - uncompressedBytes.clear(); - uncompressedBytes.resize(uncompLength); - - z_stream strm; - memset(&strm, 0, sizeof(strm)); - strm.next_in = (Bytef *)compressedBytes.data(); - strm.avail_in = compressedBytes.size(); - - bool done = false; - - if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) - { - return false; - } - - int err = Z_OK; - - while (!done) - { - // If our output buffer is too small - if (strm.total_out >= uncompLength) - { - uncompressedBytes.resize(uncompLength * 2); - uncompLength *= 2; - } - - strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out); - strm.avail_out = uncompLength - strm.total_out; - - // Inflate another chunk. - err = inflate(&strm, Z_SYNC_FLUSH); - if (err == Z_STREAM_END) - done = true; - else if (err != Z_OK) - { - break; - } - } - - if (inflateEnd(&strm) != Z_OK || !done) - { - return false; - } - - uncompressedBytes.resize(strm.total_out); - return true; -} - -bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes) -{ - if (uncompressedBytes.size() == 0) - { - compressedBytes = uncompressedBytes; - return true; - } - - unsigned compLength = std::min(uncompressedBytes.size(), 16); - compressedBytes.clear(); - compressedBytes.resize(compLength); - - z_stream zs; - memset(&zs, 0, sizeof(zs)); - - if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) - { - return false; - } - - zs.next_in = (Bytef*)uncompressedBytes.data(); - zs.avail_in = uncompressedBytes.size(); - - int ret; - compressedBytes.resize(uncompressedBytes.size()); - - unsigned offset = 0; - unsigned temp = 0; - do - { - auto remaining = compressedBytes.size() - offset; - if(remaining < 1) - { - compressedBytes.resize(compressedBytes.size() * 2); - } - zs.next_out = (Bytef *) (compressedBytes.data() + offset); - temp = zs.avail_out = compressedBytes.size() - offset; - ret = deflate(&zs, Z_FINISH); - offset += temp - zs.avail_out; - } while (ret == Z_OK); - - compressedBytes.resize(offset); - - if (deflateEnd(&zs) != Z_OK) - { - return false; - } - - if (ret != Z_STREAM_END) - { - return false; - } - return true; -} \ No newline at end of file diff --git a/libraries/logic/GZip.h b/libraries/logic/GZip.h deleted file mode 100644 index 6993a222..00000000 --- a/libraries/logic/GZip.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT GZip -{ -public: - static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes); - static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes); -}; - diff --git a/libraries/logic/InstanceList.cpp b/libraries/logic/InstanceList.cpp deleted file mode 100644 index 783df660..00000000 --- a/libraries/logic/InstanceList.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "InstanceList.h" -#include "BaseInstance.h" - -//FIXME: this really doesn't belong *here* -#include "minecraft/onesix/OneSixInstance.h" -#include "minecraft/legacy/LegacyInstance.h" -#include "minecraft/ftb/FTBPlugin.h" -#include "minecraft/MinecraftVersion.h" -#include "settings/INISettingsObject.h" -#include "NullInstance.h" -#include "FileSystem.h" -#include "pathmatcher/RegexpMatcher.h" - -const static int GROUP_FILE_FORMAT_VERSION = 1; - -InstanceList::InstanceList(SettingsObjectPtr globalSettings, const QString &instDir, QObject *parent) - : QAbstractListModel(parent), m_instDir(instDir) -{ - m_globalSettings = globalSettings; - if (!QDir::current().exists(m_instDir)) - { - QDir::current().mkpath(m_instDir); - } -} - -InstanceList::~InstanceList() -{ -} - -int InstanceList::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return m_instances.count(); -} - -QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const -{ - Q_UNUSED(parent); - if (row < 0 || row >= m_instances.size()) - return QModelIndex(); - return createIndex(row, column, (void *)m_instances.at(row).get()); -} - -QVariant InstanceList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - { - return QVariant(); - } - BaseInstance *pdata = static_cast(index.internalPointer()); - switch (role) - { - case InstancePointerRole: - { - QVariant v = qVariantFromValue((void *)pdata); - return v; - } - case InstanceIDRole: - { - return pdata->id(); - } - case Qt::DisplayRole: - { - return pdata->name(); - } - case Qt::ToolTipRole: - { - return pdata->instanceRoot(); - } - case Qt::DecorationRole: - { - return pdata->iconKey(); - } - // HACK: see GroupView.h in gui! - case GroupRole: - { - return pdata->group(); - } - default: - break; - } - return QVariant(); -} - -Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags f; - if (index.isValid()) - { - f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable); - } - return f; -} - -void InstanceList::groupChanged() -{ - // save the groups. save all of them. - saveGroupList(); -} - -QStringList InstanceList::getGroups() -{ - return m_groups.toList(); -} - -void InstanceList::suspendGroupSaving() -{ - suspendedGroupSave = true; -} - -void InstanceList::resumeGroupSaving() -{ - if(suspendedGroupSave) - { - suspendedGroupSave = false; - if(queuedGroupSave) - { - saveGroupList(); - } - } -} - -void InstanceList::deleteGroup(const QString& name) -{ - for(auto & instance: m_instances) - { - auto instGroupName = instance->group(); - if(instGroupName == name) - { - instance->setGroupPost(QString()); - } - } -} - -void InstanceList::saveGroupList() -{ - if(suspendedGroupSave) - { - queuedGroupSave = true; - return; - } - - QString groupFileName = m_instDir + "/instgroups.json"; - QMap> groupMap; - for (auto instance : m_instances) - { - QString id = instance->id(); - QString group = instance->group(); - if (group.isEmpty()) - continue; - - // keep a list/set of groups for choosing - m_groups.insert(group); - - if (!groupMap.count(group)) - { - QSet set; - set.insert(id); - groupMap[group] = set; - } - else - { - QSet &set = groupMap[group]; - set.insert(id); - } - } - QJsonObject toplevel; - toplevel.insert("formatVersion", QJsonValue(QString("1"))); - QJsonObject groupsArr; - for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++) - { - auto list = iter.value(); - auto name = iter.key(); - QJsonObject groupObj; - QJsonArray instanceArr; - groupObj.insert("hidden", QJsonValue(QString("false"))); - for (auto item : list) - { - instanceArr.append(QJsonValue(item)); - } - groupObj.insert("instances", instanceArr); - groupsArr.insert(name, groupObj); - } - toplevel.insert("groups", groupsArr); - QJsonDocument doc(toplevel); - try - { - FS::write(groupFileName, doc.toJson()); - } - catch(FS::FileSystemException & e) - { - qCritical() << "Failed to write instance group file :" << e.cause(); - } -} - -void InstanceList::loadGroupList(QMap &groupMap) -{ - QString groupFileName = m_instDir + "/instgroups.json"; - - // if there's no group file, fail - if (!QFileInfo(groupFileName).exists()) - return; - - QByteArray jsonData; - try - { - jsonData = FS::read(groupFileName); - } - catch (FS::FileSystemException & e) - { - qCritical() << "Failed to read instance group file :" << e.cause(); - return; - } - - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error); - - // if the json was bad, fail - if (error.error != QJsonParseError::NoError) - { - qCritical() << QString("Failed to parse instance group file: %1 at offset %2") - .arg(error.errorString(), QString::number(error.offset)) - .toUtf8(); - return; - } - - // if the root of the json wasn't an object, fail - if (!jsonDoc.isObject()) - { - qWarning() << "Invalid group file. Root entry should be an object."; - return; - } - - QJsonObject rootObj = jsonDoc.object(); - - // Make sure the format version matches, otherwise fail. - if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION) - return; - - // Get the groups. if it's not an object, fail - if (!rootObj.value("groups").isObject()) - { - qWarning() << "Invalid group list JSON: 'groups' should be an object."; - return; - } - - // Iterate through all the groups. - QJsonObject groupMapping = rootObj.value("groups").toObject(); - for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) - { - QString groupName = iter.key(); - - // If not an object, complain and skip to the next one. - if (!iter.value().isObject()) - { - qWarning() << QString("Group '%1' in the group list should " - "be an object.") - .arg(groupName) - .toUtf8(); - continue; - } - - QJsonObject groupObj = iter.value().toObject(); - if (!groupObj.value("instances").isArray()) - { - qWarning() << QString("Group '%1' in the group list is invalid. " - "It should contain an array " - "called 'instances'.") - .arg(groupName) - .toUtf8(); - continue; - } - - // keep a list/set of groups for choosing - m_groups.insert(groupName); - - // Iterate through the list of instances in the group. - QJsonArray instancesArray = groupObj.value("instances").toArray(); - - for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); - iter2++) - { - groupMap[(*iter2).toString()] = groupName; - } - } -} - -InstanceList::InstListError InstanceList::loadList() -{ - // load the instance groups - QMap groupMap; - loadGroupList(groupMap); - - QList tempList; - { - QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, - QDirIterator::FollowSymlinks); - while (iter.hasNext()) - { - QString subDir = iter.next(); - if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists()) - continue; - qDebug() << "Loading MultiMC instance from " << subDir; - InstancePtr instPtr; - auto error = loadInstance(instPtr, subDir); - if(!continueProcessInstance(instPtr, error, subDir, groupMap)) - continue; - tempList.append(instPtr); - } - } - - // FIXME: generalize - FTBPlugin::loadInstances(m_globalSettings, groupMap, tempList); - - beginResetModel(); - m_instances.clear(); - for(auto inst: tempList) - { - inst->setParent(this); - connect(inst.get(), SIGNAL(propertiesChanged(BaseInstance *)), this, - SLOT(propertiesChanged(BaseInstance *))); - connect(inst.get(), SIGNAL(groupChanged()), this, SLOT(groupChanged())); - connect(inst.get(), SIGNAL(nuked(BaseInstance *)), this, - SLOT(instanceNuked(BaseInstance *))); - m_instances.append(inst); - } - endResetModel(); - emit dataIsInvalid(); - return NoError; -} - -/// Clear all instances. Triggers notifications. -void InstanceList::clear() -{ - beginResetModel(); - saveGroupList(); - m_instances.clear(); - endResetModel(); - emit dataIsInvalid(); -} - -void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value) -{ - m_instDir = value.toString(); - loadList(); -} - -/// Add an instance. Triggers notifications, returns the new index -int InstanceList::add(InstancePtr t) -{ - beginInsertRows(QModelIndex(), m_instances.size(), m_instances.size()); - m_instances.append(t); - t->setParent(this); - connect(t.get(), SIGNAL(propertiesChanged(BaseInstance *)), this, - SLOT(propertiesChanged(BaseInstance *))); - connect(t.get(), SIGNAL(groupChanged()), this, SLOT(groupChanged())); - connect(t.get(), SIGNAL(nuked(BaseInstance *)), this, SLOT(instanceNuked(BaseInstance *))); - endInsertRows(); - return count() - 1; -} - -InstancePtr InstanceList::getInstanceById(QString instId) const -{ - if(instId.isEmpty()) - return InstancePtr(); - for(auto & inst: m_instances) - { - if (inst->id() == instId) - { - return inst; - } - } - return InstancePtr(); -} - -QModelIndex InstanceList::getInstanceIndexById(const QString &id) const -{ - return index(getInstIndex(getInstanceById(id).get())); -} - -int InstanceList::getInstIndex(BaseInstance *inst) const -{ - int count = m_instances.count(); - for (int i = 0; i < count; i++) - { - if (inst == m_instances[i].get()) - { - return i; - } - } - return -1; -} - -bool InstanceList::continueProcessInstance(InstancePtr instPtr, const int error, - const QDir &dir, QMap &groupMap) -{ - if (error != InstanceList::NoLoadError && error != InstanceList::NotAnInstance) - { - QString errorMsg = QString("Failed to load instance %1: ") - .arg(QFileInfo(dir.absolutePath()).baseName()) - .toUtf8(); - - switch (error) - { - default: - errorMsg += QString("Unknown instance loader error %1").arg(error); - break; - } - qCritical() << errorMsg.toUtf8(); - return false; - } - else if (!instPtr) - { - qCritical() << QString("Error loading instance %1. Instance loader returned null.") - .arg(QFileInfo(dir.absolutePath()).baseName()) - .toUtf8(); - return false; - } - else - { - auto iter = groupMap.find(instPtr->id()); - if (iter != groupMap.end()) - { - instPtr->setGroupInitial((*iter)); - } - qDebug() << "Loaded instance " << instPtr->name() << " from " << dir.absolutePath(); - return true; - } -} - -InstanceList::InstLoadError -InstanceList::loadInstance(InstancePtr &inst, const QString &instDir) -{ - auto instanceSettings = std::make_shared(FS::PathCombine(instDir, "instance.cfg")); - - instanceSettings->registerSetting("InstanceType", "Legacy"); - - QString inst_type = instanceSettings->get("InstanceType").toString(); - - // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "OneSix" || inst_type == "Nostalgia") - { - inst.reset(new OneSixInstance(m_globalSettings, instanceSettings, instDir)); - } - else if (inst_type == "Legacy") - { - inst.reset(new LegacyInstance(m_globalSettings, instanceSettings, instDir)); - } - else - { - inst.reset(new NullInstance(m_globalSettings, instanceSettings, instDir)); - } - inst->init(); - return NoLoadError; -} - -InstanceList::InstCreateError -InstanceList::createInstance(InstancePtr &inst, BaseVersionPtr version, const QString &instDir) -{ - QDir rootDir(instDir); - - qDebug() << instDir.toUtf8(); - if (!rootDir.exists() && !rootDir.mkpath(".")) - { - qCritical() << "Can't create instance folder" << instDir; - return InstanceList::CantCreateDir; - } - - if (!version) - { - qCritical() << "Can't create instance for non-existing MC version"; - return InstanceList::NoSuchVersion; - } - - auto instanceSettings = std::make_shared(FS::PathCombine(instDir, "instance.cfg")); - instanceSettings->registerSetting("InstanceType", "Legacy"); - - auto minecraftVersion = std::dynamic_pointer_cast(version); - if(minecraftVersion) - { - auto mcVer = std::dynamic_pointer_cast(version); - instanceSettings->set("InstanceType", "OneSix"); - inst.reset(new OneSixInstance(m_globalSettings, instanceSettings, instDir)); - inst->setIntendedVersionId(version->descriptor()); - inst->init(); - return InstanceList::NoCreateError; - } - return InstanceList::NoSuchVersion; -} - -InstanceList::InstCreateError -InstanceList::copyInstance(InstancePtr &newInstance, InstancePtr &oldInstance, const QString &instDir, bool copySaves) -{ - QDir rootDir(instDir); - std::unique_ptr matcher; - if(!copySaves) - { - auto matcherReal = new RegexpMatcher("[.]?minecraft/saves"); - matcherReal->caseSensitive(false); - matcher.reset(matcherReal); - } - - qDebug() << instDir.toUtf8(); - FS::copy folderCopy(oldInstance->instanceRoot(), instDir); - folderCopy.followSymlinks(false).blacklist(matcher.get()); - if (!folderCopy()) - { - FS::deletePath(instDir); - return InstanceList::CantCreateDir; - } - - INISettingsObject settings_obj(FS::PathCombine(instDir, "instance.cfg")); - settings_obj.registerSetting("InstanceType", "Legacy"); - QString inst_type = settings_obj.get("InstanceType").toString(); - - oldInstance->copy(instDir); - - auto error = loadInstance(newInstance, instDir); - - switch (error) - { - case NoLoadError: - return NoCreateError; - case NotAnInstance: - rootDir.removeRecursively(); - return CantCreateDir; - default: - case UnknownLoadError: - rootDir.removeRecursively(); - return UnknownCreateError; - } -} - -void InstanceList::instanceNuked(BaseInstance *inst) -{ - int i = getInstIndex(inst); - if (i != -1) - { - beginRemoveRows(QModelIndex(), i, i); - m_instances.removeAt(i); - endRemoveRows(); - } -} - -void InstanceList::propertiesChanged(BaseInstance *inst) -{ - int i = getInstIndex(inst); - if (i != -1) - { - emit dataChanged(index(i), index(i)); - } -} diff --git a/libraries/logic/InstanceList.h b/libraries/logic/InstanceList.h deleted file mode 100644 index 074cca7c..00000000 --- a/libraries/logic/InstanceList.h +++ /dev/null @@ -1,187 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -#include "BaseInstance.h" - -#include "multimc_logic_export.h" - -class BaseInstance; -class QDir; - -class MULTIMC_LOGIC_EXPORT InstanceList : public QAbstractListModel -{ - Q_OBJECT -private: - void loadGroupList(QMap &groupList); - void suspendGroupSaving(); - void resumeGroupSaving(); - -public slots: - void saveGroupList(); - -public: - explicit InstanceList(SettingsObjectPtr globalSettings, const QString &instDir, QObject *parent = 0); - virtual ~InstanceList(); - -public: - QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - - enum AdditionalRoles - { - GroupRole = Qt::UserRole, - InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance - InstanceIDRole = 0x34B1CB49 ///< Return id if the instance - }; - /*! - * \brief Error codes returned by functions in the InstanceList class. - * NoError Indicates that no error occurred. - * UnknownError indicates that an unspecified error occurred. - */ - enum InstListError - { - NoError = 0, - UnknownError - }; - - enum InstLoadError - { - NoLoadError = 0, - UnknownLoadError, - NotAnInstance - }; - - enum InstCreateError - { - NoCreateError = 0, - NoSuchVersion, - UnknownCreateError, - InstExists, - CantCreateDir - }; - - QString instDir() const - { - return m_instDir; - } - - /*! - * \brief Get the instance at index - */ - InstancePtr at(int i) const - { - return m_instances.at(i); - } - ; - - /*! - * \brief Get the count of loaded instances - */ - int count() const - { - return m_instances.count(); - } - ; - - /// Clear all instances. Triggers notifications. - void clear(); - - /// Add an instance. Triggers notifications, returns the new index - int add(InstancePtr t); - - /// Get an instance by ID - InstancePtr getInstanceById(QString id) const; - - QModelIndex getInstanceIndexById(const QString &id) const; - - // FIXME: instead of iterating through all instances and forming a set, keep the set around - QStringList getGroups(); - - void deleteGroup(const QString & name); - - /*! - * \brief Creates a stub instance - * - * \param inst Pointer to store the created instance in. - * \param version Game version to use for the instance - * \param instDir The new instance's directory. - * \return An InstCreateError error code. - * - InstExists if the given instance directory is already an instance. - * - CantCreateDir if the given instance directory cannot be created. - */ - InstCreateError createInstance(InstancePtr &inst, BaseVersionPtr version, - const QString &instDir); - - /*! - * \brief Creates a copy of an existing instance with a new name - * - * \param newInstance Pointer to store the created instance in. - * \param oldInstance The instance to copy - * \param instDir The new instance's directory. - * \return An InstCreateError error code. - * - InstExists if the given instance directory is already an instance. - * - CantCreateDir if the given instance directory cannot be created. - */ - InstCreateError copyInstance(InstancePtr &newInstance, InstancePtr &oldInstance, - const QString &instDir, bool copySaves); - - /*! - * \brief Loads an instance from the given directory. - * Checks the instance's INI file to figure out what the instance's type is first. - * \param inst Pointer to store the loaded instance in. - * \param instDir The instance's directory. - * \return An InstLoadError error code. - * - NotAnInstance if the given instance directory isn't a valid instance. - */ - InstLoadError loadInstance(InstancePtr &inst, const QString &instDir); - -signals: - void dataIsInvalid(); - -public slots: - void on_InstFolderChanged(const Setting &setting, QVariant value); - - /*! - * \brief Loads the instance list. Triggers notifications. - */ - InstListError loadList(); - -private slots: - void propertiesChanged(BaseInstance *inst); - void instanceNuked(BaseInstance *inst); - void groupChanged(); - -private: - int getInstIndex(BaseInstance *inst) const; - -public: - static bool continueProcessInstance(InstancePtr instPtr, const int error, const QDir &dir, QMap &groupMap); - -protected: - QString m_instDir; - QList m_instances; - QSet m_groups; - SettingsObjectPtr m_globalSettings; - bool suspendedGroupSave = false; - bool queuedGroupSave = false; -}; diff --git a/libraries/logic/Json.cpp b/libraries/logic/Json.cpp deleted file mode 100644 index f2cbc8a3..00000000 --- a/libraries/logic/Json.cpp +++ /dev/null @@ -1,272 +0,0 @@ -// Licensed under the Apache-2.0 license. See README.md for details. - -#include "Json.h" - -#include - -#include "FileSystem.h" -#include - -namespace Json -{ -void write(const QJsonDocument &doc, const QString &filename) -{ - FS::write(filename, doc.toJson()); -} -void write(const QJsonObject &object, const QString &filename) -{ - write(QJsonDocument(object), filename); -} -void write(const QJsonArray &array, const QString &filename) -{ - write(QJsonDocument(array), filename); -} - -QByteArray toBinary(const QJsonObject &obj) -{ - return QJsonDocument(obj).toBinaryData(); -} -QByteArray toBinary(const QJsonArray &array) -{ - return QJsonDocument(array).toBinaryData(); -} -QByteArray toText(const QJsonObject &obj) -{ - return QJsonDocument(obj).toJson(QJsonDocument::Compact); -} -QByteArray toText(const QJsonArray &array) -{ - return QJsonDocument(array).toJson(QJsonDocument::Compact); -} - -static bool isBinaryJson(const QByteArray &data) -{ - decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; - return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; -} -QJsonDocument requireDocument(const QByteArray &data, const QString &what) -{ - if (isBinaryJson(data)) - { - QJsonDocument doc = QJsonDocument::fromBinaryData(data); - if (doc.isNull()) - { - throw JsonException(what + ": Invalid JSON (binary JSON detected)"); - } - return doc; - } - else - { - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(data, &error); - if (error.error != QJsonParseError::NoError) - { - throw JsonException(what + ": Error parsing JSON: " + error.errorString()); - } - return doc; - } -} -QJsonDocument requireDocument(const QString &filename, const QString &what) -{ - return requireDocument(FS::read(filename), what); -} -QJsonObject requireObject(const QJsonDocument &doc, const QString &what) -{ - if (!doc.isObject()) - { - throw JsonException(what + " is not an object"); - } - return doc.object(); -} -QJsonArray requireArray(const QJsonDocument &doc, const QString &what) -{ - if (!doc.isArray()) - { - throw JsonException(what + " is not an array"); - } - return doc.array(); -} - -void writeString(QJsonObject &to, const QString &key, const QString &value) -{ - if (!value.isEmpty()) - { - to.insert(key, value); - } -} - -void writeStringList(QJsonObject &to, const QString &key, const QStringList &values) -{ - if (!values.isEmpty()) - { - QJsonArray array; - for(auto value: values) - { - array.append(value); - } - to.insert(key, array); - } -} - -template<> -QJsonValue toJson(const QUrl &url) -{ - return QJsonValue(url.toString(QUrl::FullyEncoded)); -} -template<> -QJsonValue toJson(const QByteArray &data) -{ - return QJsonValue(QString::fromLatin1(data.toHex())); -} -template<> -QJsonValue toJson(const QDateTime &datetime) -{ - return QJsonValue(datetime.toString(Qt::ISODate)); -} -template<> -QJsonValue toJson(const QDir &dir) -{ - return QDir::current().relativeFilePath(dir.absolutePath()); -} -template<> -QJsonValue toJson(const QUuid &uuid) -{ - return uuid.toString(); -} -template<> -QJsonValue toJson(const QVariant &variant) -{ - return QJsonValue::fromVariant(variant); -} - - -template<> QByteArray requireIsType(const QJsonValue &value, const QString &what) -{ - const QString string = ensureIsType(value, what); - // ensure that the string can be safely cast to Latin1 - if (string != QString::fromLatin1(string.toLatin1())) - { - throw JsonException(what + " is not encodable as Latin1"); - } - return QByteArray::fromHex(string.toLatin1()); -} - -template<> QJsonArray requireIsType(const QJsonValue &value, const QString &what) -{ - if (!value.isArray()) - { - throw JsonException(what + " is not an array"); - } - return value.toArray(); -} - - -template<> QString requireIsType(const QJsonValue &value, const QString &what) -{ - if (!value.isString()) - { - throw JsonException(what + " is not a string"); - } - return value.toString(); -} - -template<> bool requireIsType(const QJsonValue &value, const QString &what) -{ - if (!value.isBool()) - { - throw JsonException(what + " is not a bool"); - } - return value.toBool(); -} - -template<> double requireIsType(const QJsonValue &value, const QString &what) -{ - if (!value.isDouble()) - { - throw JsonException(what + " is not a double"); - } - return value.toDouble(); -} - -template<> int requireIsType(const QJsonValue &value, const QString &what) -{ - const double doubl = requireIsType(value, what); - if (fmod(doubl, 1) != 0) - { - throw JsonException(what + " is not an integer"); - } - return int(doubl); -} - -template<> QDateTime requireIsType(const QJsonValue &value, const QString &what) -{ - const QString string = requireIsType(value, what); - const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); - if (!datetime.isValid()) - { - throw JsonException(what + " is not a ISO formatted date/time value"); - } - return datetime; -} - -template<> QUrl requireIsType(const QJsonValue &value, const QString &what) -{ - const QString string = ensureIsType(value, what); - if (string.isEmpty()) - { - return QUrl(); - } - const QUrl url = QUrl(string, QUrl::StrictMode); - if (!url.isValid()) - { - throw JsonException(what + " is not a correctly formatted URL"); - } - return url; -} - -template<> QDir requireIsType(const QJsonValue &value, const QString &what) -{ - const QString string = requireIsType(value, what); - // FIXME: does not handle invalid characters! - return QDir::current().absoluteFilePath(string); -} - -template<> QUuid requireIsType(const QJsonValue &value, const QString &what) -{ - const QString string = requireIsType(value, what); - const QUuid uuid = QUuid(string); - if (uuid.toString() != string) // converts back => valid - { - throw JsonException(what + " is not a valid UUID"); - } - return uuid; -} - -template<> QJsonObject requireIsType(const QJsonValue &value, const QString &what) -{ - if (!value.isObject()) - { - throw JsonException(what + " is not an object"); - } - return value.toObject(); -} - -template<> QVariant requireIsType(const QJsonValue &value, const QString &what) -{ - if (value.isNull() || value.isUndefined()) - { - throw JsonException(what + " is null or undefined"); - } - return value.toVariant(); -} - -template<> QJsonValue requireIsType(const QJsonValue &value, const QString &what) -{ - if (value.isNull() || value.isUndefined()) - { - throw JsonException(what + " is null or undefined"); - } - return value; -} - -} diff --git a/libraries/logic/Json.h b/libraries/logic/Json.h deleted file mode 100644 index 2cb60f0e..00000000 --- a/libraries/logic/Json.h +++ /dev/null @@ -1,249 +0,0 @@ -// Licensed under the Apache-2.0 license. See README.md for details. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Exception.h" - -namespace Json -{ -class MULTIMC_LOGIC_EXPORT JsonException : public ::Exception -{ -public: - JsonException(const QString &message) : Exception(message) {} -}; - -/// @throw FileSystemException -void write(const QJsonDocument &doc, const QString &filename); -/// @throw FileSystemException -void write(const QJsonObject &object, const QString &filename); -/// @throw FileSystemException -void write(const QJsonArray &array, const QString &filename); - -QByteArray toBinary(const QJsonObject &obj); -QByteArray toBinary(const QJsonArray &array); -QByteArray toText(const QJsonObject &obj); -QByteArray toText(const QJsonArray &array); - -/// @throw JsonException -MULTIMC_LOGIC_EXPORT QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document"); -/// @throw JsonException -MULTIMC_LOGIC_EXPORT QJsonDocument requireDocument(const QString &filename, const QString &what = "Document"); -/// @throw JsonException -MULTIMC_LOGIC_EXPORT QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document"); -/// @throw JsonException -MULTIMC_LOGIC_EXPORT QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document"); - -/////////////////// WRITING //////////////////// - -void writeString(QJsonObject & to, const QString &key, const QString &value); -void writeStringList(QJsonObject & to, const QString &key, const QStringList &values); - -template -QJsonValue toJson(const T &t) -{ - return QJsonValue(t); -} -template<> -QJsonValue toJson(const QUrl &url); -template<> -QJsonValue toJson(const QByteArray &data); -template<> -QJsonValue toJson(const QDateTime &datetime); -template<> -QJsonValue toJson(const QDir &dir); -template<> -QJsonValue toJson(const QUuid &uuid); -template<> -QJsonValue toJson(const QVariant &variant); - -template -QJsonArray toJsonArray(const QList &container) -{ - QJsonArray array; - for (const T item : container) - { - array.append(toJson(item)); - } - return array; -} - -////////////////// READING //////////////////// - -/// @throw JsonException -template -T requireIsType(const QJsonValue &value, const QString &what = "Value"); - -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT double requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT bool requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT int requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QJsonObject requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QJsonArray requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QJsonValue requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QByteArray requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QDateTime requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QVariant requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QString requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QUuid requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QDir requireIsType(const QJsonValue &value, const QString &what); -/// @throw JsonException -template<> MULTIMC_LOGIC_EXPORT QUrl requireIsType(const QJsonValue &value, const QString &what); - -// the following functions are higher level functions, that make use of the above functions for -// type conversion -template -T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value") -{ - if (value.isUndefined() || value.isNull()) - { - return default_; - } - try - { - return requireIsType(value, what); - } - catch (JsonException &) - { - return default_; - } -} - -/// @throw JsonException -template -T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") -{ - const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { - throw JsonException(localWhat + "s parent does not contain " + localWhat); - } - return requireIsType(parent.value(key), localWhat); -} - -template -T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__") -{ - const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { - return default_; - } - return ensureIsType(parent.value(key), default_, localWhat); -} - -template -QVector requireIsArrayOf(const QJsonDocument &doc) -{ - const QJsonArray array = requireArray(doc); - QVector out; - for (const QJsonValue val : array) - { - out.append(requireIsType(val, "Document")); - } - return out; -} - -template -QVector ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value") -{ - const QJsonArray array = ensureIsType(value, QJsonArray(), what); - QVector out; - for (const QJsonValue val : array) - { - out.append(requireIsType(val, what)); - } - return out; -} - -template -QVector ensureIsArrayOf(const QJsonValue &value, const QVector default_, const QString &what = "Value") -{ - if (value.isUndefined()) - { - return default_; - } - return ensureIsArrayOf(value, what); -} - -/// @throw JsonException -template -QVector requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") -{ - const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { - throw JsonException(localWhat + "s parent does not contain " + localWhat); - } - return ensureIsArrayOf(parent.value(key), localWhat); -} - -template -QVector ensureIsArrayOf(const QJsonObject &parent, const QString &key, - const QVector &default_ = QVector(), const QString &what = "__placeholder__") -{ - const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); - if (!parent.contains(key)) - { - return default_; - } - return ensureIsArrayOf(parent.value(key), default_, localWhat); -} - -// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers -#define JSON_HELPERFUNCTIONS(NAME, TYPE) \ - inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \ - { \ - return requireIsType(value, what); \ - } \ - inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \ - { \ - return ensureIsType(value, default_, what); \ - } \ - inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \ - { \ - return requireIsType(parent, key, what); \ - } \ - inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \ - { \ - return ensureIsType(parent, key, default_, what); \ - } - -JSON_HELPERFUNCTIONS(Array, QJsonArray) -JSON_HELPERFUNCTIONS(Object, QJsonObject) -JSON_HELPERFUNCTIONS(JsonValue, QJsonValue) -JSON_HELPERFUNCTIONS(String, QString) -JSON_HELPERFUNCTIONS(Boolean, bool) -JSON_HELPERFUNCTIONS(Double, double) -JSON_HELPERFUNCTIONS(Integer, int) -JSON_HELPERFUNCTIONS(DateTime, QDateTime) -JSON_HELPERFUNCTIONS(Url, QUrl) -JSON_HELPERFUNCTIONS(ByteArray, QByteArray) -JSON_HELPERFUNCTIONS(Dir, QDir) -JSON_HELPERFUNCTIONS(Uuid, QUuid) -JSON_HELPERFUNCTIONS(Variant, QVariant) - -#undef JSON_HELPERFUNCTIONS - -} -using JSONValidationError = Json::JsonException; diff --git a/libraries/logic/MMCStrings.cpp b/libraries/logic/MMCStrings.cpp deleted file mode 100644 index c50d596e..00000000 --- a/libraries/logic/MMCStrings.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "MMCStrings.h" - -/// TAKEN FROM Qt, because it doesn't expose it intelligently -static inline QChar getNextChar(const QString &s, int location) -{ - return (location < s.length()) ? s.at(location) : QChar(); -} - -/// TAKEN FROM Qt, because it doesn't expose it intelligently -int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) -{ - for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) - { - // skip spaces, tabs and 0's - QChar c1 = getNextChar(s1, l1); - while (c1.isSpace()) - c1 = getNextChar(s1, ++l1); - QChar c2 = getNextChar(s2, l2); - while (c2.isSpace()) - c2 = getNextChar(s2, ++l2); - - if (c1.isDigit() && c2.isDigit()) - { - while (c1.digitValue() == 0) - c1 = getNextChar(s1, ++l1); - while (c2.digitValue() == 0) - c2 = getNextChar(s2, ++l2); - - int lookAheadLocation1 = l1; - int lookAheadLocation2 = l2; - int currentReturnValue = 0; - // find the last digit, setting currentReturnValue as we go if it isn't equal - for (QChar lookAhead1 = c1, lookAhead2 = c2; - (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); - lookAhead1 = getNextChar(s1, ++lookAheadLocation1), - lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) - { - bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); - bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); - if (!is1ADigit && !is2ADigit) - break; - if (!is1ADigit) - return -1; - if (!is2ADigit) - return 1; - if (currentReturnValue == 0) - { - if (lookAhead1 < lookAhead2) - { - currentReturnValue = -1; - } - else if (lookAhead1 > lookAhead2) - { - currentReturnValue = 1; - } - } - } - if (currentReturnValue != 0) - return currentReturnValue; - } - if (cs == Qt::CaseInsensitive) - { - if (!c1.isLower()) - c1 = c1.toLower(); - if (!c2.isLower()) - c2 = c2.toLower(); - } - int r = QString::localeAwareCompare(c1, c2); - if (r < 0) - return -1; - if (r > 0) - return 1; - } - // The two strings are the same (02 == 2) so fall back to the normal sort - return QString::compare(s1, s2, cs); -} diff --git a/libraries/logic/MMCStrings.h b/libraries/logic/MMCStrings.h deleted file mode 100644 index 5606b909..00000000 --- a/libraries/logic/MMCStrings.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -#include "multimc_logic_export.h" - -namespace Strings -{ - int MULTIMC_LOGIC_EXPORT naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs); -} diff --git a/libraries/logic/MMCZip.cpp b/libraries/logic/MMCZip.cpp deleted file mode 100644 index 0f35bc70..00000000 --- a/libraries/logic/MMCZip.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* -Copyright (C) 2010 Roberto Pompermaier -Copyright (C) 2005-2014 Sergey A. Tachenov - -Parts of this file were part of QuaZIP. - -QuaZIP 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.1 of the License, or -(at your option) any later version. - -QuaZIP 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 QuaZIP. If not, see . - -See COPYING file for the full LGPL text. - -Original ZIP package is copyrighted by Gilles Vollant and contributors, -see quazip/(un)MMCZip.h files for details. Basically it's the zlib license. -*/ - -#include -#include -#include -#include "MMCZip.h" -#include "FileSystem.h" - -#include - -bool copyData(QIODevice &inFile, QIODevice &outFile) -{ - while (!inFile.atEnd()) - { - char buf[4096]; - qint64 readLen = inFile.read(buf, 4096); - if (readLen <= 0) - return false; - if (outFile.write(buf, readLen) != readLen) - return false; - } - return true; -} - -QStringList MMCZip::extractDir(QString fileCompressed, QString dir) -{ - return JlCompress::extractDir(fileCompressed, dir); -} - -bool compressFile(QuaZip *zip, QString fileName, QString fileDest) -{ - if (!zip) - { - return false; - } - if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend && - zip->getMode() != QuaZip::mdAdd) - { - return false; - } - - QFile inFile; - inFile.setFileName(fileName); - if (!inFile.open(QIODevice::ReadOnly)) - { - return false; - } - - QuaZipFile outFile(zip); - if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()))) - { - return false; - } - - if (!copyData(inFile, outFile) || outFile.getZipError() != UNZ_OK) - { - return false; - } - - outFile.close(); - if (outFile.getZipError() != UNZ_OK) - { - return false; - } - inFile.close(); - - return true; -} - -bool MMCZip::compressSubDir(QuaZip* zip, QString dir, QString origDir, QSet& added, QString prefix, const SeparatorPrefixTree <'/'> * blacklist) -{ - if (!zip) return false; - if (zip->getMode()!=QuaZip::mdCreate && zip->getMode()!=QuaZip::mdAppend && zip->getMode()!=QuaZip::mdAdd) - { - return false; - } - - QDir directory(dir); - if (!directory.exists()) - { - return false; - } - - QDir origDirectory(origDir); - if (dir != origDir) - { - QString internalDirName = origDirectory.relativeFilePath(dir); - if(!blacklist || !blacklist->covers(internalDirName)) - { - QuaZipFile dirZipFile(zip); - auto dirPrefix = FS::PathCombine(prefix, origDirectory.relativeFilePath(dir)) + "/"; - if (!dirZipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(dirPrefix, dir), 0, 0, 0)) - { - return false; - } - dirZipFile.close(); - } - } - - QFileInfoList files = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); - for (auto file: files) - { - if(!file.isDir()) - { - continue; - } - if(!compressSubDir(zip,file.absoluteFilePath(),origDir, added, prefix, blacklist)) - { - return false; - } - } - - files = directory.entryInfoList(QDir::Files); - for (auto file: files) - { - if(!file.isFile()) - { - continue; - } - - if(file.absoluteFilePath()==zip->getZipName()) - { - continue; - } - - QString filename = origDirectory.relativeFilePath(file.absoluteFilePath()); - if(blacklist && blacklist->covers(filename)) - { - continue; - } - if(prefix.size()) - { - filename = FS::PathCombine(prefix, filename); - } - added.insert(filename); - if (!compressFile(zip,file.absoluteFilePath(),filename)) - { - return false; - } - } - - return true; -} - -bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet &contained, - std::function filter) -{ - QuaZip modZip(from.filePath()); - modZip.open(QuaZip::mdUnzip); - - QuaZipFile fileInsideMod(&modZip); - QuaZipFile zipOutFile(into); - for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) - { - QString filename = modZip.getCurrentFileName(); - if (!filter(filename)) - { - qDebug() << "Skipping file " << filename << " from " - << from.fileName() << " - filtered"; - continue; - } - if (contained.contains(filename)) - { - qDebug() << "Skipping already contained file " << filename << " from " - << from.fileName(); - continue; - } - contained.insert(filename); - - if (!fileInsideMod.open(QIODevice::ReadOnly)) - { - qCritical() << "Failed to open " << filename << " from " << from.fileName(); - return false; - } - - QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); - - if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) - { - qCritical() << "Failed to open " << filename << " in the jar"; - fileInsideMod.close(); - return false; - } - if (!copyData(fileInsideMod, zipOutFile)) - { - zipOutFile.close(); - fileInsideMod.close(); - qCritical() << "Failed to copy data of " << filename << " into the jar"; - return false; - } - zipOutFile.close(); - fileInsideMod.close(); - } - return true; -} - -bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) -{ - QuaZip zipOut(targetJarPath); - if (!zipOut.open(QuaZip::mdCreate)) - { - QFile::remove(targetJarPath); - qCritical() << "Failed to open the minecraft.jar for modding"; - return false; - } - // Files already added to the jar. - // These files will be skipped. - QSet addedFiles; - - // Modify the jar - QListIterator i(mods); - i.toBack(); - while (i.hasPrevious()) - { - const Mod &mod = i.previous(); - // do not merge disabled mods. - if (!mod.enabled()) - continue; - if (mod.type() == Mod::MOD_ZIPFILE) - { - if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles, noFilter)) - { - zipOut.close(); - QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; - return false; - } - } - else if (mod.type() == Mod::MOD_SINGLEFILE) - { - auto filename = mod.filename(); - if (!compressFile(&zipOut, filename.absoluteFilePath(), - filename.fileName())) - { - zipOut.close(); - QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; - return false; - } - addedFiles.insert(filename.fileName()); - } - else if (mod.type() == Mod::MOD_FOLDER) - { - auto filename = mod.filename(); - QString what_to_zip = filename.absoluteFilePath(); - QDir dir(what_to_zip); - dir.cdUp(); - QString parent_dir = dir.absolutePath(); - if (!compressSubDir(&zipOut, what_to_zip, parent_dir, addedFiles)) - { - zipOut.close(); - QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; - return false; - } - qDebug() << "Adding folder " << filename.fileName() << " from " - << filename.absoluteFilePath(); - } - } - - if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, metaInfFilter)) - { - zipOut.close(); - QFile::remove(targetJarPath); - qCritical() << "Failed to insert minecraft.jar contents."; - return false; - } - - // Recompress the jar - zipOut.close(); - if (zipOut.getZipError() != 0) - { - QFile::remove(targetJarPath); - qCritical() << "Failed to finalize minecraft.jar!"; - return false; - } - return true; -} - -bool MMCZip::noFilter(QString) -{ - return true; -} - -bool MMCZip::metaInfFilter(QString key) -{ - if(key.contains("META-INF")) - { - return false; - } - return true; -} - -bool MMCZip::compressDir(QString zipFile, QString dir, QString prefix, const SeparatorPrefixTree <'/'> * blacklist) -{ - QuaZip zip(zipFile); - QDir().mkpath(QFileInfo(zipFile).absolutePath()); - if(!zip.open(QuaZip::mdCreate)) - { - QFile::remove(zipFile); - return false; - } - - QSet added; - if (!compressSubDir(&zip, dir, dir, added, prefix, blacklist)) - { - QFile::remove(zipFile); - return false; - } - zip.close(); - if(zip.getZipError()!=0) - { - QFile::remove(zipFile); - return false; - } - return true; -} - -QString MMCZip::findFileInZip(QuaZip * zip, const QString & what, const QString &root) -{ - QuaZipDir rootDir(zip, root); - for(auto fileName: rootDir.entryList(QDir::Files)) - { - if(fileName == what) - return root; - } - for(auto fileName: rootDir.entryList(QDir::Dirs)) - { - QString result = findFileInZip(zip, what, root + fileName); - if(!result.isEmpty()) - { - return result; - } - } - return QString(); -} - -bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root) -{ - QuaZipDir rootDir(zip, root); - for(auto fileName: rootDir.entryList(QDir::Files)) - { - if(fileName == what) - { - result.append(root); - return true; - } - } - for(auto fileName: rootDir.entryList(QDir::Dirs)) - { - findFilesInZip(zip, what, result, root + fileName); - } - return !result.isEmpty(); -} - -bool removeFile(QStringList listFile) -{ - bool ret = true; - for (int i = 0; i < listFile.count(); i++) - { - ret &= QFile::remove(listFile.at(i)); - } - return ret; -} - -bool MMCZip::extractFile(QuaZip *zip, const QString &fileName, const QString &fileDest) -{ - if(!zip) - return false; - - if (zip->getMode() != QuaZip::mdUnzip) - return false; - - if (!fileName.isEmpty()) - zip->setCurrentFile(fileName); - - QuaZipFile inFile(zip); - if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError() != UNZ_OK) - return false; - - // Controllo esistenza cartella file risultato - QDir curDir; - if (fileDest.endsWith('/')) - { - if (!curDir.mkpath(fileDest)) - { - return false; - } - } - else - { - if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) - { - return false; - } - } - - QuaZipFileInfo64 info; - if (!zip->getCurrentFileInfo(&info)) - return false; - - QFile::Permissions srcPerm = info.getPermissions(); - if (fileDest.endsWith('/') && QFileInfo(fileDest).isDir()) - { - if (srcPerm != 0) - { - QFile(fileDest).setPermissions(srcPerm); - } - return true; - } - - QFile outFile; - outFile.setFileName(fileDest); - if (!outFile.open(QIODevice::WriteOnly)) - return false; - - if (!copyData(inFile, outFile) || inFile.getZipError() != UNZ_OK) - { - outFile.close(); - removeFile(QStringList(fileDest)); - return false; - } - outFile.close(); - - inFile.close(); - if (inFile.getZipError() != UNZ_OK) - { - removeFile(QStringList(fileDest)); - return false; - } - - if (srcPerm != 0) - { - outFile.setPermissions(srcPerm); - } - return true; -} - -QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) -{ - QDir directory(target); - QStringList extracted; - if (!zip->goToFirstFile()) - { - return QStringList(); - } - do - { - QString name = zip->getCurrentFileName(); - if(!name.startsWith(subdir)) - { - continue; - } - name.remove(0, subdir.size()); - QString absFilePath = directory.absoluteFilePath(name); - if(name.isEmpty()) - { - absFilePath += "/"; - } - if (!extractFile(zip, "", absFilePath)) - { - removeFile(extracted); - return QStringList(); - } - extracted.append(absFilePath); - } while (zip->goToNextFile()); - return extracted; -} diff --git a/libraries/logic/MMCZip.h b/libraries/logic/MMCZip.h deleted file mode 100644 index f350e668..00000000 --- a/libraries/logic/MMCZip.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include -#include -#include -#include "minecraft/Mod.h" -#include "SeparatorPrefixTree.h" -#include - -#include "multimc_logic_export.h" - -class QuaZip; - -namespace MMCZip -{ - /** - * Compress a subdirectory. - * \param parentZip Opened zip containing the parent directory. - * \param dir The full path to the directory to pack. - * \param parentDir The full path to the directory corresponding to the root of the ZIP. - * \param recursive Whether to pack sub-directories as well or only files. - * \return true if success, false otherwise. - */ - bool MULTIMC_LOGIC_EXPORT compressSubDir(QuaZip *zip, QString dir, QString origDir, QSet &added, - QString prefix = QString(), const SeparatorPrefixTree <'/'> * blacklist = nullptr); - - /** - * Compress a whole directory. - * \param fileCompressed The name of the archive. - * \param dir The directory to compress. - * \param recursive Whether to pack the subdirectories as well, or just regular files. - * \return true if success, false otherwise. - */ - bool MULTIMC_LOGIC_EXPORT compressDir(QString zipFile, QString dir, QString prefix = QString(), const SeparatorPrefixTree <'/'> * blacklist = nullptr); - - /// filter function for @mergeZipFiles - passthrough - bool MULTIMC_LOGIC_EXPORT noFilter(QString key); - - /// filter function for @mergeZipFiles - ignores METAINF - bool MULTIMC_LOGIC_EXPORT metaInfFilter(QString key); - - /** - * Merge two zip files, using a filter function - */ - bool MULTIMC_LOGIC_EXPORT mergeZipFiles(QuaZip *into, QFileInfo from, QSet &contained, std::function filter); - - /** - * take a source jar, add mods to it, resulting in target jar - */ - bool MULTIMC_LOGIC_EXPORT createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); - - /** - * Extract a whole archive. - * - * \param fileCompressed The name of the archive. - * \param dir The directory to extract to, the current directory if - * left empty. - * \return The list of the full paths of the files extracted, empty on failure. - */ - QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir = QString()); - - /** - * Find a single file in archive by file name (not path) - * - * \return the path prefix where the file is - */ - QString MULTIMC_LOGIC_EXPORT findFileInZip(QuaZip * zip, const QString & what, const QString &root = QString()); - - /** - * Find a multiple files of the same name in archive by file name - * If a file is found in a path, no deeper paths are searched - * - * \return true if anything was found - */ - bool MULTIMC_LOGIC_EXPORT findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString()); - - /** - * Extract a single file to a destination - * - * \return true if it succeeds - */ - bool MULTIMC_LOGIC_EXPORT extractFile(QuaZip *zip, const QString &fileName, const QString &fileDest); - - /** - * Extract a subdirectory from an archive - */ - QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); -} diff --git a/libraries/logic/NullInstance.h b/libraries/logic/NullInstance.h deleted file mode 100644 index fbb2d985..00000000 --- a/libraries/logic/NullInstance.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "BaseInstance.h" - -class NullInstance: public BaseInstance -{ -public: - NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) - :BaseInstance(globalSettings, settings, rootDir) - { - setFlag(BaseInstance::VersionBrokenFlag); - } - virtual ~NullInstance() {}; - virtual bool setIntendedVersionId(QString) override - { - return false; - } - virtual void cleanupAfterRun() override - { - } - virtual QString currentVersionId() const override - { - return "Null"; - }; - virtual QString intendedVersionId() const override - { - return "Null"; - }; - virtual void init() override - { - }; - virtual QString getStatusbarDescription() override - { - return tr("Unknown instance type"); - }; - virtual bool shouldUpdate() const override - { - return false; - }; - virtual QSet< QString > traits() override - { - return {}; - }; - virtual QString instanceConfigFolder() const override - { - return instanceRoot(); - }; - virtual std::shared_ptr createLaunchTask(AuthSessionPtr) override - { - return nullptr; - } - virtual std::shared_ptr< Task > createUpdateTask() override - { - return nullptr; - } - virtual std::shared_ptr createJarModdingTask() override - { - return nullptr; - } - virtual void setShouldUpdate(bool) override - { - }; - virtual std::shared_ptr< BaseVersionList > versionList() const override - { - return nullptr; - }; - virtual QProcessEnvironment createEnvironment() override - { - return QProcessEnvironment(); - } - virtual QMap getVariables() const override - { - return QMap(); - } - virtual IPathMatcher::Ptr getLogFileMatcher() override - { - return nullptr; - } - virtual QString getLogFileRoot() override - { - return instanceRoot(); - } - virtual QString typeName() const override - { - return "Null"; - } - bool canExport() const override - { - return false; - } -}; diff --git a/libraries/logic/QObjectPtr.h b/libraries/logic/QObjectPtr.h deleted file mode 100644 index b81b3234..00000000 --- a/libraries/logic/QObjectPtr.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include -#include - -namespace details -{ -struct DeleteQObjectLater -{ - void operator()(QObject *obj) const - { - obj->deleteLater(); - } -}; -} -/** - * A unique pointer class with unique pointer semantics intended for derivates of QObject - * Calls deleteLater() instead of destroying the contained object immediately - */ -template using unique_qobject_ptr = std::unique_ptr; - -/** - * A shared pointer class with shared pointer semantics intended for derivates of QObject - * Calls deleteLater() instead of destroying the contained object immediately - */ -template -class shared_qobject_ptr -{ -public: - shared_qobject_ptr(){} - shared_qobject_ptr(T * wrap) - { - reset(wrap); - } - shared_qobject_ptr(const shared_qobject_ptr& other) - { - m_ptr = other.m_ptr; - } - template - shared_qobject_ptr(const shared_qobject_ptr &other) - { - m_ptr = other.unwrap(); - } - -public: - void reset(T * wrap) - { - using namespace std::placeholders; - m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1)); - } - void reset() - { - m_ptr.reset(); - } - T * get() const - { - return m_ptr.get(); - } - T * operator->() const - { - return m_ptr.get(); - } - T & operator*() const - { - return *m_ptr.get(); - } - operator bool() const - { - return m_ptr.get() != nullptr; - } - const std::shared_ptr unwrap() const - { - return m_ptr; - } - -private: - std::shared_ptr m_ptr; -}; diff --git a/libraries/logic/RWStorage.h b/libraries/logic/RWStorage.h deleted file mode 100644 index b1598ca4..00000000 --- a/libraries/logic/RWStorage.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -template -class RWStorage -{ -public: - void add(K key, V value) - { - QWriteLocker l(&lock); - cache[key] = value; - stale_entries.remove(key); - } - V get(K key) - { - QReadLocker l(&lock); - if(cache.contains(key)) - { - return cache[key]; - } - else return V(); - } - bool get(K key, V& value) - { - QReadLocker l(&lock); - if(cache.contains(key)) - { - value = cache[key]; - return true; - } - else return false; - } - bool has(K key) - { - QReadLocker l(&lock); - return cache.contains(key); - } - bool stale(K key) - { - QReadLocker l(&lock); - if(!cache.contains(key)) - return true; - return stale_entries.contains(key); - } - void setStale(K key) - { - QReadLocker l(&lock); - if(cache.contains(key)) - { - stale_entries.insert(key); - } - } - void clear() - { - QWriteLocker l(&lock); - cache.clear(); - } -private: - QReadWriteLock lock; - QMap cache; - QSet stale_entries; -}; \ No newline at end of file diff --git a/libraries/logic/RecursiveFileSystemWatcher.cpp b/libraries/logic/RecursiveFileSystemWatcher.cpp deleted file mode 100644 index 59c3f0f0..00000000 --- a/libraries/logic/RecursiveFileSystemWatcher.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "RecursiveFileSystemWatcher.h" - -#include -#include - -RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent) - : QObject(parent), m_watcher(new QFileSystemWatcher(this)) -{ - connect(m_watcher, &QFileSystemWatcher::fileChanged, this, - &RecursiveFileSystemWatcher::fileChange); - connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, - &RecursiveFileSystemWatcher::directoryChange); -} - -void RecursiveFileSystemWatcher::setRootDir(const QDir &root) -{ - bool wasEnabled = m_isEnabled; - disable(); - m_root = root; - setFiles(scanRecursive(m_root)); - if (wasEnabled) - { - enable(); - } -} -void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles) -{ - bool wasEnabled = m_isEnabled; - disable(); - m_watchFiles = watchFiles; - if (wasEnabled) - { - enable(); - } -} - -void RecursiveFileSystemWatcher::enable() -{ - if (m_isEnabled) - { - return; - } - Q_ASSERT(m_root != QDir::root()); - addFilesToWatcherRecursive(m_root); - m_isEnabled = true; -} -void RecursiveFileSystemWatcher::disable() -{ - if (!m_isEnabled) - { - return; - } - m_isEnabled = false; - m_watcher->removePaths(m_watcher->files()); - m_watcher->removePaths(m_watcher->directories()); -} - -void RecursiveFileSystemWatcher::setFiles(const QStringList &files) -{ - if (files != m_files) - { - m_files = files; - emit filesChanged(); - } -} - -void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir) -{ - m_watcher->addPath(dir.absolutePath()); - for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) - { - addFilesToWatcherRecursive(dir.absoluteFilePath(directory)); - } - if (m_watchFiles) - { - for (const QFileInfo &info : dir.entryInfoList(QDir::Files)) - { - m_watcher->addPath(info.absoluteFilePath()); - } - } -} -QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory) -{ - QStringList ret; - if(!m_matcher) - { - return {}; - } - for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) - { - ret.append(scanRecursive(directory.absoluteFilePath(dir))); - } - for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden)) - { - auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file)); - if (m_matcher->matches(relPath)) - { - ret.append(relPath); - } - } - return ret; -} - -void RecursiveFileSystemWatcher::fileChange(const QString &path) -{ - emit fileChanged(path); -} -void RecursiveFileSystemWatcher::directoryChange(const QString &path) -{ - setFiles(scanRecursive(m_root)); -} diff --git a/libraries/logic/RecursiveFileSystemWatcher.h b/libraries/logic/RecursiveFileSystemWatcher.h deleted file mode 100644 index 07bce0b9..00000000 --- a/libraries/logic/RecursiveFileSystemWatcher.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include -#include "pathmatcher/IPathMatcher.h" - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT RecursiveFileSystemWatcher : public QObject -{ - Q_OBJECT -public: - RecursiveFileSystemWatcher(QObject *parent); - - void setRootDir(const QDir &root); - QDir rootDir() const - { - return m_root; - } - - // WARNING: setting this to true may be bad for performance - void setWatchFiles(const bool watchFiles); - bool watchFiles() const - { - return m_watchFiles; - } - - void setMatcher(IPathMatcher::Ptr matcher) - { - m_matcher = matcher; - } - - QStringList files() const - { - return m_files; - } - -signals: - void filesChanged(); - void fileChanged(const QString &path); - -public slots: - void enable(); - void disable(); - -private: - QDir m_root; - bool m_watchFiles = false; - bool m_isEnabled = false; - IPathMatcher::Ptr m_matcher; - - QFileSystemWatcher *m_watcher; - - QStringList m_files; - void setFiles(const QStringList &files); - - void addFilesToWatcherRecursive(const QDir &dir); - QStringList scanRecursive(const QDir &dir); - -private slots: - void fileChange(const QString &path); - void directoryChange(const QString &path); -}; diff --git a/libraries/logic/SeparatorPrefixTree.h b/libraries/logic/SeparatorPrefixTree.h deleted file mode 100644 index fd149af0..00000000 --- a/libraries/logic/SeparatorPrefixTree.h +++ /dev/null @@ -1,298 +0,0 @@ -#pragma once -#include -#include -#include - -template -class SeparatorPrefixTree -{ -public: - SeparatorPrefixTree(QStringList paths) - { - insert(paths); - } - - SeparatorPrefixTree(bool contained = false) - { - m_contained = contained; - } - - void insert(QStringList paths) - { - for(auto &path: paths) - { - insert(path); - } - } - - /// insert an exact path into the tree - SeparatorPrefixTree & insert(QString path) - { - auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { - children[path] = SeparatorPrefixTree(true); - return children[path]; - } - else - { - auto prefix = path.left(sepIndex); - if(!children.contains(prefix)) - { - children[prefix] = SeparatorPrefixTree(false); - } - return children[prefix].insert(path.mid(sepIndex + 1)); - } - } - - /// is the path fully contained in the tree? - bool contains(QString path) const - { - auto node = find(path); - return node != nullptr; - } - - /// does the tree cover a path? That means the prefix of the path is contained in the tree - bool covers(QString path) const - { - // if we found some valid node, it's good enough. the tree covers the path - if(m_contained) - { - return true; - } - auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { - auto found = children.find(path); - if(found == children.end()) - { - return false; - } - return (*found).covers(QString()); - } - else - { - auto prefix = path.left(sepIndex); - auto found = children.find(prefix); - if(found == children.end()) - { - return false; - } - return (*found).covers(path.mid(sepIndex + 1)); - } - } - - /// return the contained path that covers the path specified - QString cover(QString path) const - { - // if we found some valid node, it's good enough. the tree covers the path - if(m_contained) - { - return QString(""); - } - auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { - auto found = children.find(path); - if(found == children.end()) - { - return QString(); - } - auto nested = (*found).cover(QString()); - if(nested.isNull()) - { - return nested; - } - if(nested.isEmpty()) - return path; - return path + Tseparator + nested; - } - else - { - auto prefix = path.left(sepIndex); - auto found = children.find(prefix); - if(found == children.end()) - { - return QString(); - } - auto nested = (*found).cover(path.mid(sepIndex + 1)); - if(nested.isNull()) - { - return nested; - } - if(nested.isEmpty()) - return prefix; - return prefix + Tseparator + nested; - } - } - - /// Does the path-specified node exist in the tree? It does not have to be contained. - bool exists(QString path) const - { - auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { - auto found = children.find(path); - if(found == children.end()) - { - return false; - } - return true; - } - else - { - auto prefix = path.left(sepIndex); - auto found = children.find(prefix); - if(found == children.end()) - { - return false; - } - return (*found).exists(path.mid(sepIndex + 1)); - } - } - - /// find a node in the tree by name - const SeparatorPrefixTree * find(QString path) const - { - auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { - auto found = children.find(path); - if(found == children.end()) - { - return nullptr; - } - return &(*found); - } - else - { - auto prefix = path.left(sepIndex); - auto found = children.find(prefix); - if(found == children.end()) - { - return nullptr; - } - return (*found).find(path.mid(sepIndex + 1)); - } - } - - /// is this a leaf node? - bool leaf() const - { - return children.isEmpty(); - } - - /// is this node actually contained in the tree, or is it purely structural? - bool contained() const - { - return m_contained; - } - - /// Remove a path from the tree - bool remove(QString path) - { - return removeInternal(path) != Failed; - } - - /// Clear all children of this node tree node - void clear() - { - children.clear(); - } - - QStringList toStringList() const - { - QStringList collected; - // collecting these is more expensive. - auto iter = children.begin(); - while(iter != children.end()) - { - QStringList list = iter.value().toStringList(); - for(int i = 0; i < list.size(); i++) - { - list[i] = iter.key() + Tseparator + list[i]; - } - collected.append(list); - if((*iter).m_contained) - { - collected.append(iter.key()); - } - iter++; - } - return collected; - } -private: - enum Removal - { - Failed, - Succeeded, - HasChildren - }; - Removal removeInternal(QString path = QString()) - { - if(path.isEmpty()) - { - if(!m_contained) - { - // remove all children - we are removing a prefix - clear(); - return Succeeded; - } - m_contained = false; - if(children.size()) - { - return HasChildren; - } - return Succeeded; - } - Removal remStatus = Failed; - QString childToRemove; - auto sepIndex = path.indexOf(Tseparator); - if(sepIndex == -1) - { - childToRemove = path; - auto found = children.find(childToRemove); - if(found == children.end()) - { - return Failed; - } - remStatus = (*found).removeInternal(); - } - else - { - childToRemove = path.left(sepIndex); - auto found = children.find(childToRemove); - if(found == children.end()) - { - return Failed; - } - remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); - } - switch (remStatus) - { - case Failed: - case HasChildren: - { - return remStatus; - } - case Succeeded: - { - children.remove(childToRemove); - if(m_contained) - { - return HasChildren; - } - if(children.size()) - { - return HasChildren; - } - return Succeeded; - } - } - return Failed; - } - -private: - QMap> children; - bool m_contained = false; -}; diff --git a/libraries/logic/TypeMagic.h b/libraries/logic/TypeMagic.h deleted file mode 100644 index fa9d12a9..00000000 --- a/libraries/logic/TypeMagic.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -namespace TypeMagic -{ -/** "Cleans" the given type T by stripping references (&) and cv-qualifiers (const, volatile) from it - * const int => int - * QString & => QString - * const unsigned long long & => unsigned long long - * - * Usage: - * using Cleaned = Detail::CleanType; - * static_assert(std::is_same, "Cleaned == int"); - */ -// the order of remove_cv and remove_reference matters! -template -using CleanType = typename std::remove_cv::type>::type; - -/// For functors (structs with operator()), including lambdas, which in **most** cases are functors -/// "Calls" Function or Function -template struct Function : public Function {}; -/// For function pointers (&function), including static members (&Class::member) -template struct Function : public Function {}; -/// Default specialization used by others. -template struct Function -{ - using ReturnType = Ret; - using Argument = Arg; -}; -/// For member functions. Also used by the lambda overload if the lambda captures [this] -template struct Function : public Function {}; -template struct Function : public Function {}; -/// Overload for references -template struct Function : public Function {}; -/// Overload for rvalues -template struct Function : public Function {}; -// for more info: https://functionalcpp.wordpress.com/2013/08/05/function-traits/ -} diff --git a/libraries/logic/Version.cpp b/libraries/logic/Version.cpp deleted file mode 100644 index 3c4727ad..00000000 --- a/libraries/logic/Version.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "Version.h" - -#include -#include -#include -#include - -Version::Version(const QString &str) : m_string(str) -{ - parse(); -} - -bool Version::operator<(const Version &other) const -{ - const int size = qMax(m_sections.size(), other.m_sections.size()); - for (int i = 0; i < size; ++i) - { - const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); - const Section sec2 = - (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); - if (sec1 != sec2) - { - return sec1 < sec2; - } - } - - return false; -} -bool Version::operator<=(const Version &other) const -{ - return *this < other || *this == other; -} -bool Version::operator>(const Version &other) const -{ - const int size = qMax(m_sections.size(), other.m_sections.size()); - for (int i = 0; i < size; ++i) - { - const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); - const Section sec2 = - (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); - if (sec1 != sec2) - { - return sec1 > sec2; - } - } - - return false; -} -bool Version::operator>=(const Version &other) const -{ - return *this > other || *this == other; -} -bool Version::operator==(const Version &other) const -{ - const int size = qMax(m_sections.size(), other.m_sections.size()); - for (int i = 0; i < size; ++i) - { - const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i); - const Section sec2 = - (i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i); - if (sec1 != sec2) - { - return false; - } - } - - return true; -} -bool Version::operator!=(const Version &other) const -{ - return !operator==(other); -} - -void Version::parse() -{ - m_sections.clear(); - - QStringList parts = m_string.split('.'); - - for (const auto part : parts) - { - m_sections.append(Section(part)); - } -} - -bool versionIsInInterval(const QString &version, const QString &interval) -{ - return versionIsInInterval(Version(version), interval); -} -bool versionIsInInterval(const Version &version, const QString &interval) -{ - if (interval.isEmpty() || version.toString() == interval) - { - return true; - } - - // Interval notation is used - QRegularExpression exp( - "(?[\\[\\]\\(\\)])(?.*?)(,(?.*?))?(?[\\[\\]\\(\\)]),?"); - QRegularExpressionMatch match = exp.match(interval); - if (match.hasMatch()) - { - const QChar start = match.captured("start").at(0); - const QChar end = match.captured("end").at(0); - const QString bottom = match.captured("bottom"); - const QString top = match.captured("top"); - - // check if in range (bottom) - if (!bottom.isEmpty()) - { - const auto bottomVersion = Version(bottom); - if ((start == '[') && !(version >= bottomVersion)) - { - return false; - } - else if ((start == '(') && !(version > bottomVersion)) - { - return false; - } - } - - // check if in range (top) - if (!top.isEmpty()) - { - const auto topVersion = Version(top); - if ((end == ']') && !(version <= topVersion)) - { - return false; - } - else if ((end == ')') && !(version < topVersion)) - { - return false; - } - } - - return true; - } - - return false; -} diff --git a/libraries/logic/Version.h b/libraries/logic/Version.h deleted file mode 100644 index b5946ced..00000000 --- a/libraries/logic/Version.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include - -#include "multimc_logic_export.h" - -class QUrl; - -struct MULTIMC_LOGIC_EXPORT Version -{ - Version(const QString &str); - Version() {} - - bool operator<(const Version &other) const; - bool operator<=(const Version &other) const; - bool operator>(const Version &other) const; - bool operator>=(const Version &other) const; - bool operator==(const Version &other) const; - bool operator!=(const Version &other) const; - - QString toString() const - { - return m_string; - } - -private: - QString m_string; - struct Section - { - explicit Section(const QString &fullString) - { - m_fullString = fullString; - int cutoff = m_fullString.size(); - for(int i = 0; i < m_fullString.size(); i++) - { - if(!m_fullString[i].isDigit()) - { - cutoff = i; - break; - } - } - auto numPart = m_fullString.leftRef(cutoff); - if(numPart.size()) - { - numValid = true; - m_numPart = numPart.toInt(); - } - auto stringPart = m_fullString.midRef(cutoff); - if(stringPart.size()) - { - m_stringPart = stringPart.toString(); - } - } - explicit Section() {} - bool numValid = false; - int m_numPart = 0; - QString m_stringPart; - QString m_fullString; - - inline bool operator!=(const Section &other) const - { - if(numValid && other.numValid) - { - return m_numPart != other.m_numPart || m_stringPart != other.m_stringPart; - } - else - { - return m_fullString != other.m_fullString; - } - } - inline bool operator<(const Section &other) const - { - if(numValid && other.numValid) - { - if(m_numPart < other.m_numPart) - return true; - if(m_numPart == other.m_numPart && m_stringPart < other.m_stringPart) - return true; - return false; - } - else - { - return m_fullString < other.m_fullString; - } - } - inline bool operator>(const Section &other) const - { - if(numValid && other.numValid) - { - if(m_numPart > other.m_numPart) - return true; - if(m_numPart == other.m_numPart && m_stringPart > other.m_stringPart) - return true; - return false; - } - else - { - return m_fullString > other.m_fullString; - } - } - }; - QList
    m_sections; - - void parse(); -}; - -MULTIMC_LOGIC_EXPORT bool versionIsInInterval(const QString &version, const QString &interval); -MULTIMC_LOGIC_EXPORT bool versionIsInInterval(const Version &version, const QString &interval); - diff --git a/libraries/logic/java/JavaChecker.cpp b/libraries/logic/java/JavaChecker.cpp deleted file mode 100644 index 54d552a9..00000000 --- a/libraries/logic/java/JavaChecker.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "JavaChecker.h" -#include -#include -#include -#include -#include -#include -#include - -JavaChecker::JavaChecker(QObject *parent) : QObject(parent) -{ -} - -void JavaChecker::performCheck() -{ - QString checkerJar = FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "JavaCheck.jar"); - - QStringList args; - - process.reset(new QProcess()); - if(m_args.size()) - { - auto extraArgs = Commandline::splitArgs(m_args); - args.append(extraArgs); - } - if(m_minMem != 0) - { - args << QString("-Xms%1m").arg(m_minMem); - } - if(m_maxMem != 0) - { - args << QString("-Xmx%1m").arg(m_maxMem); - } - if(m_permGen != 64) - { - args << QString("-XX:PermSize=%1m").arg(m_permGen); - } - - args.append({"-jar", checkerJar}); - process->setArguments(args); - process->setProgram(m_path); - process->setProcessChannelMode(QProcess::SeparateChannels); - qDebug() << "Running java checker: " + m_path + args.join(" ");; - - connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); - connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); - connect(process.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(stdoutReady())); - connect(process.get(), SIGNAL(readyReadStandardError()), this, SLOT(stderrReady())); - connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); - killTimer.setSingleShot(true); - killTimer.start(15000); - process->start(); -} - -void JavaChecker::stdoutReady() -{ - QByteArray data = process->readAllStandardOutput(); - QString added = QString::fromLocal8Bit(data); - added.remove('\r'); - m_stdout += added; -} - -void JavaChecker::stderrReady() -{ - QByteArray data = process->readAllStandardError(); - QString added = QString::fromLocal8Bit(data); - added.remove('\r'); - m_stderr += added; -} - -void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) -{ - killTimer.stop(); - QProcessPtr _process; - _process.swap(process); - - JavaCheckResult result; - { - result.path = m_path; - result.id = m_id; - } - result.errorLog = m_stderr; - qDebug() << "STDOUT" << m_stdout; - qWarning() << "STDERR" << m_stderr; - qDebug() << "Java checker finished with status " << status << " exit code " << exitcode; - - if (status == QProcess::CrashExit || exitcode == 1) - { - qDebug() << "Java checker failed!"; - emit checkFinished(result); - return; - } - - bool success = true; - - QMap results; - QStringList lines = m_stdout.split("\n", QString::SkipEmptyParts); - for(QString line : lines) - { - line = line.trimmed(); - - auto parts = line.split('=', QString::SkipEmptyParts); - if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) - { - success = false; - } - else - { - results.insert(parts[0], parts[1]); - } - } - - if(!results.contains("os.arch") || !results.contains("java.version") || !success) - { - qDebug() << "Java checker failed - couldn't extract required information."; - emit checkFinished(result); - return; - } - - auto os_arch = results["os.arch"]; - auto java_version = results["java.version"]; - bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; - - - result.valid = true; - result.is_64bit = is_64; - result.mojangPlatform = is_64 ? "64" : "32"; - result.realPlatform = os_arch; - result.javaVersion = java_version; - qDebug() << "Java checker succeeded."; - emit checkFinished(result); -} - -void JavaChecker::error(QProcess::ProcessError err) -{ - if(err == QProcess::FailedToStart) - { - killTimer.stop(); - qDebug() << "Java checker has failed to start."; - JavaCheckResult result; - { - result.path = m_path; - result.id = m_id; - } - - emit checkFinished(result); - return; - } -} - -void JavaChecker::timeout() -{ - // NO MERCY. NO ABUSE. - if(process) - { - qDebug() << "Java checker has been killed by timeout."; - process->kill(); - } -} diff --git a/libraries/logic/java/JavaChecker.h b/libraries/logic/java/JavaChecker.h deleted file mode 100644 index 650e7ce3..00000000 --- a/libraries/logic/java/JavaChecker.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include -#include -#include - -#include "multimc_logic_export.h" - -#include "JavaVersion.h" - -class JavaChecker; - -struct MULTIMC_LOGIC_EXPORT JavaCheckResult -{ - QString path; - QString mojangPlatform; - QString realPlatform; - JavaVersion javaVersion; - QString errorLog; - bool valid = false; - bool is_64bit = false; - int id; -}; - -typedef std::shared_ptr QProcessPtr; -typedef std::shared_ptr JavaCheckerPtr; -class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject -{ - Q_OBJECT -public: - explicit JavaChecker(QObject *parent = 0); - void performCheck(); - - QString m_path; - QString m_args; - int m_id = 0; - int m_minMem = 0; - int m_maxMem = 0; - int m_permGen = 64; - -signals: - void checkFinished(JavaCheckResult result); -private: - QProcessPtr process; - QTimer killTimer; - QString m_stdout; - QString m_stderr; -public -slots: - void timeout(); - void finished(int exitcode, QProcess::ExitStatus); - void error(QProcess::ProcessError); - void stdoutReady(); - void stderrReady(); -}; diff --git a/libraries/logic/java/JavaCheckerJob.cpp b/libraries/logic/java/JavaCheckerJob.cpp deleted file mode 100644 index 0b040e43..00000000 --- a/libraries/logic/java/JavaCheckerJob.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "JavaCheckerJob.h" - -#include - -void JavaCheckerJob::partFinished(JavaCheckResult result) -{ - num_finished++; - qDebug() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" - << javacheckers.size(); - emit progress(num_finished, javacheckers.size()); - - javaresults.replace(result.id, result); - - if (num_finished == javacheckers.size()) - { - emit finished(javaresults); - } -} - -void JavaCheckerJob::executeTask() -{ - qDebug() << m_job_name.toLocal8Bit() << " started."; - m_running = true; - for (auto iter : javacheckers) - { - javaresults.append(JavaCheckResult()); - connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); - iter->performCheck(); - } -} diff --git a/libraries/logic/java/JavaCheckerJob.h b/libraries/logic/java/JavaCheckerJob.h deleted file mode 100644 index aca0d02e..00000000 --- a/libraries/logic/java/JavaCheckerJob.h +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include "JavaChecker.h" -#include "tasks/Task.h" - -class JavaCheckerJob; -typedef std::shared_ptr JavaCheckerJobPtr; - -class JavaCheckerJob : public Task -{ - Q_OBJECT -public: - explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {}; - - bool addJavaCheckerAction(JavaCheckerPtr base) - { - javacheckers.append(base); - total_progress++; - // if this is already running, the action needs to be started right away! - if (isRunning()) - { - setProgress(current_progress, total_progress); - connect(base.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); - - base->performCheck(); - } - return true; - } - - JavaCheckerPtr operator[](int index) - { - return javacheckers[index]; - } - ; - JavaCheckerPtr first() - { - if (javacheckers.size()) - return javacheckers[0]; - return JavaCheckerPtr(); - } - int size() const - { - return javacheckers.size(); - } - virtual bool isRunning() const override - { - return m_running; - } - -signals: - void started(); - void finished(QList); - -private slots: - void partFinished(JavaCheckResult result); - -protected: - virtual void executeTask() override; - -private: - QString m_job_name; - QList javacheckers; - QList javaresults; - qint64 current_progress = 0; - qint64 total_progress = 0; - int num_finished = 0; - bool m_running = false; -}; diff --git a/libraries/logic/java/JavaInstall.cpp b/libraries/logic/java/JavaInstall.cpp deleted file mode 100644 index bb262b6e..00000000 --- a/libraries/logic/java/JavaInstall.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "JavaInstall.h" -#include - -bool JavaInstall::operator<(const JavaInstall &rhs) -{ - auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); - if(archCompare != 0) - return archCompare < 0; - if(id < rhs.id) - { - return true; - } - if(id > rhs.id) - { - return false; - } - return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; -} - -bool JavaInstall::operator==(const JavaInstall &rhs) -{ - return arch == rhs.arch && id == rhs.id && path == rhs.path; -} - -bool JavaInstall::operator>(const JavaInstall &rhs) -{ - return (!operator<(rhs)) && (!operator==(rhs)); -} diff --git a/libraries/logic/java/JavaInstall.h b/libraries/logic/java/JavaInstall.h deleted file mode 100644 index 882c7386..00000000 --- a/libraries/logic/java/JavaInstall.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "BaseVersion.h" -#include "JavaVersion.h" - -struct JavaInstall : public BaseVersion -{ - JavaInstall(){} - JavaInstall(QString id, QString arch, QString path) - : id(id), arch(arch), path(path) - { - } - virtual QString descriptor() - { - return id.toString(); - } - - virtual QString name() - { - return id.toString(); - } - - virtual QString typeString() const - { - return arch; - } - - bool operator<(const JavaInstall & rhs); - bool operator==(const JavaInstall & rhs); - bool operator>(const JavaInstall & rhs); - - JavaVersion id; - QString arch; - QString path; - bool recommended = false; -}; - -typedef std::shared_ptr JavaInstallPtr; diff --git a/libraries/logic/java/JavaInstallList.cpp b/libraries/logic/java/JavaInstallList.cpp deleted file mode 100644 index c0729227..00000000 --- a/libraries/logic/java/JavaInstallList.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include -#include - -#include - -#include "java/JavaInstallList.h" -#include "java/JavaCheckerJob.h" -#include "java/JavaUtils.h" -#include "MMCStrings.h" -#include "minecraft/VersionFilterData.h" - -JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent) -{ -} - -Task *JavaInstallList::getLoadTask() -{ - return new JavaListLoadTask(this); -} - -const BaseVersionPtr JavaInstallList::at(int i) const -{ - return m_vlist.at(i); -} - -bool JavaInstallList::isLoaded() -{ - return m_loaded; -} - -int JavaInstallList::count() const -{ - return m_vlist.count(); -} - -QVariant JavaInstallList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(m_vlist[index.row()]); - case VersionIdRole: - return version->descriptor(); - case VersionRole: - return version->id.toString(); - case RecommendedRole: - return version->recommended; - case PathRole: - return version->path; - case ArchitectureRole: - return version->arch; - default: - return QVariant(); - } -} - -BaseVersionList::RoleList JavaInstallList::providesRoles() const -{ - return {VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, ArchitectureRole}; -} - - -void JavaInstallList::updateListData(QList versions) -{ - beginResetModel(); - m_vlist = versions; - m_loaded = true; - sortVersions(); - if(m_vlist.size()) - { - auto best = std::dynamic_pointer_cast(m_vlist[0]); - best->recommended = true; - } - endResetModel(); -} - -bool sortJavas(BaseVersionPtr left, BaseVersionPtr right) -{ - auto rleft = std::dynamic_pointer_cast(left); - auto rright = std::dynamic_pointer_cast(right); - return (*rleft) > (*rright); -} - -void JavaInstallList::sortVersions() -{ - beginResetModel(); - std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); - endResetModel(); -} - -JavaListLoadTask::JavaListLoadTask(JavaInstallList *vlist) : Task() -{ - m_list = vlist; - m_currentRecommended = NULL; -} - -JavaListLoadTask::~JavaListLoadTask() -{ -} - -void JavaListLoadTask::executeTask() -{ - setStatus(tr("Detecting Java installations...")); - - JavaUtils ju; - QList candidate_paths = ju.FindJavaPaths(); - - m_job = std::shared_ptr(new JavaCheckerJob("Java detection")); - connect(m_job.get(), SIGNAL(finished(QList)), this, SLOT(javaCheckerFinished(QList))); - connect(m_job.get(), &Task::progress, this, &Task::setProgress); - - qDebug() << "Probing the following Java paths: "; - int id = 0; - for(QString candidate : candidate_paths) - { - qDebug() << " " << candidate; - - auto candidate_checker = new JavaChecker(); - candidate_checker->m_path = candidate; - candidate_checker->m_id = id; - m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); - - id++; - } - - m_job->start(); -} - -void JavaListLoadTask::javaCheckerFinished(QList results) -{ - QList candidates; - - qDebug() << "Found the following valid Java installations:"; - for(JavaCheckResult result : results) - { - if(result.valid) - { - JavaInstallPtr javaVersion(new JavaInstall()); - - javaVersion->id = result.javaVersion; - javaVersion->arch = result.mojangPlatform; - javaVersion->path = result.path; - candidates.append(javaVersion); - - qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path; - } - } - - QList javas_bvp; - for (auto java : candidates) - { - //qDebug() << java->id << java->arch << " at " << java->path; - BaseVersionPtr bp_java = std::dynamic_pointer_cast(java); - - if (bp_java) - { - javas_bvp.append(java); - } - } - - m_list->updateListData(javas_bvp); - emitSucceeded(); -} diff --git a/libraries/logic/java/JavaInstallList.h b/libraries/logic/java/JavaInstallList.h deleted file mode 100644 index cf0e5784..00000000 --- a/libraries/logic/java/JavaInstallList.h +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include - -#include "BaseVersionList.h" -#include "tasks/Task.h" - -#include "JavaCheckerJob.h" -#include "JavaInstall.h" - -#include "multimc_logic_export.h" - -class JavaListLoadTask; - -class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList -{ - Q_OBJECT -public: - explicit JavaInstallList(QObject *parent = 0); - - virtual Task *getLoadTask() override; - virtual bool isLoaded() override; - virtual const BaseVersionPtr at(int i) const override; - virtual int count() const override; - virtual void sortVersions() override; - - virtual QVariant data(const QModelIndex &index, int role) const override; - virtual RoleList providesRoles() const override; - -public slots: - virtual void updateListData(QList versions) override; - -protected: - QList m_vlist; - - bool m_loaded = false; -}; - -class JavaListLoadTask : public Task -{ - Q_OBJECT - -public: - explicit JavaListLoadTask(JavaInstallList *vlist); - ~JavaListLoadTask(); - - virtual void executeTask(); -public slots: - void javaCheckerFinished(QList results); - -protected: - std::shared_ptr m_job; - JavaInstallList *m_list; - JavaInstall *m_currentRecommended; -}; diff --git a/libraries/logic/java/JavaUtils.cpp b/libraries/logic/java/JavaUtils.cpp deleted file mode 100644 index 88996e9f..00000000 --- a/libraries/logic/java/JavaUtils.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include -#include -#include - -#include - -#include -#include "java/JavaUtils.h" -#include "java/JavaCheckerJob.h" -#include "java/JavaInstallList.h" -#include "FileSystem.h" - -JavaUtils::JavaUtils() -{ -} - -JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch) -{ - JavaInstallPtr javaVersion(new JavaInstall()); - - javaVersion->id = id; - javaVersion->arch = arch; - javaVersion->path = path; - - return javaVersion; -} - -JavaInstallPtr JavaUtils::GetDefaultJava() -{ - JavaInstallPtr javaVersion(new JavaInstall()); - - javaVersion->id = "java"; - javaVersion->arch = "unknown"; - javaVersion->path = "java"; - - return javaVersion; -} - -#if defined(Q_OS_WIN32) -QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName) -{ - QList javas; - - QString archType = "unknown"; - if (keyType == KEY_WOW64_64KEY) - archType = "64"; - else if (keyType == KEY_WOW64_32KEY) - archType = "32"; - - HKEY jreKey; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0, - KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) - { - // Read the current type version from the registry. - // This will be used to find any key that contains the JavaHome value. - char *value = new char[0]; - DWORD valueSz = 0; - if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz) == - ERROR_MORE_DATA) - { - value = new char[valueSz]; - RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE *)value, &valueSz); - } - - QString recommended = value; - - TCHAR subKeyName[255]; - DWORD subKeyNameSize, numSubKeys, retCode; - - // Get the number of subkeys - RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, - NULL, NULL); - - // Iterate until RegEnumKeyEx fails - if (numSubKeys > 0) - { - for (int i = 0; i < numSubKeys; i++) - { - subKeyNameSize = 255; - retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, - NULL); - if (retCode == ERROR_SUCCESS) - { - // Now open the registry key for the version that we just got. - QString newKeyName = keyName + "\\" + subKeyName; - - HKEY newKey; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0, - KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) - { - // Read the JavaHome value to find where Java is installed. - value = new char[0]; - valueSz = 0; - if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value, - &valueSz) == ERROR_MORE_DATA) - { - value = new char[valueSz]; - RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE *)value, - &valueSz); - - // Now, we construct the version object and add it to the list. - JavaInstallPtr javaVersion(new JavaInstall()); - - javaVersion->id = subKeyName; - javaVersion->arch = archType; - javaVersion->path = - QDir(FS::PathCombine(value, "bin")).absoluteFilePath("javaw.exe"); - javas.append(javaVersion); - } - - RegCloseKey(newKey); - } - } - } - } - - RegCloseKey(jreKey); - } - - return javas; -} - -QList JavaUtils::FindJavaPaths() -{ - QList java_candidates; - - QList JRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); - QList JDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); - QList JRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); - QList JDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); - - java_candidates.append(JRE64s); - java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); - java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); - java_candidates.append(JDK64s); - java_candidates.append(JRE32s); - java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); - java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); - java_candidates.append(JDK32s); - java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); - - QList candidates; - for(JavaInstallPtr java_candidate : java_candidates) - { - if(!candidates.contains(java_candidate->path)) - { - candidates.append(java_candidate->path); - } - } - - return candidates; -} - -#elif defined(Q_OS_MAC) -QList JavaUtils::FindJavaPaths() -{ - QList javas; - javas.append(this->GetDefaultJava()->path); - javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); - javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); - javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); - QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); - QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &java, libraryJVMJavas) { - javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); - javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); - } - QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); - QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &java, systemLibraryJVMJavas) { - javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); - javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); - } - return javas; -} - -#elif defined(Q_OS_LINUX) -QList JavaUtils::FindJavaPaths() -{ - qDebug() << "Linux Java detection incomplete - defaulting to \"java\""; - - QList javas; - javas.append(this->GetDefaultJava()->path); - javas.append("/opt/java/bin/java"); - javas.append("/usr/bin/java"); - - return javas; -} -#else -QList JavaUtils::FindJavaPaths() -{ - qDebug() << "Unknown operating system build - defaulting to \"java\""; - - QList javas; - javas.append(this->GetDefaultJava()->path); - - return javas; -} -#endif diff --git a/libraries/logic/java/JavaUtils.h b/libraries/logic/java/JavaUtils.h deleted file mode 100644 index 3fb88341..00000000 --- a/libraries/logic/java/JavaUtils.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include - -#include "JavaCheckerJob.h" -#include "JavaChecker.h" -#include "JavaInstallList.h" - -#ifdef Q_OS_WIN -#include -#endif - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT JavaUtils : public QObject -{ - Q_OBJECT -public: - JavaUtils(); - - JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); - QList FindJavaPaths(); - JavaInstallPtr GetDefaultJava(); - -#ifdef Q_OS_WIN - QList FindJavaFromRegistryKey(DWORD keyType, QString keyName); -#endif -}; diff --git a/libraries/logic/java/JavaVersion.cpp b/libraries/logic/java/JavaVersion.cpp deleted file mode 100644 index 84fc48a4..00000000 --- a/libraries/logic/java/JavaVersion.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "JavaVersion.h" -#include - -#include -#include - -JavaVersion & JavaVersion::operator=(const QString & javaVersionString) -{ - string = javaVersionString; - - auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int - { - auto str = match.captured(what); - if(str.isEmpty()) - { - return 0; - } - return str.toInt(); - }; - - QRegularExpression pattern; - if(javaVersionString.startsWith("1.")) - { - pattern = QRegularExpression ("1[.](?[0-9]+)([.](?[0-9]+))?(_(?[0-9]+)?)?(-(?[a-zA-Z0-9]+))?"); - } - else - { - pattern = QRegularExpression("(?[0-9]+)([.](?[0-9]+))?([.](?[0-9]+))?(-(?[a-zA-Z0-9]+))?"); - } - - auto match = pattern.match(string); - parseable = match.hasMatch(); - major = getCapturedInteger(match, "major"); - minor = getCapturedInteger(match, "minor"); - security = getCapturedInteger(match, "security"); - prerelease = match.captured("prerelease"); - return *this; -} - -JavaVersion::JavaVersion(const QString &rhs) -{ - operator=(rhs); -} - -QString JavaVersion::toString() -{ - return string; -} - -bool JavaVersion::requiresPermGen() -{ - if(parseable) - { - return major < 8; - } - return true; -} - -bool JavaVersion::operator<(const JavaVersion &rhs) -{ - if(parseable && rhs.parseable) - { - if(major < rhs.major) - return true; - if(major > rhs.major) - return false; - if(minor < rhs.minor) - return true; - if(minor > rhs.minor) - return false; - if(security < rhs.security) - return true; - if(security > rhs.security) - return false; - - // everything else being equal, consider prerelease status - bool thisPre = !prerelease.isEmpty(); - bool rhsPre = !rhs.prerelease.isEmpty(); - if(thisPre && !rhsPre) - { - // this is a prerelease and the other one isn't -> lesser - return true; - } - else if(!thisPre && rhsPre) - { - // this isn't a prerelease and the other one is -> greater - return false; - } - else if(thisPre && rhsPre) - { - // both are prereleases - use natural compare... - return Strings::naturalCompare(prerelease, rhs.prerelease, Qt::CaseSensitive) < 0; - } - // neither is prerelease, so they are the same -> this cannot be less than rhs - return false; - } - else return Strings::naturalCompare(string, rhs.string, Qt::CaseSensitive) < 0; -} - -bool JavaVersion::operator==(const JavaVersion &rhs) -{ - if(parseable && rhs.parseable) - { - return major == rhs.major && minor == rhs.minor && security == rhs.security && prerelease == rhs.prerelease; - } - return string == rhs.string; -} - -bool JavaVersion::operator>(const JavaVersion &rhs) -{ - return (!operator<(rhs)) && (!operator==(rhs)); -} diff --git a/libraries/logic/java/JavaVersion.h b/libraries/logic/java/JavaVersion.h deleted file mode 100644 index f9a733d3..00000000 --- a/libraries/logic/java/JavaVersion.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "multimc_logic_export.h" -#include - -class MULTIMC_LOGIC_EXPORT JavaVersion -{ - friend class JavaVersionTest; -public: - JavaVersion() {}; - JavaVersion(const QString & rhs); - - JavaVersion & operator=(const QString & rhs); - - bool operator<(const JavaVersion & rhs); - bool operator==(const JavaVersion & rhs); - bool operator>(const JavaVersion & rhs); - - bool requiresPermGen(); - - QString toString(); - -private: - QString string; - int major = 0; - int minor = 0; - int security = 0; - bool parseable = false; - QString prerelease; -}; diff --git a/libraries/logic/launch/LaunchStep.cpp b/libraries/logic/launch/LaunchStep.cpp deleted file mode 100644 index 3078043b..00000000 --- a/libraries/logic/launch/LaunchStep.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "LaunchStep.h" -#include "LaunchTask.h" - -void LaunchStep::bind(LaunchTask *parent) -{ - m_parent = parent; - connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch); - connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine); - connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines); - connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished); - connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested); -} diff --git a/libraries/logic/launch/LaunchStep.h b/libraries/logic/launch/LaunchStep.h deleted file mode 100644 index ea472c0d..00000000 --- a/libraries/logic/launch/LaunchStep.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include "tasks/Task.h" -#include "MessageLevel.h" - -#include - -class LaunchTask; -class LaunchStep: public Task -{ - Q_OBJECT -public: /* methods */ - explicit LaunchStep(LaunchTask *parent):Task(nullptr), m_parent(parent) - { - bind(parent); - }; - virtual ~LaunchStep() {}; - -protected: /* methods */ - virtual void bind(LaunchTask *parent); - -signals: - void logLines(QStringList lines, MessageLevel::Enum level); - void logLine(QString line, MessageLevel::Enum level); - void readyForLaunch(); - void progressReportingRequest(); - -public slots: - virtual void proceed() {}; - -protected: /* data */ - LaunchTask *m_parent; -}; \ No newline at end of file diff --git a/libraries/logic/launch/LaunchTask.cpp b/libraries/logic/launch/LaunchTask.cpp deleted file mode 100644 index 5b7ff182..00000000 --- a/libraries/logic/launch/LaunchTask.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Authors: Orochimarufan - * - * 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. - */ - -#include "launch/LaunchTask.h" -#include "MessageLevel.h" -#include "MMCStrings.h" -#include "java/JavaChecker.h" -#include "tasks/Task.h" -#include -#include -#include -#include -#include -#include -#include - -void LaunchTask::init() -{ - m_instance->setRunning(true); -} - -std::shared_ptr LaunchTask::create(InstancePtr inst) -{ - std::shared_ptr proc(new LaunchTask(inst)); - proc->init(); - return proc; -} - -LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance) -{ -} - -void LaunchTask::appendStep(std::shared_ptr step) -{ - m_steps.append(step); -} - -void LaunchTask::prependStep(std::shared_ptr step) -{ - m_steps.prepend(step); -} - -void LaunchTask::executeTask() -{ - if(!m_steps.size()) - { - state = LaunchTask::Finished; - emitSucceeded(); - } - state = LaunchTask::Running; - onStepFinished(); -} - -void LaunchTask::onReadyForLaunch() -{ - state = LaunchTask::Waiting; - emit readyForLaunch(); -} - -void LaunchTask::onStepFinished() -{ - // initial -> just start the first step - if(currentStep == -1) - { - currentStep ++; - m_steps[currentStep]->start(); - return; - } - - auto step = m_steps[currentStep]; - if(step->successful()) - { - // end? - if(currentStep == m_steps.size() - 1) - { - emitSucceeded(); - } - else - { - currentStep ++; - step = m_steps[currentStep]; - step->start(); - } - } - else - { - emitFailed(step->failReason()); - } -} - -void LaunchTask::onProgressReportingRequested() -{ - state = LaunchTask::Waiting; - emit requestProgress(m_steps[currentStep].get()); -} - -void LaunchTask::setCensorFilter(QMap filter) -{ - m_censorFilter = filter; -} - -QString LaunchTask::censorPrivateInfo(QString in) -{ - auto iter = m_censorFilter.begin(); - while (iter != m_censorFilter.end()) - { - in.replace(iter.key(), iter.value()); - iter++; - } - return in; -} - -void LaunchTask::proceed() -{ - if(state != LaunchTask::Waiting) - { - return; - } - m_steps[currentStep]->proceed(); -} - -bool LaunchTask::abort() -{ - switch(state) - { - case LaunchTask::Aborted: - case LaunchTask::Failed: - case LaunchTask::Finished: - return true; - case LaunchTask::NotStarted: - { - state = LaunchTask::Aborted; - emitFailed("Aborted"); - return true; - } - case LaunchTask::Running: - case LaunchTask::Waiting: - { - auto step = m_steps[currentStep]; - if(!step->canAbort()) - { - return false; - } - if(step->abort()) - { - state = LaunchTask::Aborted; - return true; - } - } - default: - break; - } - return false; -} - -void LaunchTask::onLogLines(const QStringList &lines, MessageLevel::Enum defaultLevel) -{ - for (auto & line: lines) - { - onLogLine(line, defaultLevel); - } -} - -void LaunchTask::onLogLine(QString line, MessageLevel::Enum level) -{ - // if the launcher part set a log level, use it - auto innerLevel = MessageLevel::fromLine(line); - if(innerLevel != MessageLevel::Unknown) - { - level = innerLevel; - } - - // If the level is still undetermined, guess level - if (level == MessageLevel::StdErr || level == MessageLevel::StdOut || level == MessageLevel::Unknown) - { - level = m_instance->guessLevel(line, level); - } - - // censor private user info - line = censorPrivateInfo(line); - - emit log(line, level); -} - -void LaunchTask::emitSucceeded() -{ - m_instance->cleanupAfterRun(); - m_instance->setRunning(false); - Task::emitSucceeded(); -} - -void LaunchTask::emitFailed(QString reason) -{ - m_instance->cleanupAfterRun(); - m_instance->setRunning(false); - Task::emitFailed(reason); -} - -QString LaunchTask::substituteVariables(const QString &cmd) const -{ - QString out = cmd; - auto variables = m_instance->getVariables(); - for (auto it = variables.begin(); it != variables.end(); ++it) - { - out.replace("$" + it.key(), it.value()); - } - auto env = QProcessEnvironment::systemEnvironment(); - for (auto var : env.keys()) - { - out.replace("$" + var, env.value(var)); - } - return out; -} - diff --git a/libraries/logic/launch/LaunchTask.h b/libraries/logic/launch/LaunchTask.h deleted file mode 100644 index 447445ca..00000000 --- a/libraries/logic/launch/LaunchTask.h +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Authors: Orochimarufan - * - * 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. - */ - -#pragma once -#include -#include "BaseInstance.h" -#include "MessageLevel.h" -#include "LoggedProcess.h" -#include "LaunchStep.h" - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT LaunchTask: public Task -{ - Q_OBJECT -protected: - explicit LaunchTask(InstancePtr instance); - void init(); - -public: - enum State - { - NotStarted, - Running, - Waiting, - Failed, - Aborted, - Finished - }; - -public: /* methods */ - static std::shared_ptr create(InstancePtr inst); - virtual ~LaunchTask() {}; - - void appendStep(std::shared_ptr step); - void prependStep(std::shared_ptr step); - void setCensorFilter(QMap filter); - - InstancePtr instance() - { - return m_instance; - } - - void setPid(qint64 pid) - { - m_pid = pid; - } - - qint64 pid() - { - return m_pid; - } - - /** - * @brief prepare the process for launch (for multi-stage launch) - */ - virtual void executeTask() override; - - /** - * @brief launch the armed instance - */ - void proceed(); - - /** - * @brief abort launch - */ - virtual bool abort() override; - -public: - QString substituteVariables(const QString &cmd) const; - QString censorPrivateInfo(QString in); - -protected: /* methods */ - virtual void emitFailed(QString reason) override; - virtual void emitSucceeded() override; - -signals: - /** - * @brief emitted when the launch preparations are done - */ - void readyForLaunch(); - - void requestProgress(Task *task); - - void requestLogging(); - - /** - * @brief emitted when we want to log something - * @param text the text to log - * @param level the level to log at - */ - void log(QString text, MessageLevel::Enum level = MessageLevel::MultiMC); - -public slots: - void onLogLines(const QStringList& lines, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC); - void onLogLine(QString line, MessageLevel::Enum defaultLevel = MessageLevel::MultiMC); - void onReadyForLaunch(); - void onStepFinished(); - void onProgressReportingRequested(); - -protected: /* data */ - InstancePtr m_instance; - QList > m_steps; - QMap m_censorFilter; - int currentStep = -1; - State state = NotStarted; - qint64 m_pid = -1; -}; diff --git a/libraries/logic/launch/LoggedProcess.cpp b/libraries/logic/launch/LoggedProcess.cpp deleted file mode 100644 index 88ca40aa..00000000 --- a/libraries/logic/launch/LoggedProcess.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "LoggedProcess.h" -#include "MessageLevel.h" -#include - -LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent) -{ - // QProcess has a strange interface... let's map a lot of those into a few. - connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); - connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); - connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus))); - connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError))); - connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); -} - -QStringList reprocess(const QByteArray & data, QString & leftover) -{ - QString str = leftover + QString::fromLocal8Bit(data); - - str.remove('\r'); - QStringList lines = str.split("\n"); - leftover = lines.takeLast(); - return lines; -} - -void LoggedProcess::on_stdErr() -{ - auto lines = reprocess(readAllStandardError(), m_err_leftover); - emit log(lines, MessageLevel::StdErr); -} - -void LoggedProcess::on_stdOut() -{ - auto lines = reprocess(readAllStandardOutput(), m_out_leftover); - emit log(lines, MessageLevel::StdOut); -} - -void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status) -{ - // save the exit code - m_exit_code = exit_code; - - // Flush console window - if (!m_err_leftover.isEmpty()) - { - emit log({m_err_leftover}, MessageLevel::StdErr); - m_err_leftover.clear(); - } - if (!m_out_leftover.isEmpty()) - { - emit log({m_err_leftover}, MessageLevel::StdOut); - m_out_leftover.clear(); - } - - // based on state, send signals - if (!m_is_aborting) - { - if (status == QProcess::NormalExit) - { - //: Message displayed on instance exit - emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::MultiMC); - changeState(LoggedProcess::Finished); - } - else - { - //: Message displayed on instance crashed - if(exit_code == -1) - emit log({tr("Process crashed.")}, MessageLevel::MultiMC); - else - emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::MultiMC); - changeState(LoggedProcess::Crashed); - } - } - else - { - //: Message displayed after the instance exits due to kill request - emit log({tr("Process was killed by user.")}, MessageLevel::Error); - changeState(LoggedProcess::Aborted); - } -} - -void LoggedProcess::on_error(QProcess::ProcessError error) -{ - switch(error) - { - case QProcess::FailedToStart: - { - emit log({tr("The process failed to start.")}, MessageLevel::Fatal); - changeState(LoggedProcess::FailedToStart); - break; - } - // we'll just ignore those... never needed them - case QProcess::Crashed: - case QProcess::ReadError: - case QProcess::Timedout: - case QProcess::UnknownError: - case QProcess::WriteError: - break; - } -} - -void LoggedProcess::kill() -{ - m_is_aborting = true; - QProcess::kill(); -} - -int LoggedProcess::exitCode() const -{ - return m_exit_code; -} - -void LoggedProcess::changeState(LoggedProcess::State state) -{ - if(state == m_state) - return; - m_state = state; - emit stateChanged(m_state); -} - -LoggedProcess::State LoggedProcess::state() const -{ - return m_state; -} - -void LoggedProcess::on_stateChange(QProcess::ProcessState state) -{ - switch(state) - { - case QProcess::NotRunning: - break; // let's not - there are too many that handle this already. - case QProcess::Starting: - { - if(m_state != LoggedProcess::NotRunning) - { - qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting; - } - changeState(LoggedProcess::Starting); - return; - } - case QProcess::Running: - { - if(m_state != LoggedProcess::Starting) - { - qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running; - } - changeState(LoggedProcess::Running); - return; - } - } -} - -#if defined Q_OS_WIN32 -#include -#endif - -qint64 LoggedProcess::processId() const -{ -#ifdef Q_OS_WIN - return pid() ? pid()->dwProcessId : 0; -#else - return pid(); -#endif -} diff --git a/libraries/logic/launch/LoggedProcess.h b/libraries/logic/launch/LoggedProcess.h deleted file mode 100644 index baa53d79..00000000 --- a/libraries/logic/launch/LoggedProcess.h +++ /dev/null @@ -1,76 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include "MessageLevel.h" - -/* - * This is a basic process. - * It has line-based logging support and hides some of the nasty bits. - */ -class LoggedProcess : public QProcess -{ -Q_OBJECT -public: - enum State - { - NotRunning, - Starting, - FailedToStart, - Running, - Finished, - Crashed, - Aborted - }; - -public: - explicit LoggedProcess(QObject* parent = 0); - virtual ~LoggedProcess() {}; - - State state() const; - int exitCode() const; - qint64 processId() const; - -signals: - void log(QStringList lines, MessageLevel::Enum level); - void stateChanged(LoggedProcess::State state); - -public slots: - /** - * @brief kill the process - equivalent to kill -9 - */ - void kill(); - - -private slots: - void on_stdErr(); - void on_stdOut(); - void on_exit(int exit_code, QProcess::ExitStatus status); - void on_error(QProcess::ProcessError error); - void on_stateChange(QProcess::ProcessState); - -private: - void changeState(LoggedProcess::State state); - -private: - QString m_err_leftover; - QString m_out_leftover; - bool m_killed = false; - State m_state = NotRunning; - int m_exit_code = 0; - bool m_is_aborting = false; -}; diff --git a/libraries/logic/launch/MessageLevel.cpp b/libraries/logic/launch/MessageLevel.cpp deleted file mode 100644 index a5191290..00000000 --- a/libraries/logic/launch/MessageLevel.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "MessageLevel.h" - -MessageLevel::Enum MessageLevel::getLevel(const QString& levelName) -{ - if (levelName == "MultiMC") - return MessageLevel::MultiMC; - else if (levelName == "Debug") - return MessageLevel::Debug; - else if (levelName == "Info") - return MessageLevel::Info; - else if (levelName == "Message") - return MessageLevel::Message; - else if (levelName == "Warning") - return MessageLevel::Warning; - else if (levelName == "Error") - return MessageLevel::Error; - else if (levelName == "Fatal") - return MessageLevel::Fatal; - // Skip PrePost, it's not exposed to !![]! - // Also skip StdErr and StdOut - else - return MessageLevel::Unknown; -} - -MessageLevel::Enum MessageLevel::fromLine(QString &line) -{ - // Level prefix - int endmark = line.indexOf("]!"); - if (line.startsWith("!![") && endmark != -1) - { - auto level = MessageLevel::getLevel(line.left(endmark).mid(3)); - line = line.mid(endmark + 2); - return level; - } - return MessageLevel::Unknown; -} diff --git a/libraries/logic/launch/MessageLevel.h b/libraries/logic/launch/MessageLevel.h deleted file mode 100644 index 0128148d..00000000 --- a/libraries/logic/launch/MessageLevel.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -/** - * @brief the MessageLevel Enum - * defines what level a log message is - */ -namespace MessageLevel -{ -enum Enum -{ - Unknown, /**< No idea what this is or where it came from */ - StdOut, /**< Undetermined stderr messages */ - StdErr, /**< Undetermined stdout messages */ - MultiMC, /**< MultiMC Messages */ - Debug, /**< Debug Messages */ - Info, /**< Info Messages */ - Message, /**< Standard Messages */ - Warning, /**< Warnings */ - Error, /**< Errors */ - Fatal, /**< Fatal Errors */ -}; -MessageLevel::Enum getLevel(const QString &levelName); - -/* Get message level from a line. Line is modified if it was successful. */ -MessageLevel::Enum fromLine(QString &line); -} diff --git a/libraries/logic/launch/steps/CheckJava.cpp b/libraries/logic/launch/steps/CheckJava.cpp deleted file mode 100644 index a4eaa307..00000000 --- a/libraries/logic/launch/steps/CheckJava.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "CheckJava.h" -#include -#include -#include -#include - -void CheckJava::executeTask() -{ - auto instance = m_parent->instance(); - auto settings = instance->settings(); - m_javaPath = FS::ResolveExecutable(settings->get("JavaPath").toString()); - bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool(); - - auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); - if (realJavaPath.isEmpty()) - { - if (perInstance) - { - emit logLine( - tr("The java binary \"%1\" couldn't be found. Please fix the java path " - "override in the instance's settings or disable it.").arg(m_javaPath), - MessageLevel::Warning); - } - else - { - emit logLine(tr("The java binary \"%1\" couldn't be found. Please set up java in " - "the settings.").arg(m_javaPath), - MessageLevel::Warning); - } - emitFailed(tr("Java path is not valid.")); - return; - } - else - { - emit logLine("Java path is:\n" + m_javaPath + "\n\n", MessageLevel::MultiMC); - } - - QFileInfo javaInfo(realJavaPath); - qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); - auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); - m_javaUnixTime = javaUnixTime; - // if they are not the same, check! - if (javaUnixTime != storedUnixTime) - { - m_JavaChecker = std::make_shared(); - QString errorLog; - QString version; - emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC); - connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, - &CheckJava::checkJavaFinished); - m_JavaChecker->m_path = realJavaPath; - m_JavaChecker->performCheck(); - return; - } - emitSucceeded(); -} - -void CheckJava::checkJavaFinished(JavaCheckResult result) -{ - if (!result.valid) - { - // Error message displayed if java can't start - emit logLine(tr("Could not start java:"), MessageLevel::Error); - emit logLines(result.errorLog.split('\n'), MessageLevel::Error); - emit logLine("\nCheck your MultiMC Java settings.", MessageLevel::MultiMC); - emitFailed(tr("Could not start java!")); - } - else - { - auto instance = m_parent->instance(); - emit logLine(tr("Java version is %1!\n").arg(result.javaVersion.toString()), - MessageLevel::MultiMC); - instance->settings()->set("JavaVersion", result.javaVersion.toString()); - instance->settings()->set("JavaTimestamp", m_javaUnixTime); - emitSucceeded(); - } -} diff --git a/libraries/logic/launch/steps/CheckJava.h b/libraries/logic/launch/steps/CheckJava.h deleted file mode 100644 index b63dd4f4..00000000 --- a/libraries/logic/launch/steps/CheckJava.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -class CheckJava: public LaunchStep -{ - Q_OBJECT -public: - explicit CheckJava(LaunchTask *parent) :LaunchStep(parent){}; - virtual ~CheckJava() {}; - - virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } -private slots: - void checkJavaFinished(JavaCheckResult result); - -private: - QString m_javaPath; - qlonglong m_javaUnixTime; - JavaCheckerPtr m_JavaChecker; -}; diff --git a/libraries/logic/launch/steps/LaunchMinecraft.cpp b/libraries/logic/launch/steps/LaunchMinecraft.cpp deleted file mode 100644 index 77a89f17..00000000 --- a/libraries/logic/launch/steps/LaunchMinecraft.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "LaunchMinecraft.h" -#include -#include -#include -#include - -LaunchMinecraft::LaunchMinecraft(LaunchTask *parent) : LaunchStep(parent) -{ - connect(&m_process, &LoggedProcess::log, this, &LaunchMinecraft::logLines); - connect(&m_process, &LoggedProcess::stateChanged, this, &LaunchMinecraft::on_state); -} - -void LaunchMinecraft::executeTask() -{ - auto instance = m_parent->instance(); - std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); - - m_launchScript = minecraftInstance->createLaunchScript(m_session); - - QStringList args = minecraftInstance->javaArguments(); - - // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' is created. - if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->minecraftRoot(), "server-resource-packs"))) - { - emit logLine(tr("Couldn't create the 'server-resource-packs' folder"), MessageLevel::Error); - } - - QString allArgs = args.join(", "); - emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC); - - auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString()); - - m_process.setProcessEnvironment(instance->createEnvironment()); - - QString wrapperCommand = instance->getWrapperCommand(); - if(!wrapperCommand.isEmpty()) - { - auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand); - if (realWrapperCommand.isEmpty()) - { - QString reason = tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(reason); - return; - } - emit logLine("Wrapper command is:\n" + wrapperCommand + "\n\n", MessageLevel::MultiMC); - args.prepend(javaPath); - m_process.start(wrapperCommand, args); - } - else - { - m_process.start(javaPath, args); - } -} - -void LaunchMinecraft::on_state(LoggedProcess::State state) -{ - switch(state) - { - case LoggedProcess::FailedToStart: - { - //: Error message displayed if instace can't start - QString reason = tr("Could not launch minecraft!"); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(reason); - return; - } - case LoggedProcess::Aborted: - case LoggedProcess::Crashed: - - { - m_parent->setPid(-1); - emitFailed("Game crashed."); - return; - } - case LoggedProcess::Finished: - { - m_parent->setPid(-1); - // if the exit code wasn't 0, report this as a crash - auto exitCode = m_process.exitCode(); - if(exitCode != 0) - { - emitFailed("Game crashed."); - return; - } - //FIXME: make this work again - // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); - // run post-exit - emitSucceeded(); - break; - } - case LoggedProcess::Running: - emit logLine(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC); - m_parent->setPid(m_process.processId()); - m_parent->instance()->setLastLaunch(); - // send the launch script to the launcher part - m_process.write(m_launchScript.toUtf8()); - - mayProceed = true; - emit readyForLaunch(); - break; - default: - break; - } -} - -void LaunchMinecraft::setWorkingDirectory(const QString &wd) -{ - m_process.setWorkingDirectory(wd); -} - -void LaunchMinecraft::proceed() -{ - if(mayProceed) - { - QString launchString("launch\n"); - m_process.write(launchString.toUtf8()); - mayProceed = false; - } -} - -bool LaunchMinecraft::abort() -{ - if(mayProceed) - { - mayProceed = false; - QString launchString("abort\n"); - m_process.write(launchString.toUtf8()); - } - else - { - auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { - m_process.kill(); - } - } - return true; -} diff --git a/libraries/logic/launch/steps/LaunchMinecraft.h b/libraries/logic/launch/steps/LaunchMinecraft.h deleted file mode 100644 index 6b9f7919..00000000 --- a/libraries/logic/launch/steps/LaunchMinecraft.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -class LaunchMinecraft: public LaunchStep -{ - Q_OBJECT -public: - explicit LaunchMinecraft(LaunchTask *parent); - virtual void executeTask(); - virtual bool abort(); - virtual void proceed(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); - void setAuthSession(AuthSessionPtr session) - { - m_session = session; - } -private slots: - void on_state(LoggedProcess::State state); - -private: - LoggedProcess m_process; - QString m_command; - QString m_launchScript; - AuthSessionPtr m_session; - bool mayProceed = false; -}; diff --git a/libraries/logic/launch/steps/ModMinecraftJar.cpp b/libraries/logic/launch/steps/ModMinecraftJar.cpp deleted file mode 100644 index fce2d70a..00000000 --- a/libraries/logic/launch/steps/ModMinecraftJar.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "ModMinecraftJar.h" -#include -#include - -void ModMinecraftJar::executeTask() -{ - m_jarModTask = m_parent->instance()->createJarModdingTask(); - if(m_jarModTask) - { - connect(m_jarModTask.get(), SIGNAL(finished()), this, SLOT(jarModdingFinished())); - m_jarModTask->start(); - return; - } - emitSucceeded(); -} - -void ModMinecraftJar::jarModdingFinished() -{ - if(m_jarModTask->successful()) - { - emitSucceeded(); - } - else - { - QString reason = tr("jar modding failed because: %1.\n\n").arg(m_jarModTask->failReason()); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(reason); - } -} diff --git a/libraries/logic/launch/steps/ModMinecraftJar.h b/libraries/logic/launch/steps/ModMinecraftJar.h deleted file mode 100644 index b35dfafa..00000000 --- a/libraries/logic/launch/steps/ModMinecraftJar.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include - -// FIXME: temporary wrapper for existing task. -class ModMinecraftJar: public LaunchStep -{ - Q_OBJECT -public: - explicit ModMinecraftJar(LaunchTask *parent) : LaunchStep(parent) {}; - virtual ~ModMinecraftJar(){}; - - virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } -private slots: - void jarModdingFinished(); - -private: - std::shared_ptr m_jarModTask; -}; diff --git a/libraries/logic/launch/steps/PostLaunchCommand.cpp b/libraries/logic/launch/steps/PostLaunchCommand.cpp deleted file mode 100644 index 29a45f1b..00000000 --- a/libraries/logic/launch/steps/PostLaunchCommand.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "PostLaunchCommand.h" -#include - -PostLaunchCommand::PostLaunchCommand(LaunchTask *parent) : LaunchStep(parent) -{ - auto instance = m_parent->instance(); - m_command = instance->getPostExitCommand(); - m_process.setProcessEnvironment(instance->createEnvironment()); - connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines); - connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state); -} - -void PostLaunchCommand::executeTask() -{ - QString postlaunch_cmd = m_parent->substituteVariables(m_command); - emit logLine(tr("Running Post-Launch command: %1").arg(postlaunch_cmd), MessageLevel::MultiMC); - m_process.start(postlaunch_cmd); -} - -void PostLaunchCommand::on_state(LoggedProcess::State state) -{ - auto getError = [&]() - { - return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); - }; - switch(state) - { - case LoggedProcess::Aborted: - case LoggedProcess::Crashed: - case LoggedProcess::FailedToStart: - { - auto error = getError(); - emit logLine(error, MessageLevel::Fatal); - emitFailed(error); - return; - } - case LoggedProcess::Finished: - { - if(m_process.exitCode() != 0) - { - auto error = getError(); - emit logLine(error, MessageLevel::Fatal); - emitFailed(error); - } - else - { - emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::MultiMC); - emitSucceeded(); - } - } - default: - break; - } -} - -void PostLaunchCommand::setWorkingDirectory(const QString &wd) -{ - m_process.setWorkingDirectory(wd); -} - -bool PostLaunchCommand::abort() -{ - auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { - m_process.kill(); - } - return true; -} diff --git a/libraries/logic/launch/steps/PostLaunchCommand.h b/libraries/logic/launch/steps/PostLaunchCommand.h deleted file mode 100644 index 4d5b0a52..00000000 --- a/libraries/logic/launch/steps/PostLaunchCommand.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include - -class PostLaunchCommand: public LaunchStep -{ - Q_OBJECT -public: - explicit PostLaunchCommand(LaunchTask *parent); - virtual void executeTask(); - virtual bool abort(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); -private slots: - void on_state(LoggedProcess::State state); - -private: - LoggedProcess m_process; - QString m_command; -}; diff --git a/libraries/logic/launch/steps/PreLaunchCommand.cpp b/libraries/logic/launch/steps/PreLaunchCommand.cpp deleted file mode 100644 index 47197a82..00000000 --- a/libraries/logic/launch/steps/PreLaunchCommand.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "PreLaunchCommand.h" -#include - -PreLaunchCommand::PreLaunchCommand(LaunchTask *parent) : LaunchStep(parent) -{ - auto instance = m_parent->instance(); - m_command = instance->getPreLaunchCommand(); - m_process.setProcessEnvironment(instance->createEnvironment()); - connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines); - connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state); -} - -void PreLaunchCommand::executeTask() -{ - //FIXME: where to put this? - QString prelaunch_cmd = m_parent->substituteVariables(m_command); - emit logLine(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd), MessageLevel::MultiMC); - m_process.start(prelaunch_cmd); -} - -void PreLaunchCommand::on_state(LoggedProcess::State state) -{ - auto getError = [&]() - { - return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); - }; - switch(state) - { - case LoggedProcess::Aborted: - case LoggedProcess::Crashed: - case LoggedProcess::FailedToStart: - { - auto error = getError(); - emit logLine(error, MessageLevel::Fatal); - emitFailed(error); - return; - } - case LoggedProcess::Finished: - { - if(m_process.exitCode() != 0) - { - auto error = getError(); - emit logLine(error, MessageLevel::Fatal); - emitFailed(error); - } - else - { - emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::MultiMC); - emitSucceeded(); - } - } - default: - break; - } -} - -void PreLaunchCommand::setWorkingDirectory(const QString &wd) -{ - m_process.setWorkingDirectory(wd); -} - -bool PreLaunchCommand::abort() -{ - auto state = m_process.state(); - if (state == LoggedProcess::Running || state == LoggedProcess::Starting) - { - m_process.kill(); - } - return true; -} diff --git a/libraries/logic/launch/steps/PreLaunchCommand.h b/libraries/logic/launch/steps/PreLaunchCommand.h deleted file mode 100644 index 077bdfca..00000000 --- a/libraries/logic/launch/steps/PreLaunchCommand.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include - -class PreLaunchCommand: public LaunchStep -{ - Q_OBJECT -public: - explicit PreLaunchCommand(LaunchTask *parent); - virtual void executeTask(); - virtual bool abort(); - virtual bool canAbort() const - { - return true; - } - void setWorkingDirectory(const QString &wd); -private slots: - void on_state(LoggedProcess::State state); - -private: - LoggedProcess m_process; - QString m_command; -}; diff --git a/libraries/logic/launch/steps/TextPrint.cpp b/libraries/logic/launch/steps/TextPrint.cpp deleted file mode 100644 index f307b1fd..00000000 --- a/libraries/logic/launch/steps/TextPrint.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "TextPrint.h" - -TextPrint::TextPrint(LaunchTask * parent, const QStringList &lines, MessageLevel::Enum level) : LaunchStep(parent) -{ - m_lines = lines; - m_level = level; -} -TextPrint::TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level) : LaunchStep(parent) -{ - m_lines.append(line); - m_level = level; -} - -void TextPrint::executeTask() -{ - emit logLines(m_lines, m_level); - emitSucceeded(); -} - -bool TextPrint::canAbort() const -{ - return true; -} - -bool TextPrint::abort() -{ - emitFailed("Aborted."); - return true; -} diff --git a/libraries/logic/launch/steps/TextPrint.h b/libraries/logic/launch/steps/TextPrint.h deleted file mode 100644 index fdd9014a..00000000 --- a/libraries/logic/launch/steps/TextPrint.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -#include "multimc_logic_export.h" - -/* - * FIXME: maybe do not export - */ - -class MULTIMC_LOGIC_EXPORT TextPrint: public LaunchStep -{ - Q_OBJECT -public: - explicit TextPrint(LaunchTask *parent, const QStringList &lines, MessageLevel::Enum level); - explicit TextPrint(LaunchTask *parent, const QString &line, MessageLevel::Enum level); - virtual ~TextPrint(){}; - - virtual void executeTask(); - virtual bool canAbort() const; - virtual bool abort(); - -private: - QStringList m_lines; - MessageLevel::Enum m_level; -}; diff --git a/libraries/logic/launch/steps/Update.cpp b/libraries/logic/launch/steps/Update.cpp deleted file mode 100644 index 4901f001..00000000 --- a/libraries/logic/launch/steps/Update.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "Update.h" -#include - -void Update::executeTask() -{ - m_updateTask = m_parent->instance()->createUpdateTask(); - if(m_updateTask) - { - connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished())); - connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress); - connect(m_updateTask.get(), &Task::status, this, &Task::setStatus); - emit progressReportingRequest(); - return; - } - emitSucceeded(); -} - -void Update::proceed() -{ - m_updateTask->start(); -} - -void Update::updateFinished() -{ - if(m_updateTask->successful()) - { - emitSucceeded(); - } - else - { - QString reason = tr("Instance update failed because: %1.\n\n").arg(m_updateTask->failReason()); - emit logLine(reason, MessageLevel::Fatal); - emitFailed(reason); - } -} diff --git a/libraries/logic/launch/steps/Update.h b/libraries/logic/launch/steps/Update.h deleted file mode 100644 index 14928253..00000000 --- a/libraries/logic/launch/steps/Update.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -// FIXME: stupid. should be defined by the instance type? or even completely abstracted away... -class Update: public LaunchStep -{ - Q_OBJECT -public: - explicit Update(LaunchTask *parent):LaunchStep(parent) {}; - virtual ~Update() {}; - - virtual void executeTask(); - virtual bool canAbort() const - { - return false; - } - virtual void proceed(); -private slots: - void updateFinished(); - -private: - std::shared_ptr m_updateTask; -}; diff --git a/libraries/logic/minecraft/AssetsUtils.cpp b/libraries/logic/minecraft/AssetsUtils.cpp deleted file mode 100644 index 7a525abe..00000000 --- a/libraries/logic/minecraft/AssetsUtils.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AssetsUtils.h" -#include "FileSystem.h" -#include "net/MD5EtagDownload.h" - -namespace AssetsUtils -{ - -/* - * Returns true on success, with index populated - * index is undefined otherwise - */ -bool loadAssetsIndexJson(QString assetsId, QString path, AssetsIndex *index) -{ - /* - { - "objects": { - "icons/icon_16x16.png": { - "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a", - "size": 3665 - }, - ... - } - } - } - */ - - QFile file(path); - - // Try to open the file and fail if we can't. - // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { - qCritical() << "Failed to read assets index file" << path; - return false; - } - index->id = assetsId; - - // Read the file and close it. - QByteArray jsonData = file.readAll(); - file.close(); - - QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); - - // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { - qCritical() << "Failed to parse assets index file:" << parseError.errorString() - << "at offset " << QString::number(parseError.offset); - return false; - } - - // Make sure the root is an object. - if (!jsonDoc.isObject()) - { - qCritical() << "Invalid assets index JSON: Root should be an array."; - return false; - } - - QJsonObject root = jsonDoc.object(); - - QJsonValue isVirtual = root.value("virtual"); - if (!isVirtual.isUndefined()) - { - index->isVirtual = isVirtual.toBool(false); - } - - QJsonValue objects = root.value("objects"); - QVariantMap map = objects.toVariant().toMap(); - - for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) - { - // qDebug() << iter.key(); - - QVariant variant = iter.value(); - QVariantMap nested_objects = variant.toMap(); - - AssetObject object; - - for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); - nested_iter != nested_objects.end(); ++nested_iter) - { - // qDebug() << nested_iter.key() << nested_iter.value().toString(); - QString key = nested_iter.key(); - QVariant value = nested_iter.value(); - - if (key == "hash") - { - object.hash = value.toString(); - } - else if (key == "size") - { - object.size = value.toDouble(); - } - } - - index->objects.insert(iter.key(), object); - } - - return true; -} - -QDir reconstructAssets(QString assetsId) -{ - QDir assetsDir = QDir("assets/"); - QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes")); - QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects")); - QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual")); - - QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json"); - QFile indexFile(indexPath); - QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId)); - - if (!indexFile.exists()) - { - qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets"; - return virtualRoot; - } - - qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() - << objectDir.path() << virtualDir.path() << virtualRoot.path(); - - AssetsIndex index; - bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, &index); - - if (loadAssetsIndex && index.isVirtual) - { - qDebug() << "Reconstructing virtual assets folder at" << virtualRoot.path(); - - for (QString map : index.objects.keys()) - { - AssetObject asset_object = index.objects.value(map); - QString target_path = FS::PathCombine(virtualRoot.path(), map); - QFile target(target_path); - - QString tlk = asset_object.hash.left(2); - - QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash); - QFile original(original_path); - if (!original.exists()) - continue; - if (!target.exists()) - { - QFileInfo info(target_path); - QDir target_dir = info.dir(); - // qDebug() << target_dir; - if (!target_dir.exists()) - QDir("").mkpath(target_dir.path()); - - bool couldCopy = original.copy(target_path); - qDebug() << " Copying" << original_path << "to" << target_path - << QString::number(couldCopy); // << original.errorString(); - } - } - - // TODO: Write last used time to virtualRoot/.lastused - } - - return virtualRoot; -} - -} - -NetActionPtr AssetObject::getDownloadAction() -{ - QFileInfo objectFile(getLocalPath()); - if ((!objectFile.isFile()) || (objectFile.size() != size)) - { - auto objectDL = MD5EtagDownload::make(getUrl(), objectFile.filePath()); - objectDL->m_total_progress = size; - return objectDL; - } - return nullptr; -} - -QString AssetObject::getLocalPath() -{ - return "assets/objects/" + getRelPath(); -} - -QUrl AssetObject::getUrl() -{ - return QUrl("http://resources.download.minecraft.net/" + getRelPath()); -} - -QString AssetObject::getRelPath() -{ - return hash.left(2) + "/" + hash; -} - -NetJobPtr AssetsIndex::getDownloadJob() -{ - auto job = new NetJob(QObject::tr("Assets for %1").arg(id)); - for (auto &object : objects.values()) - { - auto dl = object.getDownloadAction(); - if(dl) - { - job->addNetAction(dl); - } - } - if(job->size()) - return job; - return nullptr; -} diff --git a/libraries/logic/minecraft/AssetsUtils.h b/libraries/logic/minecraft/AssetsUtils.h deleted file mode 100644 index 90251c2d..00000000 --- a/libraries/logic/minecraft/AssetsUtils.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include "net/NetAction.h" -#include "net/NetJob.h" - -struct AssetObject -{ - QString getRelPath(); - QUrl getUrl(); - QString getLocalPath(); - NetActionPtr getDownloadAction(); - - QString hash; - qint64 size; -}; - -struct AssetsIndex -{ - NetJobPtr getDownloadJob(); - - QString id; - QMap objects; - bool isVirtual = false; -}; - -namespace AssetsUtils -{ -bool loadAssetsIndexJson(QString id, QString file, AssetsIndex* index); -/// Reconstruct a virtual assets folder for the given assets ID and return the folder -QDir reconstructAssets(QString assetsId); -} diff --git a/libraries/logic/minecraft/GradleSpecifier.h b/libraries/logic/minecraft/GradleSpecifier.h deleted file mode 100644 index 18308537..00000000 --- a/libraries/logic/minecraft/GradleSpecifier.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include -#include -#include "DefaultVariable.h" - -struct GradleSpecifier -{ - GradleSpecifier() - { - m_valid = false; - } - GradleSpecifier(QString value) - { - operator=(value); - } - GradleSpecifier & operator =(const QString & value) - { - /* - org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar - DEBUG 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar" - DEBUG 1 "org.gradle.test.classifiers" - DEBUG 2 "service" - DEBUG 3 "1.0" - DEBUG 4 ":jdk15" - DEBUG 5 "jdk15" - DEBUG 6 "@jar" - DEBUG 7 "jar" - */ - QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(:([^:@]+))?" "(@([^:@]+))?"); - m_valid = matcher.exactMatch(value); - auto elements = matcher.capturedTexts(); - m_groupId = elements[1]; - m_artifactId = elements[2]; - m_version = elements[3]; - m_classifier = elements[5]; - if(!elements[7].isEmpty()) - { - m_extension = elements[7]; - } - return *this; - } - operator QString() const - { - if(!m_valid) - return "INVALID"; - QString retval = m_groupId + ":" + m_artifactId + ":" + m_version; - if(!m_classifier.isEmpty()) - { - retval += ":" + m_classifier; - } - if(m_extension.isExplicit()) - { - retval += "@" + m_extension; - } - return retval; - } - QString toPath() const - { - if(!m_valid) - return "INVALID"; - QString path = m_groupId; - path.replace('.', '/'); - path += '/' + m_artifactId + '/' + m_version + '/' + m_artifactId + '-' + m_version; - if(!m_classifier.isEmpty()) - { - path += "-" + m_classifier; - } - path += "." + m_extension; - return path; - } - inline bool valid() const - { - return m_valid; - } - inline QString version() const - { - return m_version; - } - inline QString groupId() const - { - return m_groupId; - } - inline QString artifactId() const - { - return m_artifactId; - } - inline void setClassifier(const QString & classifier) - { - m_classifier = classifier; - } - inline QString classifier() const - { - return m_classifier; - } - inline QString extension() const - { - return m_extension; - } - inline QString artifactPrefix() const - { - return m_groupId + ":" + m_artifactId; - } - bool matchName(const GradleSpecifier & other) const - { - return other.artifactId() == artifactId() && other.groupId() == groupId(); - } - bool operator==(const GradleSpecifier & other) const - { - if(m_groupId != other.m_groupId) - return false; - if(m_artifactId != other.m_artifactId) - return false; - if(m_version != other.m_version) - return false; - if(m_classifier != other.m_classifier) - return false; - if(m_extension != other.m_extension) - return false; - return true; - } -private: - QString m_groupId; - QString m_artifactId; - QString m_version; - QString m_classifier; - DefaultVariable m_extension = DefaultVariable("jar"); - bool m_valid = false; -}; diff --git a/libraries/logic/minecraft/JarMod.h b/libraries/logic/minecraft/JarMod.h deleted file mode 100644 index 42d05da9..00000000 --- a/libraries/logic/minecraft/JarMod.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -#include -#include -class Jarmod; -typedef std::shared_ptr JarmodPtr; -class Jarmod -{ -public: /* data */ - QString name; - QString originalName; -}; diff --git a/libraries/logic/minecraft/Library.cpp b/libraries/logic/minecraft/Library.cpp deleted file mode 100644 index 922db84e..00000000 --- a/libraries/logic/minecraft/Library.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include "Library.h" -#include -#include -#include -#include - -void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32, QStringList& native64) const -{ - auto actualPath = [&](QString relPath) - { - QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); - return out.absoluteFilePath(); - }; - if(m_mojangDownloads) - { - if(m_mojangDownloads->artifact) - { - auto artifact = m_mojangDownloads->artifact; - jar += actualPath(artifact->path); - } - if(!isNative()) - return; - if(m_nativeClassifiers.contains(system)) - { - auto nativeClassifier = m_nativeClassifiers[system]; - if(nativeClassifier.contains("${arch}")) - { - auto nat32Classifier = nativeClassifier; - nat32Classifier.replace("${arch}", "32"); - auto nat64Classifier = nativeClassifier; - nat64Classifier.replace("${arch}", "64"); - auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); - if(nat32info) - native32 += actualPath(nat32info->path); - auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); - if(nat64info) - native64 += actualPath(nat64info->path); - } - else - { - native += actualPath(m_mojangDownloads->getDownloadInfo(nativeClassifier)->path); - } - } - } - else - { - QString raw_storage = storageSuffix(system); - if(isNative()) - { - if (raw_storage.contains("${arch}")) - { - auto nat32Storage = raw_storage; - nat32Storage.replace("${arch}", "32"); - auto nat64Storage = raw_storage; - nat64Storage.replace("${arch}", "64"); - native32 += actualPath(nat32Storage); - native64 += actualPath(nat64Storage); - } - else - { - native += actualPath(raw_storage); - } - } - else - { - jar += actualPath(raw_storage); - } - } -} - -QList Library::getDownloads(OpSys system, HttpMetaCache * cache, QStringList &failedFiles) const -{ - QList out; - bool isLocal = (hint() == "local"); - bool isForge = (hint() == "forge-pack-xz"); - - auto add_download = [&](QString storage, QString dl) - { - auto entry = cache->resolveEntry("libraries", storage); - if (!entry->isStale()) - return true; - if(isLocal) - { - QFileInfo fileinfo(entry->getFullPath()); - if(!fileinfo.exists()) - { - failedFiles.append(entry->getFullPath()); - return false; - } - return true; - } - if (isForge) - { - out.append(ForgeXzDownload::make(storage, entry)); - } - else - { - out.append(CacheDownload::make(dl, entry)); - } - return true; - }; - - if(m_mojangDownloads) - { - if(m_mojangDownloads->artifact) - { - auto artifact = m_mojangDownloads->artifact; - add_download(artifact->path, artifact->url); - } - if(m_nativeClassifiers.contains(system)) - { - auto nativeClassifier = m_nativeClassifiers[system]; - if(nativeClassifier.contains("${arch}")) - { - auto nat32Classifier = nativeClassifier; - nat32Classifier.replace("${arch}", "32"); - auto nat64Classifier = nativeClassifier; - nat64Classifier.replace("${arch}", "64"); - auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); - if(nat32info) - add_download(nat32info->path, nat32info->url); - auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); - if(nat64info) - add_download(nat64info->path, nat64info->url); - } - else - { - auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); - if(info) - { - add_download(info->path, info->url); - } - } - } - } - else - { - QString raw_storage = storageSuffix(system); - auto raw_dl = [&](){ - if (!m_absoluteURL.isEmpty()) - { - return m_absoluteURL; - } - - if (m_repositoryURL.isEmpty()) - { - return QString("https://" + URLConstants::LIBRARY_BASE) + raw_storage; - } - - if(m_repositoryURL.endsWith('/')) - { - return m_repositoryURL + raw_storage; - } - else - { - return m_repositoryURL + QChar('/') + raw_storage; - } - }(); - if (raw_storage.contains("${arch}")) - { - QString cooked_storage = raw_storage; - QString cooked_dl = raw_dl; - add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); - cooked_storage = raw_storage; - cooked_dl = raw_dl; - add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); - } - else - { - add_download(raw_storage, raw_dl); - } - } - return out; -} - -bool Library::isActive() const -{ - bool result = true; - if (m_rules.empty()) - { - result = true; - } - else - { - RuleAction ruleResult = Disallow; - for (auto rule : m_rules) - { - RuleAction temp = rule->apply(this); - if (temp != Defer) - ruleResult = temp; - } - result = result && (ruleResult == Allow); - } - if (isNative()) - { - result = result && m_nativeClassifiers.contains(currentSystem); - } - return result; -} - -void Library::setStoragePrefix(QString prefix) -{ - m_storagePrefix = prefix; -} - -QString Library::defaultStoragePrefix() -{ - return "libraries/"; -} - -QString Library::storagePrefix() const -{ - if(m_storagePrefix.isEmpty()) - { - return defaultStoragePrefix(); - } - return m_storagePrefix; -} - -QString Library::storageSuffix(OpSys system) const -{ - // non-native? use only the gradle specifier - if (!isNative()) - { - return m_name.toPath(); - } - - // otherwise native, override classifiers. Mojang HACK! - GradleSpecifier nativeSpec = m_name; - if (m_nativeClassifiers.contains(system)) - { - nativeSpec.setClassifier(m_nativeClassifiers[system]); - } - else - { - nativeSpec.setClassifier("INVALID"); - } - return nativeSpec.toPath(); -} diff --git a/libraries/logic/minecraft/Library.h b/libraries/logic/minecraft/Library.h deleted file mode 100644 index fdce93f3..00000000 --- a/libraries/logic/minecraft/Library.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Rule.h" -#include "minecraft/OpSys.h" -#include "GradleSpecifier.h" -#include "net/URLConstants.h" -#include "MojangDownloadInfo.h" - -#include "multimc_logic_export.h" - -class Library; - -typedef std::shared_ptr LibraryPtr; - -class MULTIMC_LOGIC_EXPORT Library -{ - friend class OneSixVersionFormat; - friend class MojangVersionFormat; - friend class LibraryTest; -public: - Library() - { - } - Library(const QString &name) - { - m_name = name; - } - /// limited copy without some data. TODO: why? - static LibraryPtr limitedCopy(LibraryPtr base) - { - auto newlib = std::make_shared(); - newlib->m_name = base->m_name; - newlib->m_repositoryURL = base->m_repositoryURL; - newlib->m_hint = base->m_hint; - newlib->m_absoluteURL = base->m_absoluteURL; - newlib->m_extractExcludes = base->m_extractExcludes; - newlib->m_nativeClassifiers = base->m_nativeClassifiers; - newlib->m_rules = base->m_rules; - newlib->m_storagePrefix = base->m_storagePrefix; - newlib->m_mojangDownloads = base->m_mojangDownloads; - return newlib; - } - -public: /* methods */ - /// Returns the raw name field - const GradleSpecifier & rawName() const - { - return m_name; - } - - void setRawName(const GradleSpecifier & spec) - { - m_name = spec; - } - - void setClassifier(const QString & spec) - { - m_name.setClassifier(spec); - } - - /// returns the full group and artifact prefix - QString artifactPrefix() const - { - return m_name.artifactPrefix(); - } - - /// get the artifact ID - QString artifactId() const - { - return m_name.artifactId(); - } - - /// get the artifact version - QString version() const - { - return m_name.version(); - } - - /// Returns true if the library is native - bool isNative() const - { - return m_nativeClassifiers.size() != 0; - } - - void setStoragePrefix(QString prefix = QString()); - - /// Set the url base for downloads - void setRepositoryURL(const QString &base_url) - { - m_repositoryURL = base_url; - } - - void getApplicableFiles(OpSys system, QStringList & jar, QStringList & native, QStringList & native32, QStringList & native64) const; - - void setAbsoluteUrl(const QString &absolute_url) - { - m_absoluteURL = absolute_url; - } - - void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info) - { - m_mojangDownloads = info; - } - - void setHint(const QString &hint) - { - m_hint = hint; - } - - /// Set the load rules - void setRules(QList> rules) - { - m_rules = rules; - } - - /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive() const; - - // Get a list of downloads for this library - QList getDownloads(OpSys system, class HttpMetaCache * cache, QStringList &failedFiles) const; - -private: /* methods */ - /// the default storage prefix used by MultiMC - static QString defaultStoragePrefix(); - - /// Get the prefix - root of the storage to be used - QString storagePrefix() const; - - /// Get the relative path where the library should be saved - QString storageSuffix(OpSys system) const; - - QString hint() const - { - return m_hint; - } - -protected: /* data */ - /// the basic gradle dependency specifier. - GradleSpecifier m_name; - - /// DEPRECATED URL prefix of the maven repo where the file can be downloaded - QString m_repositoryURL; - - /// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined - QString m_absoluteURL; - - /** - * MultiMC-specific type hint - modifies how the library is treated - */ - QString m_hint; - - /** - * storage - by default the local libraries folder in multimc, but could be elsewhere - * MultiMC specific, because of FTB. - */ - QString m_storagePrefix; - - /// true if the library had an extract/excludes section (even empty) - bool m_hasExcludes = false; - - /// a list of files that shouldn't be extracted from the library - QStringList m_extractExcludes; - - /// native suffixes per OS - QMap m_nativeClassifiers; - - /// true if the library had a rules section (even empty) - bool applyRules = false; - - /// rules associated with the library - QList> m_rules; - - /// MOJANG: container with Mojang style download info - MojangLibraryDownloadInfo::Ptr m_mojangDownloads; -}; diff --git a/libraries/logic/minecraft/MinecraftInstance.cpp b/libraries/logic/minecraft/MinecraftInstance.cpp deleted file mode 100644 index 405ccd26..00000000 --- a/libraries/logic/minecraft/MinecraftInstance.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include "MinecraftInstance.h" -#include -#include "settings/SettingsObject.h" -#include "Env.h" -#include "minecraft/MinecraftVersionList.h" -#include -#include -#include -#include -#include - -#define IBUS "@im=ibus" - -// all of this because keeping things compatible with deprecated old settings -// if either of the settings {a, b} is true, this also resolves to true -class OrSetting : public Setting -{ - Q_OBJECT -public: - OrSetting(QString id, std::shared_ptr a, std::shared_ptr b) - :Setting({id}, false), m_a(a), m_b(b) - { - } - virtual QVariant get() const - { - bool a = m_a->get().toBool(); - bool b = m_b->get().toBool(); - return a || b; - } - virtual void reset() {} - virtual void set(QVariant value) {} -private: - std::shared_ptr m_a; - std::shared_ptr m_b; -}; - -MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) - : BaseInstance(globalSettings, settings, rootDir) -{ - // Java Settings - auto javaOverride = m_settings->registerSetting("OverrideJava", false); - auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false); - auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false); - - // combinations - auto javaOrLocation = std::make_shared("JavaOrLocationOverride", javaOverride, locationOverride); - auto javaOrArgs = std::make_shared("JavaOrArgsOverride", javaOverride, argsOverride); - - m_settings->registerOverride(globalSettings->getSetting("JavaPath"), javaOrLocation); - m_settings->registerOverride(globalSettings->getSetting("JvmArgs"), javaOrArgs); - - // special! - m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation); - m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation); - - // Window Size - auto windowSetting = m_settings->registerSetting("OverrideWindow", false); - m_settings->registerOverride(globalSettings->getSetting("LaunchMaximized"), windowSetting); - m_settings->registerOverride(globalSettings->getSetting("MinecraftWinWidth"), windowSetting); - m_settings->registerOverride(globalSettings->getSetting("MinecraftWinHeight"), windowSetting); - - // Memory - auto memorySetting = m_settings->registerSetting("OverrideMemory", false); - m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting); - m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting); - m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting); -} - -QString MinecraftInstance::minecraftRoot() const -{ - QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); - QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); - - if (dotMCDir.exists() && !mcDir.exists()) - return dotMCDir.filePath(); - else - return mcDir.filePath(); -} - -std::shared_ptr< BaseVersionList > MinecraftInstance::versionList() const -{ - return ENV.getVersionList("net.minecraft"); -} - -QStringList MinecraftInstance::javaArguments() const -{ - QStringList args; - - // custom args go first. we want to override them if we have our own here. - args.append(extraArguments()); - - // OSX dock icon and name -#ifdef Q_OS_MAC - args << "-Xdock:icon=icon.png"; - args << QString("-Xdock:name=\"%1\"").arg(windowTitle()); -#endif - - // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767 -#ifdef Q_OS_WIN32 - args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_" - "minecraft.exe.heapdump"); -#endif - - args << QString("-Xms%1m").arg(settings()->get("MinMemAlloc").toInt()); - args << QString("-Xmx%1m").arg(settings()->get("MaxMemAlloc").toInt()); - - // No PermGen in newer java. - JavaVersion javaVersion(settings()->get("JavaVersion").toString()); - if(javaVersion.requiresPermGen()) - { - auto permgen = settings()->get("PermGen").toInt(); - if (permgen != 64) - { - args << QString("-XX:PermSize=%1m").arg(permgen); - } - } - - args << "-Duser.language=en"; - args << "-jar" << FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "NewLaunch.jar"); - - return args; -} - -QMap MinecraftInstance::getVariables() const -{ - QMap out; - out.insert("INST_NAME", name()); - out.insert("INST_ID", id()); - out.insert("INST_DIR", QDir(instanceRoot()).absolutePath()); - out.insert("INST_MC_DIR", QDir(minecraftRoot()).absolutePath()); - out.insert("INST_JAVA", settings()->get("JavaPath").toString()); - out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); - return out; -} - -static QString processLD_LIBRARY_PATH(const QString & LD_LIBRARY_PATH) -{ - QDir mmcBin(QCoreApplication::applicationDirPath()); - auto items = LD_LIBRARY_PATH.split(':'); - QStringList final; - for(auto & item: items) - { - QDir test(item); - if(test == mmcBin) - { - qDebug() << "Env:LD_LIBRARY_PATH ignoring path" << item; - continue; - } - final.append(item); - } - return final.join(':'); -} - -QProcessEnvironment MinecraftInstance::createEnvironment() -{ - // prepare the process environment - QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); - QProcessEnvironment env; - - QStringList ignored = - { - "JAVA_ARGS", - "CLASSPATH", - "CONFIGPATH", - "JAVA_HOME", - "JRE_HOME", - "_JAVA_OPTIONS", - "JAVA_OPTIONS", - "JAVA_TOOL_OPTIONS" - }; - for(auto key: rawenv.keys()) - { - auto value = rawenv.value(key); - // filter out dangerous java crap - if(ignored.contains(key)) - { - qDebug() << "Env: ignoring" << key << value; - continue; - } - // filter MultiMC-related things - if(key.startsWith("QT_")) - { - qDebug() << "Env: ignoring" << key << value; - continue; - } -#ifdef Q_OS_LINUX - // Do not pass LD_* variables to java. They were intended for MultiMC - if(key.startsWith("LD_")) - { - qDebug() << "Env: ignoring" << key << value; - continue; - } - // Strip IBus - // IBus is a Linux IME framework. For some reason, it breaks MC? - if (key == "XMODIFIERS" && value.contains(IBUS)) - { - QString save = value; - value.replace(IBUS, ""); - qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value; - } - if(key == "GAME_PRELOAD") - { - env.insert("LD_PRELOAD", value); - continue; - } - if(key == "GAME_LIBRARY_PATH") - { - env.insert("LD_LIBRARY_PATH", processLD_LIBRARY_PATH(value)); - continue; - } -#endif - qDebug() << "Env: " << key << value; - env.insert(key, value); - } -#ifdef Q_OS_LINUX - // HACK: Workaround for QTBUG42500 - if(!env.contains("LD_LIBRARY_PATH")) - { - env.insert("LD_LIBRARY_PATH", ""); - } -#endif - - // export some infos - auto variables = getVariables(); - for (auto it = variables.begin(); it != variables.end(); ++it) - { - env.insert(it.key(), it.value()); - } - return env; -} - -QMap MinecraftInstance::createCensorFilterFromSession(AuthSessionPtr session) -{ - if(!session) - { - return QMap(); - } - auto & sessionRef = *session.get(); - QMap filter; - auto addToFilter = [&filter](QString key, QString value) - { - if(key.trimmed().size()) - { - filter[key] = value; - } - }; - if (sessionRef.session != "-") - { - addToFilter(sessionRef.session, tr("")); - } - addToFilter(sessionRef.access_token, tr("")); - addToFilter(sessionRef.client_token, tr("")); - addToFilter(sessionRef.uuid, tr("")); - addToFilter(sessionRef.player_name, tr("")); - - auto i = sessionRef.u.properties.begin(); - while (i != sessionRef.u.properties.end()) - { - addToFilter(i.value(), "<" + i.key().toUpper() + ">"); - ++i; - } - return filter; -} - -MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLevel::Enum level) -{ - QRegularExpression re("\\[(?[0-9:]+)\\] \\[[^/]+/(?[^\\]]+)\\]"); - auto match = re.match(line); - if(match.hasMatch()) - { - // New style logs from log4j - QString timestamp = match.captured("timestamp"); - QString levelStr = match.captured("level"); - if(levelStr == "INFO") - level = MessageLevel::Message; - if(levelStr == "WARN") - level = MessageLevel::Warning; - if(levelStr == "ERROR") - level = MessageLevel::Error; - if(levelStr == "FATAL") - level = MessageLevel::Fatal; - if(levelStr == "TRACE" || levelStr == "DEBUG") - level = MessageLevel::Debug; - } - else - { - // Old style forge logs - if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || - line.contains("[FINER]") || line.contains("[FINEST]")) - level = MessageLevel::Message; - if (line.contains("[SEVERE]") || line.contains("[STDERR]")) - level = MessageLevel::Error; - if (line.contains("[WARNING]")) - level = MessageLevel::Warning; - if (line.contains("[DEBUG]")) - level = MessageLevel::Debug; - } - if (line.contains("overwriting existing")) - return MessageLevel::Fatal; - //NOTE: this diverges from the real regexp. no unicode, the first section is + instead of * - static const QString javaSymbol = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*"; - if (line.contains("Exception in thread") - || line.contains(QRegularExpression("\\s+at " + javaSymbol)) - || line.contains(QRegularExpression("Caused by: " + javaSymbol)) - || line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")) - || line.contains(QRegularExpression("... \\d+ more$")) - ) - return MessageLevel::Error; - return level; -} - -IPathMatcher::Ptr MinecraftInstance::getLogFileMatcher() -{ - auto combined = std::make_shared(); - combined->add(std::make_shared(".*\\.log(\\.[0-9]*)?(\\.gz)?$")); - combined->add(std::make_shared("crash-.*\\.txt")); - combined->add(std::make_shared("IDMap dump.*\\.txt$")); - combined->add(std::make_shared("ModLoader\\.txt(\\..*)?$")); - return combined; -} - -QString MinecraftInstance::getLogFileRoot() -{ - return minecraftRoot(); -} - -QString MinecraftInstance::prettifyTimeDuration(int64_t duration) -{ - int seconds = (int) (duration % 60); - duration /= 60; - int minutes = (int) (duration % 60); - duration /= 60; - int hours = (int) (duration % 24); - int days = (int) (duration / 24); - if((hours == 0)&&(days == 0)) - { - return tr("%1m %2s").arg(minutes).arg(seconds); - } - if (days == 0) - { - return tr("%1h %2m").arg(hours).arg(minutes); - } - return tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes); -} - -QString MinecraftInstance::getStatusbarDescription() -{ - QStringList traits; - if (flags() & VersionBrokenFlag) - { - traits.append(tr("broken")); - } - - QString description; - description.append(tr("Minecraft %1 (%2)").arg(intendedVersionId()).arg(typeName())); - if(totalTimePlayed() > 0) - { - description.append(tr(", played for %1").arg(prettifyTimeDuration(totalTimePlayed()))); - } - /* - if(traits.size()) - { - description.append(QString(" (%1)").arg(traits.join(", "))); - } - */ - return description; -} - -#include "MinecraftInstance.moc" diff --git a/libraries/logic/minecraft/MinecraftInstance.h b/libraries/logic/minecraft/MinecraftInstance.h deleted file mode 100644 index cd3a8d90..00000000 --- a/libraries/logic/minecraft/MinecraftInstance.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include "BaseInstance.h" -#include "minecraft/Mod.h" -#include - -#include "multimc_logic_export.h" - -class ModList; -class WorldList; - -class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance -{ -public: - MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir); - virtual ~MinecraftInstance() {}; - - /// Path to the instance's minecraft directory. - QString minecraftRoot() const; - - ////// Mod Lists ////// - virtual std::shared_ptr resourcePackList() const - { - return nullptr; - } - virtual std::shared_ptr texturePackList() const - { - return nullptr; - } - virtual std::shared_ptr worldList() const - { - return nullptr; - } - /// get all jar mods applicable to this instance's jar - virtual QList getJarMods() const - { - return QList(); - } - - /// get the launch script to be used with this - virtual QString createLaunchScript(AuthSessionPtr session) = 0; - - //FIXME: nuke? - virtual std::shared_ptr versionList() const override; - - /// get arguments passed to java - QStringList javaArguments() const; - - /// get variables for launch command variable substitution/environment - virtual QMap getVariables() const override; - - /// create an environment for launching processes - virtual QProcessEnvironment createEnvironment() override; - - /// guess log level from a line of minecraft log - virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level) override; - - virtual IPathMatcher::Ptr getLogFileMatcher() override; - - virtual QString getLogFileRoot() override; - - virtual QString getStatusbarDescription() override; - -protected: - QMap createCensorFilterFromSession(AuthSessionPtr session); -private: - QString prettifyTimeDuration(int64_t duration); -}; - -typedef std::shared_ptr MinecraftInstancePtr; diff --git a/libraries/logic/minecraft/MinecraftProfile.cpp b/libraries/logic/minecraft/MinecraftProfile.cpp deleted file mode 100644 index 70d0cee4..00000000 --- a/libraries/logic/minecraft/MinecraftProfile.cpp +++ /dev/null @@ -1,610 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "minecraft/MinecraftProfile.h" -#include "ProfileUtils.h" -#include "ProfileStrategy.h" -#include "Exception.h" - -MinecraftProfile::MinecraftProfile(ProfileStrategy *strategy) - : QAbstractListModel() -{ - setStrategy(strategy); - clear(); -} - -void MinecraftProfile::setStrategy(ProfileStrategy* strategy) -{ - Q_ASSERT(strategy != nullptr); - - if(m_strategy != nullptr) - { - delete m_strategy; - m_strategy = nullptr; - } - m_strategy = strategy; - m_strategy->profile = this; -} - -ProfileStrategy* MinecraftProfile::strategy() -{ - return m_strategy; -} - -void MinecraftProfile::reload() -{ - beginResetModel(); - m_strategy->load(); - reapplyPatches(); - endResetModel(); -} - -void MinecraftProfile::clear() -{ - m_minecraftVersion.clear(); - m_minecraftVersionType.clear(); - m_minecraftAssets.reset(); - m_minecraftArguments.clear(); - m_tweakers.clear(); - m_mainClass.clear(); - m_appletClass.clear(); - m_libraries.clear(); - m_traits.clear(); - m_jarMods.clear(); - mojangDownloads.clear(); - m_problemSeverity = ProblemSeverity::PROBLEM_NONE; -} - -void MinecraftProfile::clearPatches() -{ - beginResetModel(); - m_patches.clear(); - endResetModel(); -} - -void MinecraftProfile::appendPatch(ProfilePatchPtr patch) -{ - int index = m_patches.size(); - beginInsertRows(QModelIndex(), index, index); - m_patches.append(patch); - endInsertRows(); -} - -bool MinecraftProfile::remove(const int index) -{ - auto patch = versionPatch(index); - if (!patch->isRemovable()) - { - qDebug() << "Patch" << patch->getID() << "is non-removable"; - return false; - } - - if(!m_strategy->removePatch(patch)) - { - qCritical() << "Patch" << patch->getID() << "could not be removed"; - return false; - } - - beginRemoveRows(QModelIndex(), index, index); - m_patches.removeAt(index); - endRemoveRows(); - reapplyPatches(); - saveCurrentOrder(); - return true; -} - -bool MinecraftProfile::remove(const QString id) -{ - int i = 0; - for (auto patch : m_patches) - { - if (patch->getID() == id) - { - return remove(i); - } - i++; - } - return false; -} - -bool MinecraftProfile::customize(int index) -{ - auto patch = versionPatch(index); - if (!patch->isCustomizable()) - { - qDebug() << "Patch" << patch->getID() << "is not customizable"; - return false; - } - if(!m_strategy->customizePatch(patch)) - { - qCritical() << "Patch" << patch->getID() << "could not be customized"; - return false; - } - reapplyPatches(); - saveCurrentOrder(); - // FIXME: maybe later in unstable - // emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1)); - return true; -} - -bool MinecraftProfile::revertToBase(int index) -{ - auto patch = versionPatch(index); - if (!patch->isRevertible()) - { - qDebug() << "Patch" << patch->getID() << "is not revertible"; - return false; - } - if(!m_strategy->revertPatch(patch)) - { - qCritical() << "Patch" << patch->getID() << "could not be reverted"; - return false; - } - reapplyPatches(); - saveCurrentOrder(); - // FIXME: maybe later in unstable - // emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1)); - return true; -} - -ProfilePatchPtr MinecraftProfile::versionPatch(const QString &id) -{ - for (auto file : m_patches) - { - if (file->getID() == id) - { - return file; - } - } - return nullptr; -} - -ProfilePatchPtr MinecraftProfile::versionPatch(int index) -{ - if(index < 0 || index >= m_patches.size()) - return nullptr; - return m_patches[index]; -} - -bool MinecraftProfile::isVanilla() -{ - for(auto patchptr: m_patches) - { - if(patchptr->isCustom()) - return false; - } - return true; -} - -bool MinecraftProfile::revertToVanilla() -{ - // remove patches, if present - auto VersionPatchesCopy = m_patches; - for(auto & it: VersionPatchesCopy) - { - if (!it->isCustom()) - { - continue; - } - if(it->isRevertible() || it->isRemovable()) - { - if(!remove(it->getID())) - { - qWarning() << "Couldn't remove" << it->getID() << "from profile!"; - reapplyPatches(); - saveCurrentOrder(); - return false; - } - } - } - reapplyPatches(); - saveCurrentOrder(); - return true; -} - -QVariant MinecraftProfile::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= m_patches.size()) - return QVariant(); - - auto patch = m_patches.at(row); - - if (role == Qt::DisplayRole) - { - switch (column) - { - case 0: - return m_patches.at(row)->getName(); - case 1: - { - if(patch->isCustom()) - { - return QString("%1 (Custom)").arg(patch->getVersion()); - } - else - { - return patch->getVersion(); - } - } - default: - return QVariant(); - } - } - if(role == Qt::DecorationRole) - { - switch(column) - { - case 0: - { - auto severity = patch->getProblemSeverity(); - switch (severity) - { - case PROBLEM_WARNING: - return "warning"; - case PROBLEM_ERROR: - return "error"; - default: - return QVariant(); - } - } - default: - { - return QVariant(); - } - } - } - return QVariant(); -} -QVariant MinecraftProfile::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) - { - if (role == Qt::DisplayRole) - { - switch (section) - { - case 0: - return tr("Name"); - case 1: - return tr("Version"); - default: - return QVariant(); - } - } - } - return QVariant(); -} -Qt::ItemFlags MinecraftProfile::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; -} - -int MinecraftProfile::rowCount(const QModelIndex &parent) const -{ - return m_patches.size(); -} - -int MinecraftProfile::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -void MinecraftProfile::saveCurrentOrder() const -{ - ProfileUtils::PatchOrder order; - for(auto item: m_patches) - { - if(!item->isMoveable()) - continue; - order.append(item->getID()); - } - m_strategy->saveOrder(order); -} - -void MinecraftProfile::move(const int index, const MoveDirection direction) -{ - int theirIndex; - if (direction == MoveUp) - { - theirIndex = index - 1; - } - else - { - theirIndex = index + 1; - } - - if (index < 0 || index >= m_patches.size()) - return; - if (theirIndex >= rowCount()) - theirIndex = rowCount() - 1; - if (theirIndex == -1) - theirIndex = rowCount() - 1; - if (index == theirIndex) - return; - int togap = theirIndex > index ? theirIndex + 1 : theirIndex; - - auto from = versionPatch(index); - auto to = versionPatch(theirIndex); - - if (!from || !to || !to->isMoveable() || !from->isMoveable()) - { - return; - } - beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap); - m_patches.swap(index, theirIndex); - endMoveRows(); - reapplyPatches(); - saveCurrentOrder(); -} -void MinecraftProfile::resetOrder() -{ - m_strategy->resetOrder(); - reload(); -} - -bool MinecraftProfile::reapplyPatches() -{ - try - { - clear(); - for(auto file: m_patches) - { - file->applyTo(this); - } - } - catch (Exception & error) - { - clear(); - qWarning() << "Couldn't apply profile patches because: " << error.cause(); - return false; - } - return true; -} - -static void applyString(const QString & from, QString & to) -{ - if(from.isEmpty()) - return; - to = from; -} - -void MinecraftProfile::applyMinecraftVersion(const QString& id) -{ - applyString(id, this->m_minecraftVersion); -} - -void MinecraftProfile::applyAppletClass(const QString& appletClass) -{ - applyString(appletClass, this->m_appletClass); -} - -void MinecraftProfile::applyMainClass(const QString& mainClass) -{ - applyString(mainClass, this->m_mainClass); -} - -void MinecraftProfile::applyMinecraftArguments(const QString& minecraftArguments) -{ - applyString(minecraftArguments, this->m_minecraftArguments); -} - -void MinecraftProfile::applyMinecraftVersionType(const QString& type) -{ - applyString(type, this->m_minecraftVersionType); -} - -void MinecraftProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets) -{ - if(assets) - { - m_minecraftAssets = assets; - } -} - -void MinecraftProfile::applyMojangDownload(const QString &key, MojangDownloadInfo::Ptr download) -{ - if(download) - { - mojangDownloads[key] = download; - } - else - { - mojangDownloads.remove(key); - } -} - -void MinecraftProfile::applyTraits(const QSet& traits) -{ - this->m_traits.unite(traits); -} - -void MinecraftProfile::applyTweakers(const QStringList& tweakers) -{ - // FIXME: check for dupes? - // FIXME: does order matter? - for (auto tweaker : tweakers) - { - this->m_tweakers += tweaker; - } -} - -void MinecraftProfile::applyJarMods(const QList& jarMods) -{ - this->m_jarMods.append(jarMods); -} - -static int findLibraryByName(QList haystack, const GradleSpecifier &needle) -{ - int retval = -1; - for (int i = 0; i < haystack.size(); ++i) - { - if (haystack.at(i)->rawName().matchName(needle)) - { - // only one is allowed. - if (retval != -1) - return -1; - retval = i; - } - } - return retval; -} - -void MinecraftProfile::applyLibrary(LibraryPtr library) -{ - if(!library->isActive()) - { - return; - } - // find the library by name. - const int index = findLibraryByName(m_libraries, library->rawName()); - // library not found? just add it. - if (index < 0) - { - m_libraries.append(Library::limitedCopy(library)); - return; - } - auto existingLibrary = m_libraries.at(index); - // if we are higher it means we should update - if (Version(library->version()) > Version(existingLibrary->version())) - { - auto libraryCopy = Library::limitedCopy(library); - m_libraries.replace(index, libraryCopy); - } -} - -void MinecraftProfile::applyProblemSeverity(ProblemSeverity severity) -{ - if (m_problemSeverity < severity) - { - m_problemSeverity = severity; - } -} - - -QString MinecraftProfile::getMinecraftVersion() const -{ - return m_minecraftVersion; -} - -QString MinecraftProfile::getAppletClass() const -{ - return m_appletClass; -} - -QString MinecraftProfile::getMainClass() const -{ - return m_mainClass; -} - -const QSet &MinecraftProfile::getTraits() const -{ - return m_traits; -} - -const QStringList & MinecraftProfile::getTweakers() const -{ - return m_tweakers; -} - -bool MinecraftProfile::hasTrait(const QString& trait) const -{ - return m_traits.contains(trait); -} - -ProblemSeverity MinecraftProfile::getProblemSeverity() const -{ - return m_problemSeverity; -} - -QString MinecraftProfile::getMinecraftVersionType() const -{ - return m_minecraftVersionType; -} - -std::shared_ptr MinecraftProfile::getMinecraftAssets() const -{ - if(!m_minecraftAssets) - { - return std::make_shared("legacy"); - } - return m_minecraftAssets; -} - -QString MinecraftProfile::getMinecraftArguments() const -{ - return m_minecraftArguments; -} - -const QList & MinecraftProfile::getJarMods() const -{ - return m_jarMods; -} - -const QList & MinecraftProfile::getLibraries() const -{ - return m_libraries; -} - -QString MinecraftProfile::getMainJarUrl() const -{ - auto iter = mojangDownloads.find("client"); - if(iter != mojangDownloads.end()) - { - // current - return iter.value()->url; - } - else - { - // legacy fallback - return URLConstants::getLegacyJarUrl(getMinecraftVersion()); - } -} - -void MinecraftProfile::installJarMods(QStringList selectedFiles) -{ - m_strategy->installJarMods(selectedFiles); -} - -/* - * TODO: get rid of this. Get rid of all order numbers. - */ -int MinecraftProfile::getFreeOrderNumber() -{ - int largest = 100; - // yes, I do realize this is dumb. The order thing itself is dumb. and to be removed next. - for(auto thing: m_patches) - { - int order = thing->getOrder(); - if(order > largest) - largest = order; - } - return largest + 1; -} diff --git a/libraries/logic/minecraft/MinecraftProfile.h b/libraries/logic/minecraft/MinecraftProfile.h deleted file mode 100644 index ca9288ad..00000000 --- a/libraries/logic/minecraft/MinecraftProfile.h +++ /dev/null @@ -1,200 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include - -#include -#include -#include - -#include "Library.h" -#include "VersionFile.h" -#include "JarMod.h" -#include "MojangDownloadInfo.h" - -#include "multimc_logic_export.h" - -class ProfileStrategy; -class OneSixInstance; - - -class MULTIMC_LOGIC_EXPORT MinecraftProfile : public QAbstractListModel -{ - Q_OBJECT - -public: - explicit MinecraftProfile(ProfileStrategy *strategy); - - void setStrategy(ProfileStrategy * strategy); - ProfileStrategy *strategy(); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - virtual int columnCount(const QModelIndex &parent) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; - - /// is this version unchanged by the user? - bool isVanilla(); - - /// remove any customizations on top of whatever 'vanilla' means - bool revertToVanilla(); - - /// install more jar mods - void installJarMods(QStringList selectedFiles); - - /// DEPRECATED, remove ASAP - int getFreeOrderNumber(); - - enum MoveDirection { MoveUp, MoveDown }; - /// move patch file # up or down the list - void move(const int index, const MoveDirection direction); - - /// remove patch file # - including files/records - bool remove(const int index); - - /// remove patch file by id - including files/records - bool remove(const QString id); - - bool customize(int index); - - bool revertToBase(int index); - - void resetOrder(); - - /// reload all profile patches from storage, clear the profile and apply the patches - void reload(); - - /// clear the profile - void clear(); - - /// apply the patches. Catches all the errors and returns true/false for success/failure - bool reapplyPatches(); - -public: /* application of profile variables from patches */ - void applyMinecraftVersion(const QString& id); - void applyMainClass(const QString& mainClass); - void applyAppletClass(const QString& appletClass); - void applyMinecraftArguments(const QString& minecraftArguments); - void applyMinecraftVersionType(const QString& type); - void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets); - void applyTraits(const QSet &traits); - void applyTweakers(const QStringList &tweakers); - void applyJarMods(const QList &jarMods); - void applyLibrary(LibraryPtr library); - void applyProblemSeverity(ProblemSeverity severity); - void applyMojangDownload(const QString & key, MojangDownloadInfo::Ptr download); - -public: /* getters for profile variables */ - QString getMinecraftVersion() const; - QString getMainClass() const; - QString getAppletClass() const; - QString getMinecraftVersionType() const; - MojangAssetIndexInfo::Ptr getMinecraftAssets() const; - QString getMinecraftArguments() const; - const QSet & getTraits() const; - const QStringList & getTweakers() const; - const QList & getJarMods() const; - const QList & getLibraries() const; - QString getMainJarUrl() const; - bool hasTrait(const QString & trait) const; - ProblemSeverity getProblemSeverity() const; - -public: - /// get the profile patch by id - ProfilePatchPtr versionPatch(const QString &id); - - /// get the profile patch by index - ProfilePatchPtr versionPatch(int index); - - /// save the current patch order - void saveCurrentOrder() const; - - /// Remove all the patches - void clearPatches(); - - /// Add the patch object to the internal list of patches - void appendPatch(ProfilePatchPtr patch); - -private: /* data */ - /// the version of Minecraft - jar to use - QString m_minecraftVersion; - - /// Release type - "release" or "snapshot" - QString m_minecraftVersionType; - - /// Assets type - "legacy" or a version ID - MojangAssetIndexInfo::Ptr m_minecraftAssets; - - // Mojang: list of 'downloads' - client jar, server jar, windows server exe, maybe more. - QMap > mojangDownloads; - - /** - * arguments that should be used for launching minecraft - * - * ex: "--username ${auth_player_name} --session ${auth_session} - * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" - */ - QString m_minecraftArguments; - - /// A list of all tweaker classes - QStringList m_tweakers; - - /// The main class to load first - QString m_mainClass; - - /// The applet class, for some very old minecraft releases - QString m_appletClass; - - /// the list of libraries - QList m_libraries; - - /// traits, collected from all the version files (version files can only add) - QSet m_traits; - - /// A list of jar mods. version files can add those. - QList m_jarMods; - - ProblemSeverity m_problemSeverity = PROBLEM_NONE; - - /* - FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. - - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx", - "version": "^10\\.5\\.\\d$" - } - } - ], - "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX - 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" - } - */ - // QList rules; - - /// list of attached profile patches - QList m_patches; - - /// strategy used for profile operations - ProfileStrategy *m_strategy = nullptr; -}; diff --git a/libraries/logic/minecraft/MinecraftVersion.cpp b/libraries/logic/minecraft/MinecraftVersion.cpp deleted file mode 100644 index 1e1d273c..00000000 --- a/libraries/logic/minecraft/MinecraftVersion.cpp +++ /dev/null @@ -1,215 +0,0 @@ -#include "MinecraftVersion.h" -#include "MinecraftProfile.h" -#include "VersionBuildError.h" -#include "ProfileUtils.h" -#include "settings/SettingsObject.h" -#include "minecraft/VersionFilterData.h" - -bool MinecraftVersion::usesLegacyLauncher() -{ - return getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate; -} - - -QString MinecraftVersion::descriptor() -{ - return m_version; -} - -QString MinecraftVersion::name() -{ - return m_version; -} - -QString MinecraftVersion::typeString() const -{ - if(m_type == "snapshot") - { - return QObject::tr("Snapshot"); - } - else if (m_type == "release") - { - return QObject::tr("Regular release"); - } - else if (m_type == "old_alpha") - { - return QObject::tr("Alpha"); - } - else if (m_type == "old_beta") - { - return QObject::tr("Beta"); - } - else - { - return QString(); - } -} - -VersionSource MinecraftVersion::getVersionSource() -{ - return m_versionSource; -} - -bool MinecraftVersion::hasJarMods() -{ - return false; -} - -bool MinecraftVersion::isMinecraftVersion() -{ - return true; -} - -void MinecraftVersion::applyFileTo(MinecraftProfile *profile) -{ - if(m_versionSource == Local && getVersionFile()) - { - getVersionFile()->applyTo(profile); - } - else - { - throw VersionIncomplete(QObject::tr("Can't apply incomplete/builtin Minecraft version %1").arg(m_version)); - } -} - -QString MinecraftVersion::getUrl() const -{ - // legacy fallback - if(m_versionFileURL.isEmpty()) - { - return QString("http://") + URLConstants::AWS_DOWNLOAD_VERSIONS + m_version + "/" + m_version + ".json"; - } - // current - return m_versionFileURL; -} - -VersionFilePtr MinecraftVersion::getVersionFile() -{ - QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_version)); - m_problems.clear(); - if(!versionFile.exists()) - { - if(m_loadedVersionFile) - { - m_loadedVersionFile.reset(); - } - addProblem(PROBLEM_WARNING, QObject::tr("The patch file doesn't exist locally. It's possible it just needs to be downloaded.")); - } - else - { - try - { - if(versionFile.lastModified() != m_loadedVersionFileTimestamp) - { - auto loadedVersionFile = ProfileUtils::parseBinaryJsonFile(versionFile); - loadedVersionFile->name = "Minecraft"; - loadedVersionFile->setCustomizable(true); - m_loadedVersionFileTimestamp = versionFile.lastModified(); - m_loadedVersionFile = loadedVersionFile; - } - } - catch(Exception e) - { - m_loadedVersionFile.reset(); - addProblem(PROBLEM_ERROR, QObject::tr("The patch file couldn't be read:\n%1").arg(e.cause())); - } - } - return m_loadedVersionFile; -} - -bool MinecraftVersion::isCustomizable() -{ - switch(m_versionSource) - { - case Local: - case Remote: - // locally cached file, or a remote file that we can acquire can be customized - return true; - default: - // Everything else is undefined and therefore not customizable. - return false; - } - return false; -} - -const QList &MinecraftVersion::getProblems() -{ - if(getVersionFile()) - { - return getVersionFile()->getProblems(); - } - return ProfilePatch::getProblems(); -} - -ProblemSeverity MinecraftVersion::getProblemSeverity() -{ - if(getVersionFile()) - { - return getVersionFile()->getProblemSeverity(); - } - return ProfilePatch::getProblemSeverity(); -} - -void MinecraftVersion::applyTo(MinecraftProfile *profile) -{ - // do we have this one cached? - if (m_versionSource == Local) - { - applyFileTo(profile); - return; - } - throw VersionIncomplete(QObject::tr("Minecraft version %1 could not be applied: version files are missing.").arg(m_version)); -} - -int MinecraftVersion::getOrder() -{ - return order; -} - -void MinecraftVersion::setOrder(int order) -{ - this->order = order; -} - -QList MinecraftVersion::getJarMods() -{ - return QList(); -} - -QString MinecraftVersion::getName() -{ - return "Minecraft"; -} -QString MinecraftVersion::getVersion() -{ - return m_version; -} -QString MinecraftVersion::getID() -{ - return "net.minecraft"; -} -QString MinecraftVersion::getFilename() -{ - return QString(); -} -QDateTime MinecraftVersion::getReleaseDateTime() -{ - return m_releaseTime; -} - - -bool MinecraftVersion::needsUpdate() -{ - return m_versionSource == Remote || hasUpdate(); -} - -bool MinecraftVersion::hasUpdate() -{ - return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate); -} - -bool MinecraftVersion::isCustom() -{ - // if we add any other source types, this will evaluate to false for them. - return m_versionSource != Local && m_versionSource != Remote; -} diff --git a/libraries/logic/minecraft/MinecraftVersion.h b/libraries/logic/minecraft/MinecraftVersion.h deleted file mode 100644 index b21427d9..00000000 --- a/libraries/logic/minecraft/MinecraftVersion.h +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -#include "BaseVersion.h" -#include "ProfilePatch.h" -#include "VersionFile.h" - -#include "multimc_logic_export.h" - -class MinecraftProfile; -class MinecraftVersion; -typedef std::shared_ptr MinecraftVersionPtr; - -class MULTIMC_LOGIC_EXPORT MinecraftVersion : public BaseVersion, public ProfilePatch -{ -friend class MinecraftVersionList; - -public: /* methods */ - // FIXME: nuke this. - bool usesLegacyLauncher(); - - virtual QString descriptor() override; - virtual QString name() override; - virtual QString typeString() const override; - virtual bool hasJarMods() override; - virtual bool isMinecraftVersion() override; - virtual void applyTo(MinecraftProfile *profile) override; - virtual int getOrder() override; - virtual void setOrder(int order) override; - virtual QList getJarMods() override; - virtual QString getID() override; - virtual QString getVersion() override; - virtual QString getName() override; - virtual QString getFilename() override; - QDateTime getReleaseDateTime() override; - VersionSource getVersionSource() override; - - bool needsUpdate(); - bool hasUpdate(); - virtual bool isCustom() override; - virtual bool isMoveable() override - { - return false; - } - virtual bool isCustomizable() override; - virtual bool isRemovable() override - { - return false; - } - virtual bool isRevertible() override - { - return false; - } - virtual bool isEditable() override - { - return false; - } - virtual bool isVersionChangeable() override - { - return true; - } - - virtual VersionFilePtr getVersionFile() override; - - // virtual QJsonDocument toJson(bool saveOrder) override; - - QString getUrl() const; - - virtual const QList &getProblems() override; - virtual ProblemSeverity getProblemSeverity() override; - -private: /* methods */ - void applyFileTo(MinecraftProfile *profile); - -protected: /* data */ - VersionSource m_versionSource = Remote; - - /// The URL that this version will be downloaded from. - QString m_versionFileURL; - - /// the human readable version name - QString m_version; - - /// The type of this release - QString m_type; - - /// the time this version was actually released by Mojang - QDateTime m_releaseTime; - - /// the time this version was last updated by Mojang - QDateTime m_updateTime; - - /// order of this file... default = -2 - int order = -2; - - /// an update available from Mojang - MinecraftVersionPtr upstreamUpdate; - - QDateTime m_loadedVersionFileTimestamp; - mutable VersionFilePtr m_loadedVersionFile; -}; diff --git a/libraries/logic/minecraft/MinecraftVersionList.cpp b/libraries/logic/minecraft/MinecraftVersionList.cpp deleted file mode 100644 index a5cc3a39..00000000 --- a/libraries/logic/minecraft/MinecraftVersionList.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include "Json.h" -#include -#include - -#include "Env.h" -#include "Exception.h" - -#include "MinecraftVersionList.h" -#include "net/URLConstants.h" - -#include "ParseUtils.h" -#include "ProfileUtils.h" -#include "VersionFilterData.h" -#include "onesix/OneSixVersionFormat.h" -#include "MojangVersionFormat.h" -#include - -static const char * localVersionCache = "versions/versions.dat"; - -class MCVListLoadTask : public Task -{ - Q_OBJECT - -public: - explicit MCVListLoadTask(MinecraftVersionList *vlist); - virtual ~MCVListLoadTask() override{}; - - virtual void executeTask() override; - -protected -slots: - void list_downloaded(); - -protected: - QNetworkReply *vlistReply; - MinecraftVersionList *m_list; - MinecraftVersion *m_currentStable; -}; - -class MCVListVersionUpdateTask : public Task -{ - Q_OBJECT - -public: - explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, std::shared_ptr updatedVersion); - virtual ~MCVListVersionUpdateTask() override{}; - virtual void executeTask() override; - -protected -slots: - void json_downloaded(); - -protected: - NetJobPtr specificVersionDownloadJob; - std::shared_ptr updatedVersion; - MinecraftVersionList *m_list; -}; - -class ListLoadError : public Exception -{ -public: - ListLoadError(QString cause) : Exception(cause) {}; - virtual ~ListLoadError() noexcept - { - } -}; - -MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) -{ - loadCachedList(); -} - -Task *MinecraftVersionList::getLoadTask() -{ - return new MCVListLoadTask(this); -} - -bool MinecraftVersionList::isLoaded() -{ - return m_loaded; -} - -const BaseVersionPtr MinecraftVersionList::at(int i) const -{ - return m_vlist.at(i); -} - -int MinecraftVersionList::count() const -{ - return m_vlist.count(); -} - -static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) -{ - auto left = std::dynamic_pointer_cast(first); - auto right = std::dynamic_pointer_cast(second); - return left->getReleaseDateTime() > right->getReleaseDateTime(); -} - -void MinecraftVersionList::sortInternal() -{ - qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); -} - -void MinecraftVersionList::loadCachedList() -{ - QFile localIndex(localVersionCache); - if (!localIndex.exists()) - { - return; - } - if (!localIndex.open(QIODevice::ReadOnly)) - { - // FIXME: this is actually a very bad thing! How do we deal with this? - qCritical() << "The minecraft version cache can't be read."; - return; - } - auto data = localIndex.readAll(); - try - { - localIndex.close(); - QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(data); - if (jsonDoc.isNull()) - { - throw ListLoadError(tr("Error reading the version list.")); - } - loadList(jsonDoc, Local); - } - catch (Exception &e) - { - // the cache has gone bad for some reason... flush it. - qCritical() << "The minecraft version cache is corrupted. Flushing cache."; - localIndex.remove(); - return; - } - m_hasLocalIndex = true; -} - -void MinecraftVersionList::loadList(QJsonDocument jsonDoc, VersionSource source) -{ - qDebug() << "Loading" << ((source == Remote) ? "remote" : "local") << "version list."; - - if (!jsonDoc.isObject()) - { - throw ListLoadError(tr("Error parsing version list JSON: jsonDoc is not an object")); - } - - QJsonObject root = jsonDoc.object(); - - try - { - QJsonObject latest = Json::requireObject(root.value("latest")); - m_latestReleaseID = Json::requireString(latest.value("release")); - m_latestSnapshotID = Json::requireString(latest.value("snapshot")); - } - catch (Exception &err) - { - qCritical() - << tr("Error parsing version list JSON: couldn't determine latest versions"); - } - - // Now, get the array of versions. - if (!root.value("versions").isArray()) - { - throw ListLoadError(tr("Error parsing version list JSON: version list object is " - "missing 'versions' array")); - } - QJsonArray versions = root.value("versions").toArray(); - - QList tempList; - for (auto version : versions) - { - // Load the version info. - if (!version.isObject()) - { - qCritical() << "Error while parsing version list : invalid JSON structure"; - continue; - } - - QJsonObject versionObj = version.toObject(); - QString versionID = versionObj.value("id").toString(""); - if (versionID.isEmpty()) - { - qCritical() << "Error while parsing version : version ID is missing"; - continue; - } - - if (g_VersionFilterData.legacyBlacklist.contains(versionID)) - { - qWarning() << "Blacklisted legacy version ignored: " << versionID; - continue; - } - - // Now, we construct the version object and add it to the list. - std::shared_ptr mcVersion(new MinecraftVersion()); - mcVersion->m_version = versionID; - - mcVersion->m_releaseTime = timeFromS3Time(versionObj.value("releaseTime").toString("")); - mcVersion->m_updateTime = timeFromS3Time(versionObj.value("time").toString("")); - - // depends on where we load the version from -- network request or local file? - mcVersion->m_versionSource = source; - mcVersion->m_versionFileURL = versionObj.value("url").toString(""); - QString versionTypeStr = versionObj.value("type").toString(""); - if (versionTypeStr.isEmpty()) - { - qCritical() << "Ignoring" << versionID - << "because it doesn't have the version type set."; - continue; - } - // OneSix or Legacy. use filter to determine type - if (versionTypeStr == "release") - { - } - else if (versionTypeStr == "snapshot") // It's a snapshot... yay - { - } - else if (versionTypeStr == "old_alpha") - { - } - else if (versionTypeStr == "old_beta") - { - } - else - { - qCritical() << "Ignoring" << versionID - << "because it has an invalid version type."; - continue; - } - mcVersion->m_type = versionTypeStr; - qDebug() << "Loaded version" << versionID << "from" - << ((source == Remote) ? "remote" : "local") << "version list."; - tempList.append(mcVersion); - } - updateListData(tempList); - if(source == Remote) - { - m_loaded = true; - } -} - -void MinecraftVersionList::sortVersions() -{ - beginResetModel(); - sortInternal(); - endResetModel(); -} - -QVariant MinecraftVersionList::data(const QModelIndex& index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(m_vlist[index.row()]); - - case VersionRole: - return version->name(); - - case VersionIdRole: - return version->descriptor(); - - case RecommendedRole: - return version->descriptor() == m_latestReleaseID; - - case LatestRole: - { - if(version->descriptor() != m_latestSnapshotID) - return false; - MinecraftVersionPtr latestRelease = std::dynamic_pointer_cast(getLatestStable()); - /* - if(latestRelease && latestRelease->m_releaseTime > version->m_releaseTime) - { - return false; - } - */ - return true; - } - - case TypeRole: - return version->typeString(); - - default: - return QVariant(); - } -} - -BaseVersionList::RoleList MinecraftVersionList::providesRoles() const -{ - return {VersionPointerRole, VersionRole, VersionIdRole, RecommendedRole, LatestRole, TypeRole}; -} - -BaseVersionPtr MinecraftVersionList::getLatestStable() const -{ - if(m_lookup.contains(m_latestReleaseID)) - return m_lookup[m_latestReleaseID]; - return BaseVersionPtr(); -} - -BaseVersionPtr MinecraftVersionList::getRecommended() const -{ - return getLatestStable(); -} - -void MinecraftVersionList::updateListData(QList versions) -{ - beginResetModel(); - for (auto version : versions) - { - auto descr = version->descriptor(); - - if (!m_lookup.contains(descr)) - { - m_lookup[version->descriptor()] = version; - m_vlist.append(version); - continue; - } - auto orig = std::dynamic_pointer_cast(m_lookup[descr]); - auto added = std::dynamic_pointer_cast(version); - // updateListData is called after Mojang list loads. those can be local or remote - // remote comes always after local - // any other options are ignored - if (orig->m_versionSource != Local || added->m_versionSource != Remote) - { - continue; - } - // alright, it's an update. put it inside the original, for further processing. - orig->upstreamUpdate = added; - } - sortInternal(); - endResetModel(); -} - -MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist) -{ - m_list = vlist; - m_currentStable = NULL; - vlistReply = nullptr; -} - -void MCVListLoadTask::executeTask() -{ - setStatus(tr("Loading instance version list...")); - auto worker = ENV.qnam(); - vlistReply = worker->get(QNetworkRequest(QUrl("https://launchermeta.mojang.com/mc/game/version_manifest.json"))); - connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); -} - -void MCVListLoadTask::list_downloaded() -{ - if (vlistReply->error() != QNetworkReply::NoError) - { - vlistReply->deleteLater(); - emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString()); - return; - } - - auto data = vlistReply->readAll(); - vlistReply->deleteLater(); - try - { - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - if (jsonError.error != QJsonParseError::NoError) - { - throw ListLoadError( - tr("Error parsing version list JSON: %1").arg(jsonError.errorString())); - } - m_list->loadList(jsonDoc, Remote); - } - catch (Exception &e) - { - emitFailed(e.cause()); - return; - } - - emitSucceeded(); - return; -} - -MCVListVersionUpdateTask::MCVListVersionUpdateTask(MinecraftVersionList *vlist, std::shared_ptr updatedVersion) - : Task() -{ - m_list = vlist; - this->updatedVersion = updatedVersion; -} - -void MCVListVersionUpdateTask::executeTask() -{ - auto job = new NetJob("Version index"); - job->addNetAction(ByteArrayDownload::make(QUrl(updatedVersion->getUrl()))); - specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded())); - connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString))); - connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - specificVersionDownloadJob->start(); -} - -void MCVListVersionUpdateTask::json_downloaded() -{ - NetActionPtr DlJob = specificVersionDownloadJob->first(); - auto data = std::dynamic_pointer_cast(DlJob)->m_data; - specificVersionDownloadJob.reset(); - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed(tr("The download version file is not valid.")); - return; - } - VersionFilePtr file; - try - { - file = MojangVersionFormat::versionFileFromJson(jsonDoc, "net.minecraft.json"); - } - catch (Exception &e) - { - emitFailed(tr("Couldn't process version file: %1").arg(e.cause())); - return; - } - - // Strip LWJGL from the version file. We use our own. - ProfileUtils::removeLwjglFromPatch(file); - - file->fileId = "net.minecraft"; - - // now dump the file to disk - auto doc = OneSixVersionFormat::versionFileToJson(file, false); - auto newdata = doc.toBinaryData(); - auto id = updatedVersion->descriptor(); - QString targetPath = "versions/" + id + "/" + id + ".dat"; - FS::ensureFilePathExists(targetPath); - QSaveFile vfile1(targetPath); - if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) - { - emitFailed(tr("Can't open %1 for writing.").arg(targetPath)); - return; - } - qint64 actual = 0; - if ((actual = vfile1.write(newdata)) != newdata.size()) - { - emitFailed(tr("Failed to write into %1. Written %2 out of %3.") - .arg(targetPath) - .arg(actual) - .arg(newdata.size())); - return; - } - if (!vfile1.commit()) - { - emitFailed(tr("Can't commit changes to %1").arg(targetPath)); - return; - } - - m_list->finalizeUpdate(id); - emitSucceeded(); -} - -std::shared_ptr MinecraftVersionList::createUpdateTask(QString version) -{ - auto iter = m_lookup.find(version); - if(iter == m_lookup.end()) - return nullptr; - - auto mcversion = std::dynamic_pointer_cast(*iter); - if(!mcversion) - { - return nullptr; - } - - return std::shared_ptr(new MCVListVersionUpdateTask(this, mcversion)); -} - -void MinecraftVersionList::saveCachedList() -{ - // FIXME: throw. - if (!FS::ensureFilePathExists(localVersionCache)) - return; - QSaveFile tfile(localVersionCache); - if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) - return; - QJsonObject toplevel; - QJsonArray entriesArr; - for (auto version : m_vlist) - { - auto mcversion = std::dynamic_pointer_cast(version); - // do not save the remote versions. - if (mcversion->m_versionSource != Local) - continue; - QJsonObject entryObj; - - entryObj.insert("id", mcversion->descriptor()); - entryObj.insert("version", mcversion->descriptor()); - entryObj.insert("time", timeToS3Time(mcversion->m_updateTime)); - entryObj.insert("releaseTime", timeToS3Time(mcversion->m_releaseTime)); - entryObj.insert("url", mcversion->m_versionFileURL); - entryObj.insert("type", mcversion->m_type); - entriesArr.append(entryObj); - } - toplevel.insert("versions", entriesArr); - - { - bool someLatest = false; - QJsonObject latestObj; - if(!m_latestReleaseID.isNull()) - { - latestObj.insert("release", m_latestReleaseID); - someLatest = true; - } - if(!m_latestSnapshotID.isNull()) - { - latestObj.insert("snapshot", m_latestSnapshotID); - someLatest = true; - } - if(someLatest) - { - toplevel.insert("latest", latestObj); - } - } - - QJsonDocument doc(toplevel); - QByteArray jsonData = doc.toBinaryData(); - qint64 result = tfile.write(jsonData); - if (result == -1) - return; - if (result != jsonData.size()) - return; - tfile.commit(); -} - -void MinecraftVersionList::finalizeUpdate(QString version) -{ - int idx = -1; - for (int i = 0; i < m_vlist.size(); i++) - { - if (version == m_vlist[i]->descriptor()) - { - idx = i; - break; - } - } - if (idx == -1) - { - return; - } - - auto updatedVersion = std::dynamic_pointer_cast(m_vlist[idx]); - - // if we have an update for the version, replace it, make the update local - if (updatedVersion->upstreamUpdate) - { - auto updatedWith = updatedVersion->upstreamUpdate; - updatedWith->m_versionSource = Local; - m_vlist[idx] = updatedWith; - m_lookup[version] = updatedWith; - } - else - { - // otherwise, just set the version as local; - updatedVersion->m_versionSource = Local; - } - - dataChanged(index(idx), index(idx)); - - saveCachedList(); -} - -#include "MinecraftVersionList.moc" diff --git a/libraries/logic/minecraft/MinecraftVersionList.h b/libraries/logic/minecraft/MinecraftVersionList.h deleted file mode 100644 index 0fca02a7..00000000 --- a/libraries/logic/minecraft/MinecraftVersionList.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include - -#include "BaseVersionList.h" -#include "tasks/Task.h" -#include "minecraft/MinecraftVersion.h" -#include - -#include "multimc_logic_export.h" - -class MCVListLoadTask; -class MCVListVersionUpdateTask; - -class MULTIMC_LOGIC_EXPORT MinecraftVersionList : public BaseVersionList -{ - Q_OBJECT -private: - void sortInternal(); - void loadList(QJsonDocument jsonDoc, VersionSource source); - void loadCachedList(); - void saveCachedList(); - void finalizeUpdate(QString version); -public: - friend class MCVListLoadTask; - friend class MCVListVersionUpdateTask; - - explicit MinecraftVersionList(QObject *parent = 0); - - std::shared_ptr createUpdateTask(QString version); - - virtual Task *getLoadTask() override; - virtual bool isLoaded() override; - virtual const BaseVersionPtr at(int i) const override; - virtual int count() const override; - virtual void sortVersions() override; - virtual QVariant data(const QModelIndex & index, int role) const override; - virtual RoleList providesRoles() const override; - - virtual BaseVersionPtr getLatestStable() const override; - virtual BaseVersionPtr getRecommended() const override; - -protected: - QList m_vlist; - QMap m_lookup; - - bool m_loaded = false; - bool m_hasLocalIndex = false; - QString m_latestReleaseID = "INVALID"; - QString m_latestSnapshotID = "INVALID"; - -protected -slots: - virtual void updateListData(QList versions) override; -}; diff --git a/libraries/logic/minecraft/Mod.cpp b/libraries/logic/minecraft/Mod.cpp deleted file mode 100644 index 9b9f76f9..00000000 --- a/libraries/logic/minecraft/Mod.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Mod.h" -#include "settings/INIFile.h" -#include -#include - -Mod::Mod(const QFileInfo &file) -{ - repath(file); -} - -void Mod::repath(const QFileInfo &file) -{ - m_file = file; - QString name_base = file.fileName(); - - m_type = Mod::MOD_UNKNOWN; - - if (m_file.isDir()) - { - m_type = MOD_FOLDER; - m_name = name_base; - m_mmc_id = name_base; - } - else if (m_file.isFile()) - { - if (name_base.endsWith(".disabled")) - { - m_enabled = false; - name_base.chop(9); - } - else - { - m_enabled = true; - } - m_mmc_id = name_base; - if (name_base.endsWith(".zip") || name_base.endsWith(".jar")) - { - m_type = MOD_ZIPFILE; - name_base.chop(4); - } - else if (name_base.endsWith(".litemod")) - { - m_type = MOD_LITEMOD; - name_base.chop(8); - } - else - { - m_type = MOD_SINGLEFILE; - } - m_name = name_base; - } - - if (m_type == MOD_ZIPFILE) - { - QuaZip zip(m_file.filePath()); - if (!zip.open(QuaZip::mdUnzip)) - return; - - QuaZipFile file(&zip); - - if (zip.setCurrentFile("mcmod.info")) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - return; - } - - ReadMCModInfo(file.readAll()); - file.close(); - zip.close(); - return; - } - else if (zip.setCurrentFile("forgeversion.properties")) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - return; - } - - ReadForgeInfo(file.readAll()); - file.close(); - zip.close(); - return; - } - - zip.close(); - } - else if (m_type == MOD_FOLDER) - { - QFileInfo mcmod_info(FS::PathCombine(m_file.filePath(), "mcmod.info")); - if (mcmod_info.isFile()) - { - QFile mcmod(mcmod_info.filePath()); - if (!mcmod.open(QIODevice::ReadOnly)) - return; - auto data = mcmod.readAll(); - if (data.isEmpty() || data.isNull()) - return; - ReadMCModInfo(data); - } - } - else if (m_type == MOD_LITEMOD) - { - QuaZip zip(m_file.filePath()); - if (!zip.open(QuaZip::mdUnzip)) - return; - - QuaZipFile file(&zip); - - if (zip.setCurrentFile("litemod.json")) - { - if (!file.open(QIODevice::ReadOnly)) - { - zip.close(); - return; - } - - ReadLiteModInfo(file.readAll()); - file.close(); - } - zip.close(); - } -} - -// NEW format -// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3 - -// OLD format: -// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc -void Mod::ReadMCModInfo(QByteArray contents) -{ - auto getInfoFromArray = [&](QJsonArray arr)->void - { - if (!arr.at(0).isObject()) - return; - auto firstObj = arr.at(0).toObject(); - m_mod_id = firstObj.value("modid").toString(); - m_name = firstObj.value("name").toString(); - m_version = firstObj.value("version").toString(); - m_homeurl = firstObj.value("url").toString(); - m_updateurl = firstObj.value("updateUrl").toString(); - m_homeurl = m_homeurl.trimmed(); - if(!m_homeurl.isEmpty()) - { - // fix up url. - if (!m_homeurl.startsWith("http://") && !m_homeurl.startsWith("https://") && - !m_homeurl.startsWith("ftp://")) - { - m_homeurl.prepend("http://"); - } - } - m_description = firstObj.value("description").toString(); - QJsonArray authors = firstObj.value("authorList").toArray(); - if (authors.size() == 0) - authors = firstObj.value("authors").toArray(); - - if (authors.size() == 0) - m_authors = ""; - else if (authors.size() >= 1) - { - m_authors = authors.at(0).toString(); - for (int i = 1; i < authors.size(); i++) - { - m_authors += ", " + authors.at(i).toString(); - } - } - m_credits = firstObj.value("credits").toString(); - return; - } - ; - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); - // this is the very old format that had just the array - if (jsonDoc.isArray()) - { - getInfoFromArray(jsonDoc.array()); - } - else if (jsonDoc.isObject()) - { - auto val = jsonDoc.object().value("modinfoversion"); - if(val.isUndefined()) - val = jsonDoc.object().value("modListVersion"); - int version = val.toDouble(); - if (version != 2) - { - qCritical() << "BAD stuff happened to mod json:"; - qCritical() << contents; - return; - } - auto arrVal = jsonDoc.object().value("modlist"); - if(arrVal.isUndefined()) - arrVal = jsonDoc.object().value("modList"); - if (arrVal.isArray()) - { - getInfoFromArray(arrVal.toArray()); - } - } -} - -void Mod::ReadForgeInfo(QByteArray contents) -{ - // Read the data - m_name = "Minecraft Forge"; - m_mod_id = "Forge"; - m_homeurl = "http://www.minecraftforge.net/forum/"; - INIFile ini; - if (!ini.loadFile(contents)) - return; - - QString major = ini.get("forge.major.number", "0").toString(); - QString minor = ini.get("forge.minor.number", "0").toString(); - QString revision = ini.get("forge.revision.number", "0").toString(); - QString build = ini.get("forge.build.number", "0").toString(); - - m_version = major + "." + minor + "." + revision + "." + build; -} - -void Mod::ReadLiteModInfo(QByteArray contents) -{ - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); - auto object = jsonDoc.object(); - if (object.contains("name")) - { - m_mod_id = m_name = object.value("name").toString(); - } - if (object.contains("version")) - { - m_version = object.value("version").toString(""); - } - else - { - m_version = object.value("revision").toString(""); - } - m_mcversion = object.value("mcversion").toString(); - m_authors = object.value("author").toString(); - m_description = object.value("description").toString(); - m_homeurl = object.value("url").toString(); -} - -bool Mod::replace(Mod &with) -{ - if (!destroy()) - return false; - bool success = false; - auto t = with.type(); - - if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE || t == MOD_LITEMOD) - { - qDebug() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath(); - success = QFile::copy(with.m_file.filePath(), m_file.filePath()); - } - if (t == MOD_FOLDER) - { - success = FS::copy(with.m_file.filePath(), m_file.path())(); - } - if (success) - { - m_name = with.m_name; - m_mmc_id = with.m_mmc_id; - m_mod_id = with.m_mod_id; - m_version = with.m_version; - m_mcversion = with.m_mcversion; - m_description = with.m_description; - m_authors = with.m_authors; - m_credits = with.m_credits; - m_homeurl = with.m_homeurl; - m_type = with.m_type; - m_file.refresh(); - } - return success; -} - -bool Mod::destroy() -{ - if (m_type == MOD_FOLDER) - { - QDir d(m_file.filePath()); - if (d.removeRecursively()) - { - m_type = MOD_UNKNOWN; - return true; - } - return false; - } - else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD) - { - QFile f(m_file.filePath()); - if (f.remove()) - { - m_type = MOD_UNKNOWN; - return true; - } - return false; - } - return true; -} - -QString Mod::version() const -{ - switch (type()) - { - case MOD_ZIPFILE: - case MOD_LITEMOD: - return m_version; - case MOD_FOLDER: - return "Folder"; - case MOD_SINGLEFILE: - return "File"; - default: - return "VOID"; - } -} - -bool Mod::enable(bool value) -{ - if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) - return false; - - if (m_enabled == value) - return false; - - QString path = m_file.absoluteFilePath(); - if (value) - { - QFile foo(path); - if (!path.endsWith(".disabled")) - return false; - path.chop(9); - if (!foo.rename(path)) - return false; - } - else - { - QFile foo(path); - path += ".disabled"; - if (!foo.rename(path)) - return false; - } - m_file = QFileInfo(path); - m_enabled = value; - return true; -} -bool Mod::operator==(const Mod &other) const -{ - return mmc_id() == other.mmc_id(); -} -bool Mod::strongCompare(const Mod &other) const -{ - return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type(); -} diff --git a/libraries/logic/minecraft/Mod.h b/libraries/logic/minecraft/Mod.h deleted file mode 100644 index 19f4c740..00000000 --- a/libraries/logic/minecraft/Mod.h +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once -#include - -class Mod -{ -public: - enum ModType - { - MOD_UNKNOWN, //!< Indicates an unspecified mod type. - MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files. - MOD_SINGLEFILE, //!< The mod is a single file (not a zip file). - MOD_FOLDER, //!< The mod is in a folder on the filesystem. - MOD_LITEMOD, //!< The mod is a litemod - }; - - Mod(const QFileInfo &file); - - QFileInfo filename() const - { - return m_file; - } - QString mmc_id() const - { - return m_mmc_id; - } - QString mod_id() const - { - return m_mod_id; - } - ModType type() const - { - return m_type; - } - QString mcversion() const - { - return m_mcversion; - } - ; - bool valid() - { - return m_type != MOD_UNKNOWN; - } - QString name() const - { - if(m_name.trimmed().isEmpty()) - { - return m_mmc_id; - } - return m_name; - } - - QString version() const; - - QString homeurl() const - { - return m_homeurl; - } - - QString description() const - { - return m_description; - } - - QString authors() const - { - return m_authors; - } - - QString credits() const - { - return m_credits; - } - - bool enabled() const - { - return m_enabled; - } - - bool enable(bool value); - - // delete all the files of this mod - bool destroy(); - // replace this mod with a copy of the other - bool replace(Mod &with); - // change the mod's filesystem path (used by mod lists for *MAGIC* purposes) - void repath(const QFileInfo &file); - - // WEAK compare operator - used for replacing mods - bool operator==(const Mod &other) const; - bool strongCompare(const Mod &other) const; - -private: - void ReadMCModInfo(QByteArray contents); - void ReadForgeInfo(QByteArray contents); - void ReadLiteModInfo(QByteArray contents); - -protected: - - // FIXME: what do do with those? HMM... - /* - void ReadModInfoData(QString info); - void ReadForgeInfoData(QString infoFileData); - */ - - QFileInfo m_file; - QString m_mmc_id; - QString m_mod_id; - bool m_enabled = true; - QString m_name; - QString m_version; - QString m_mcversion; - QString m_homeurl; - QString m_updateurl; - QString m_description; - QString m_authors; - QString m_credits; - - ModType m_type; -}; diff --git a/libraries/logic/minecraft/ModList.cpp b/libraries/logic/minecraft/ModList.cpp deleted file mode 100644 index d9ed4886..00000000 --- a/libraries/logic/minecraft/ModList.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "ModList.h" -#include -#include -#include -#include -#include -#include -#include - -ModList::ModList(const QString &dir, const QString &list_file) - : QAbstractListModel(), m_dir(dir), m_list_file(list_file) -{ - FS::ensureFolderPathExists(m_dir.absolutePath()); - m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | - QDir::NoSymLinks); - m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); - m_list_id = QUuid::createUuid().toString(); - m_watcher = new QFileSystemWatcher(this); - is_watching = false; - connect(m_watcher, SIGNAL(directoryChanged(QString)), this, - SLOT(directoryChanged(QString))); -} - -void ModList::startWatching() -{ - update(); - is_watching = m_watcher->addPath(m_dir.absolutePath()); - if (is_watching) - { - qDebug() << "Started watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to start watching " << m_dir.absolutePath(); - } -} - -void ModList::stopWatching() -{ - is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) - { - qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); - } -} - -void ModList::internalSort(QList &what) -{ - auto predicate = [](const Mod &left, const Mod &right) - { - if (left.name() == right.name()) - { - return left.mmc_id().localeAwareCompare(right.mmc_id()) < 0; - } - return left.name().localeAwareCompare(right.name()) < 0; - }; - std::sort(what.begin(), what.end(), predicate); -} - -bool ModList::update() -{ - if (!isValid()) - return false; - - QList orderedMods; - QList newMods; - m_dir.refresh(); - auto folderContents = m_dir.entryInfoList(); - bool orderOrStateChanged = false; - - // first, process the ordered items (if any) - OrderList listOrder = readListFile(); - for (auto item : listOrder) - { - QFileInfo infoEnabled(m_dir.filePath(item.id)); - QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled")); - int idxEnabled = folderContents.indexOf(infoEnabled); - int idxDisabled = folderContents.indexOf(infoDisabled); - bool isEnabled; - // if both enabled and disabled versions are present, it's a special case... - if (idxEnabled >= 0 && idxDisabled >= 0) - { - // we only process the one we actually have in the order file. - // and exactly as we have it. - // THIS IS A CORNER CASE - isEnabled = item.enabled; - } - else - { - // only one is present. - // we pick the one that we found. - // we assume the mod was enabled/disabled by external means - isEnabled = idxEnabled >= 0; - } - int idx = isEnabled ? idxEnabled : idxDisabled; - QFileInfo &info = isEnabled ? infoEnabled : infoDisabled; - // if the file from the index file exists - if (idx != -1) - { - // remove from the actual folder contents list - folderContents.takeAt(idx); - // append the new mod - orderedMods.append(Mod(info)); - if (isEnabled != item.enabled) - orderOrStateChanged = true; - } - else - { - orderOrStateChanged = true; - } - } - // if there are any untracked files... - if (folderContents.size()) - { - // the order surely changed! - for (auto entry : folderContents) - { - newMods.append(Mod(entry)); - } - internalSort(newMods); - orderedMods.append(newMods); - orderOrStateChanged = true; - } - // otherwise, if we were already tracking some mods - else if (mods.size()) - { - // if the number doesn't match, order changed. - if (mods.size() != orderedMods.size()) - orderOrStateChanged = true; - // if it does match, compare the mods themselves - else - for (int i = 0; i < mods.size(); i++) - { - if (!mods[i].strongCompare(orderedMods[i])) - { - orderOrStateChanged = true; - break; - } - } - } - beginResetModel(); - mods.swap(orderedMods); - endResetModel(); - if (orderOrStateChanged && !m_list_file.isEmpty()) - { - qDebug() << "Mod list " << m_list_file << " changed!"; - saveListFile(); - emit changed(); - } - return true; -} - -void ModList::directoryChanged(QString path) -{ - update(); -} - -ModList::OrderList ModList::readListFile() -{ - OrderList itemList; - if (m_list_file.isNull() || m_list_file.isEmpty()) - return itemList; - - QFile textFile(m_list_file); - if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) - return OrderList(); - - QTextStream textStream; - textStream.setAutoDetectUnicode(true); - textStream.setDevice(&textFile); - while (true) - { - QString line = textStream.readLine(); - if (line.isNull() || line.isEmpty()) - break; - else - { - OrderItem it; - it.enabled = !line.endsWith(".disabled"); - if (!it.enabled) - { - line.chop(9); - } - it.id = line; - itemList.append(it); - } - } - textFile.close(); - return itemList; -} - -bool ModList::saveListFile() -{ - if (m_list_file.isNull() || m_list_file.isEmpty()) - return false; - QFile textFile(m_list_file); - if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) - return false; - QTextStream textStream; - textStream.setGenerateByteOrderMark(true); - textStream.setCodec("UTF-8"); - textStream.setDevice(&textFile); - for (auto mod : mods) - { - textStream << mod.mmc_id(); - if (!mod.enabled()) - textStream << ".disabled"; - textStream << endl; - } - textFile.close(); - return false; -} - -bool ModList::isValid() -{ - return m_dir.exists() && m_dir.isReadable(); -} - -bool ModList::installMod(const QString &filename, int index) -{ - // NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName - QFileInfo fileinfo(FS::NormalizePath(filename)); - - qDebug() << "installing: " << fileinfo.absoluteFilePath(); - - if (!fileinfo.exists() || !fileinfo.isReadable() || index < 0) - { - return false; - } - Mod m(fileinfo); - if (!m.valid()) - return false; - - // if it's already there, replace the original mod (in place) - int idx = mods.indexOf(m); - if (idx != -1) - { - int idx2 = mods.indexOf(m, idx + 1); - if (idx2 != -1) - return false; - if (mods[idx].replace(m)) - { - - auto left = this->index(index); - auto right = this->index(index, columnCount(QModelIndex()) - 1); - emit dataChanged(left, right); - saveListFile(); - update(); - return true; - } - return false; - } - - auto type = m.type(); - if (type == Mod::MOD_UNKNOWN) - return false; - if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD) - { - QString newpath = FS::PathCombine(m_dir.path(), fileinfo.fileName()); - if (!QFile::copy(fileinfo.filePath(), newpath)) - return false; - m.repath(newpath); - beginInsertRows(QModelIndex(), index, index); - mods.insert(index, m); - endInsertRows(); - saveListFile(); - update(); - return true; - } - else if (type == Mod::MOD_FOLDER) - { - - QString from = fileinfo.filePath(); - QString to = FS::PathCombine(m_dir.path(), fileinfo.fileName()); - if (!FS::copy(from, to)()) - return false; - m.repath(to); - beginInsertRows(QModelIndex(), index, index); - mods.insert(index, m); - endInsertRows(); - saveListFile(); - update(); - return true; - } - return false; -} - -bool ModList::deleteMod(int index) -{ - if (index >= mods.size() || index < 0) - return false; - Mod &m = mods[index]; - if (m.destroy()) - { - beginRemoveRows(QModelIndex(), index, index); - mods.removeAt(index); - endRemoveRows(); - saveListFile(); - emit changed(); - return true; - } - return false; -} - -bool ModList::deleteMods(int first, int last) -{ - for (int i = first; i <= last; i++) - { - Mod &m = mods[i]; - m.destroy(); - } - beginRemoveRows(QModelIndex(), first, last); - mods.erase(mods.begin() + first, mods.begin() + last + 1); - endRemoveRows(); - saveListFile(); - emit changed(); - return true; -} - -bool ModList::moveModTo(int from, int to) -{ - if (from < 0 || from >= mods.size()) - return false; - if (to >= rowCount()) - to = rowCount() - 1; - if (to == -1) - to = rowCount() - 1; - if (from == to) - return false; - int togap = to > from ? to + 1 : to; - beginMoveRows(QModelIndex(), from, from, QModelIndex(), togap); - mods.move(from, to); - endMoveRows(); - saveListFile(); - emit changed(); - return true; -} - -bool ModList::moveModUp(int from) -{ - if (from > 0) - return moveModTo(from, from - 1); - return false; -} - -bool ModList::moveModsUp(int first, int last) -{ - if (first == 0) - return false; - - beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1); - mods.move(first - 1, last); - endMoveRows(); - saveListFile(); - emit changed(); - return true; -} - -bool ModList::moveModDown(int from) -{ - if (from < 0) - return false; - if (from < mods.size() - 1) - return moveModTo(from, from + 1); - return false; -} - -bool ModList::moveModsDown(int first, int last) -{ - if (last == mods.size() - 1) - return false; - - beginMoveRows(QModelIndex(), first, last, QModelIndex(), last + 2); - mods.move(last + 1, first); - endMoveRows(); - saveListFile(); - emit changed(); - return true; -} - -int ModList::columnCount(const QModelIndex &parent) const -{ - return 3; -} - -QVariant ModList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= mods.size()) - return QVariant(); - - switch (role) - { - case Qt::DisplayRole: - switch (column) - { - case NameColumn: - return mods[row].name(); - case VersionColumn: - return mods[row].version(); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - return mods[row].mmc_id(); - - case Qt::CheckStateRole: - switch (column) - { - case ActiveColumn: - return mods[row].enabled() ? Qt::Checked : Qt::Unchecked; - default: - return QVariant(); - } - default: - return QVariant(); - } -} - -bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) - { - return false; - } - - if (role == Qt::CheckStateRole) - { - auto &mod = mods[index.row()]; - if (mod.enable(!mod.enabled())) - { - emit dataChanged(index, index); - return true; - } - } - return false; -} - -QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ActiveColumn: - return QString(); - case NameColumn: - return tr("Name"); - case VersionColumn: - return tr("Version"); - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case ActiveColumn: - return tr("Is the mod enabled?"); - case NameColumn: - return tr("The name of the mod."); - case VersionColumn: - return tr("The version of the mod."); - default: - return QVariant(); - } - default: - return QVariant(); - } - return QVariant(); -} - -Qt::ItemFlags ModList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | - defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; -} - -QStringList ModList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - types << "text/plain"; - return types; -} - -Qt::DropActions ModList::supportedDropActions() const -{ - // copy from outside, move from within and other mod lists - return Qt::CopyAction | Qt::MoveAction; -} - -Qt::DropActions ModList::supportedDragActions() const -{ - // move to other mod lists or VOID - return Qt::MoveAction; -} - -QMimeData *ModList::mimeData(const QModelIndexList &indexes) const -{ - QMimeData *data = new QMimeData(); - - if (indexes.size() == 0) - return data; - - auto idx = indexes[0]; - int row = idx.row(); - if (row < 0 || row >= mods.size()) - return data; - - QStringList params; - params << m_list_id << QString::number(row); - data->setText(params.join('|')); - return data; -} - -bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent) -{ - if (action == Qt::IgnoreAction) - return true; - // check if the action is supported - if (!data || !(action & supportedDropActions())) - return false; - if (parent.isValid()) - { - row = parent.row(); - column = parent.column(); - } - - if (row > rowCount()) - row = rowCount(); - if (row == -1) - row = rowCount(); - if (column == -1) - column = 0; - qDebug() << "Drop row: " << row << " column: " << column; - - // files dropped from outside? - if (data->hasUrls()) - { - bool was_watching = is_watching; - if (was_watching) - stopWatching(); - auto urls = data->urls(); - for (auto url : urls) - { - // only local files may be dropped... - if (!url.isLocalFile()) - continue; - QString filename = url.toLocalFile(); - installMod(filename, row); - // if there is no ordering, re-sort the list - if (m_list_file.isEmpty()) - { - beginResetModel(); - internalSort(mods); - endResetModel(); - } - } - if (was_watching) - startWatching(); - return true; - } - else if (data->hasText()) - { - QString sourcestr = data->text(); - auto list = sourcestr.split('|'); - if (list.size() != 2) - return false; - QString remoteId = list[0]; - int remoteIndex = list[1].toInt(); - qDebug() << "move: " << sourcestr; - // no moving of things between two lists - if (remoteId != m_list_id) - return false; - // no point moving to the same place... - if (row == remoteIndex) - return false; - // otherwise, move the mod :D - moveModTo(remoteIndex, row); - return true; - } - return false; -} diff --git a/libraries/logic/minecraft/ModList.h b/libraries/logic/minecraft/ModList.h deleted file mode 100644 index 05ada8ee..00000000 --- a/libraries/logic/minecraft/ModList.h +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include -#include - -#include "minecraft/Mod.h" - -#include "multimc_logic_export.h" - -class LegacyInstance; -class BaseInstance; -class QFileSystemWatcher; - -/** - * A legacy mod list. - * Backed by a folder. - */ -class MULTIMC_LOGIC_EXPORT ModList : public QAbstractListModel -{ - Q_OBJECT -public: - enum Columns - { - ActiveColumn = 0, - NameColumn, - VersionColumn - }; - ModList(const QString &dir, const QString &list_file = QString()); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole); - - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const - { - return size(); - } - ; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex &parent) const; - - size_t size() const - { - return mods.size(); - } - ; - bool empty() const - { - return size() == 0; - } - Mod &operator[](size_t index) - { - return mods[index]; - } - - /// Reloads the mod list and returns true if the list changed. - virtual bool update(); - - /** - * Adds the given mod to the list at the given index - if the list supports custom ordering - */ - virtual bool installMod(const QString & filename, int index = 0); - - /// Deletes the mod at the given index. - virtual bool deleteMod(int index); - - /// Deletes all the selected mods - virtual bool deleteMods(int first, int last); - - /** - * move the mod at index to the position N - * 0 is the beginning of the list, length() is the end of the list. - */ - virtual bool moveModTo(int from, int to); - - /** - * move the mod at index one position upwards - */ - virtual bool moveModUp(int from); - virtual bool moveModsUp(int first, int last); - - /** - * move the mod at index one position downwards - */ - virtual bool moveModDown(int from); - virtual bool moveModsDown(int first, int last); - - /// flags, mostly to support drag&drop - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - /// get data for drag action - virtual QMimeData *mimeData(const QModelIndexList &indexes) const; - /// get the supported mime types - virtual QStringList mimeTypes() const; - /// process data from drop action - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent); - /// what drag actions do we support? - virtual Qt::DropActions supportedDragActions() const; - - /// what drop actions do we support? - virtual Qt::DropActions supportedDropActions() const; - - void startWatching(); - void stopWatching(); - - virtual bool isValid(); - - QDir dir() - { - return m_dir; - } - - const QList & allMods() - { - return mods; - } - -private: - void internalSort(QList & what); - struct OrderItem - { - QString id; - bool enabled = false; - }; - typedef QList OrderList; - OrderList readListFile(); - bool saveListFile(); -private -slots: - void directoryChanged(QString path); - -signals: - void changed(); - -protected: - QFileSystemWatcher *m_watcher; - bool is_watching; - QDir m_dir; - QString m_list_file; - QString m_list_id; - QList mods; -}; diff --git a/libraries/logic/minecraft/MojangDownloadInfo.h b/libraries/logic/minecraft/MojangDownloadInfo.h deleted file mode 100644 index 1f3306e0..00000000 --- a/libraries/logic/minecraft/MojangDownloadInfo.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once -#include -#include -#include - -struct MojangDownloadInfo -{ - // types - typedef std::shared_ptr Ptr; - - // data - /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested! - QString path; - /// absolute URL of this file - QString url; - /// sha-1 checksum of the file - QString sha1; - /// size of the file in bytes - int size; -}; - - - -struct MojangLibraryDownloadInfo -{ - MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {}; - MojangLibraryDownloadInfo() {}; - - // types - typedef std::shared_ptr Ptr; - - // methods - MojangDownloadInfo *getDownloadInfo(QString classifier) - { - if (classifier.isNull()) - { - return artifact.get(); - } - - return classifiers[classifier].get(); - } - - // data - MojangDownloadInfo::Ptr artifact; - QMap classifiers; -}; - - - -struct MojangAssetIndexInfo : public MojangDownloadInfo -{ - // types - typedef std::shared_ptr Ptr; - - // methods - MojangAssetIndexInfo() - { - } - - MojangAssetIndexInfo(QString id) - { - this->id = id; - url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id + ".json"; - known = false; - } - - // data - int totalSize; - QString id; - bool known = true; -}; diff --git a/libraries/logic/minecraft/MojangVersionFormat.cpp b/libraries/logic/minecraft/MojangVersionFormat.cpp deleted file mode 100644 index 34129c9e..00000000 --- a/libraries/logic/minecraft/MojangVersionFormat.cpp +++ /dev/null @@ -1,381 +0,0 @@ -#include "MojangVersionFormat.h" -#include "onesix/OneSixVersionFormat.h" -#include "MinecraftVersion.h" -#include "VersionBuildError.h" -#include "MojangDownloadInfo.h" - -#include "Json.h" -using namespace Json; -#include "ParseUtils.h" - -static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18; - -static MojangAssetIndexInfo::Ptr assetIndexFromJson (const QJsonObject &obj); -static MojangDownloadInfo::Ptr downloadInfoFromJson (const QJsonObject &obj); -static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson (const QJsonObject &libObj); -static QJsonObject assetIndexToJson (MojangAssetIndexInfo::Ptr assetidxinfo); -static QJsonObject libDownloadInfoToJson (MojangLibraryDownloadInfo::Ptr libinfo); -static QJsonObject downloadInfoToJson (MojangDownloadInfo::Ptr info); - -namespace Bits -{ -static void readString(const QJsonObject &root, const QString &key, QString &variable) -{ - if (root.contains(key)) - { - variable = requireString(root.value(key)); - } -} - -static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj) -{ - // optional, not used - readString(obj, "path", out->path); - // required! - out->sha1 = requireString(obj, "sha1"); - out->url = requireString(obj, "url"); - out->size = requireInteger(obj, "size"); -} - -static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj) -{ - out->totalSize = requireInteger(obj, "totalSize"); - out->id = requireString(obj, "id"); - // out->known = true; -} -} - -MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject &obj) -{ - auto out = std::make_shared(); - Bits::readDownloadInfo(out, obj); - return out; -} - -MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj) -{ - auto out = std::make_shared(); - Bits::readDownloadInfo(out, obj); - Bits::readAssetIndex(out, obj); - return out; -} - -QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info) -{ - QJsonObject out; - if(!info->path.isNull()) - { - out.insert("path", info->path); - } - out.insert("sha1", info->sha1); - out.insert("size", info->size); - out.insert("url", info->url); - return out; -} - -MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj) -{ - auto out = std::make_shared(); - auto dlObj = requireObject(libObj.value("downloads")); - if(dlObj.contains("artifact")) - { - out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact")); - } - if(dlObj.contains("classifiers")) - { - auto classifiersObj = requireObject(dlObj, "classifiers"); - for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++) - { - auto classifier = iter.key(); - auto classifierObj = requireObject(iter.value()); - out->classifiers[classifier] = downloadInfoFromJson(classifierObj); - } - } - return out; -} - -QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo) -{ - QJsonObject out; - if(libinfo->artifact) - { - out.insert("artifact", downloadInfoToJson(libinfo->artifact)); - } - if(libinfo->classifiers.size()) - { - QJsonObject classifiersOut; - for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++) - { - classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value())); - } - out.insert("classifiers", classifiersOut); - } - return out; -} - -QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info) -{ - QJsonObject out; - if(!info->path.isNull()) - { - out.insert("path", info->path); - } - out.insert("sha1", info->sha1); - out.insert("size", info->size); - out.insert("url", info->url); - out.insert("totalSize", info->totalSize); - out.insert("id", info->id); - return out; -} - -void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out) -{ - Bits::readString(in, "id", out->minecraftVersion); - Bits::readString(in, "mainClass", out->mainClass); - Bits::readString(in, "minecraftArguments", out->minecraftArguments); - if(out->minecraftArguments.isEmpty()) - { - QString processArguments; - Bits::readString(in, "processArguments", processArguments); - QString toCompare = processArguments.toLower(); - if (toCompare == "legacy") - { - out->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } - else if (!toCompare.isEmpty()) - { - out->addProblem(PROBLEM_ERROR, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments)); - } - } - Bits::readString(in, "type", out->type); - - Bits::readString(in, "assets", out->assets); - if(in.contains("assetIndex")) - { - out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex")); - } - else if (!out->assets.isNull()) - { - out->mojangAssetIndex = std::make_shared(out->assets); - } - - out->m_releaseTime = timeFromS3Time(in.value("releaseTime").toString("")); - out->m_updateTime = timeFromS3Time(in.value("time").toString("")); - - if (in.contains("minimumLauncherVersion")) - { - out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion")); - if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) - { - out->addProblem( - PROBLEM_WARNING, - QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!") - .arg(out->minimumLauncherVersion) - .arg(CURRENT_MINIMUM_LAUNCHER_VERSION)); - } - } - if(in.contains("downloads")) - { - auto downloadsObj = requireObject(in, "downloads"); - for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++) - { - auto classifier = iter.key(); - auto classifierObj = requireObject(iter.value()); - out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj); - } - } -} - -VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename) -{ - VersionFilePtr out(new VersionFile()); - if (doc.isEmpty() || doc.isNull()) - { - throw JSONValidationError(filename + " is empty or null"); - } - if (!doc.isObject()) - { - throw JSONValidationError(filename + " is not an object"); - } - - QJsonObject root = doc.object(); - - readVersionProperties(root, out.get()); - - out->name = "Minecraft"; - out->fileId = "net.minecraft"; - out->version = out->minecraftVersion; - out->filename = filename; - - - if (root.contains("libraries")) - { - for (auto libVal : requireArray(root.value("libraries"))) - { - auto libObj = requireObject(libVal); - - auto lib = MojangVersionFormat::libraryFromJson(libObj, filename); - out->libraries.append(lib); - } - } - return out; -} - -void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out) -{ - writeString(out, "id", in->minecraftVersion); - writeString(out, "mainClass", in->mainClass); - writeString(out, "minecraftArguments", in->minecraftArguments); - writeString(out, "type", in->type); - if(!in->m_releaseTime.isNull()) - { - writeString(out, "releaseTime", timeToS3Time(in->m_releaseTime)); - } - if(!in->m_updateTime.isNull()) - { - writeString(out, "time", timeToS3Time(in->m_updateTime)); - } - if(in->minimumLauncherVersion != -1) - { - out.insert("minimumLauncherVersion", in->minimumLauncherVersion); - } - writeString(out, "assets", in->assets); - if(in->mojangAssetIndex && in->mojangAssetIndex->known) - { - out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex)); - } - if(in->mojangDownloads.size()) - { - QJsonObject downloadsOut; - for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++) - { - downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value())); - } - out.insert("downloads", downloadsOut); - } -} - -QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch) -{ - QJsonObject root; - writeVersionProperties(patch.get(), root); - if (!patch->libraries.isEmpty()) - { - QJsonArray array; - for (auto value: patch->libraries) - { - array.append(MojangVersionFormat::libraryToJson(value.get())); - } - root.insert("libraries", array); - } - - // write the contents to a json document. - { - QJsonDocument out; - out.setObject(root); - return out; - } -} - -LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename) -{ - LibraryPtr out(new Library()); - if (!libObj.contains("name")) - { - throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field"); - } - out->m_name = libObj.value("name").toString(); - - Bits::readString(libObj, "url", out->m_repositoryURL); - if (libObj.contains("extract")) - { - out->m_hasExcludes = true; - auto extractObj = requireObject(libObj.value("extract")); - for (auto excludeVal : requireArray(extractObj.value("exclude"))) - { - out->m_extractExcludes.append(requireString(excludeVal)); - } - } - if (libObj.contains("natives")) - { - QJsonObject nativesObj = requireObject(libObj.value("natives")); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - if (!it.value().isString()) - { - qWarning() << filename << "contains an invalid native (skipping)"; - } - OpSys opSys = OpSys_fromString(it.key()); - if (opSys != Os_Other) - { - out->m_nativeClassifiers[opSys] = it.value().toString(); - } - } - } - if (libObj.contains("rules")) - { - out->applyRules = true; - out->m_rules = rulesFromJsonV4(libObj); - } - if (libObj.contains("downloads")) - { - out->m_mojangDownloads = libDownloadInfoFromJson(libObj); - } - return out; -} - -QJsonObject MojangVersionFormat::libraryToJson(Library *library) -{ - QJsonObject libRoot; - libRoot.insert("name", (QString)library->m_name); - if (!library->m_repositoryURL.isEmpty()) - { - libRoot.insert("url", library->m_repositoryURL); - } - if (library->isNative()) - { - QJsonObject nativeList; - auto iter = library->m_nativeClassifiers.begin(); - while (iter != library->m_nativeClassifiers.end()) - { - nativeList.insert(OpSys_toString(iter.key()), iter.value()); - iter++; - } - libRoot.insert("natives", nativeList); - if (library->m_extractExcludes.size()) - { - QJsonArray excludes; - QJsonObject extract; - for (auto exclude : library->m_extractExcludes) - { - excludes.append(exclude); - } - extract.insert("exclude", excludes); - libRoot.insert("extract", extract); - } - } - if (library->m_rules.size()) - { - QJsonArray allRules; - for (auto &rule : library->m_rules) - { - QJsonObject ruleObj = rule->toJson(); - allRules.append(ruleObj); - } - libRoot.insert("rules", allRules); - } - if(library->m_mojangDownloads) - { - auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads); - libRoot.insert("downloads", downloadsObj); - } - return libRoot; -} diff --git a/libraries/logic/minecraft/MojangVersionFormat.h b/libraries/logic/minecraft/MojangVersionFormat.h deleted file mode 100644 index 4e141088..00000000 --- a/libraries/logic/minecraft/MojangVersionFormat.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT MojangVersionFormat -{ -friend class OneSixVersionFormat; -protected: - // does not include libraries - static void readVersionProperties(const QJsonObject& in, VersionFile* out); - // does not include libraries - static void writeVersionProperties(const VersionFile* in, QJsonObject& out); -public: - // version files / profile patches - static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename); - static QJsonDocument versionFileToJson(const VersionFilePtr &patch); - - // libraries - static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename); - static QJsonObject libraryToJson(Library *library); -}; diff --git a/libraries/logic/minecraft/OpSys.cpp b/libraries/logic/minecraft/OpSys.cpp deleted file mode 100644 index 4c2a236d..00000000 --- a/libraries/logic/minecraft/OpSys.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "OpSys.h" - -OpSys OpSys_fromString(QString name) -{ - if (name == "linux") - return Os_Linux; - if (name == "windows") - return Os_Windows; - if (name == "osx") - return Os_OSX; - return Os_Other; -} - -QString OpSys_toString(OpSys name) -{ - switch (name) - { - case Os_Linux: - return "linux"; - case Os_OSX: - return "osx"; - case Os_Windows: - return "windows"; - default: - return "other"; - } -} \ No newline at end of file diff --git a/libraries/logic/minecraft/OpSys.h b/libraries/logic/minecraft/OpSys.h deleted file mode 100644 index 9ebea3de..00000000 --- a/libraries/logic/minecraft/OpSys.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once -#include -enum OpSys -{ - Os_Windows, - Os_Linux, - Os_OSX, - Os_Other -}; - -OpSys OpSys_fromString(QString); -QString OpSys_toString(OpSys); - -#ifdef Q_OS_WIN32 -#define currentSystem Os_Windows -#else -#ifdef Q_OS_MAC -#define currentSystem Os_OSX -#else -#define currentSystem Os_Linux -#endif -#endif \ No newline at end of file diff --git a/libraries/logic/minecraft/ParseUtils.cpp b/libraries/logic/minecraft/ParseUtils.cpp deleted file mode 100644 index ca188432..00000000 --- a/libraries/logic/minecraft/ParseUtils.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include "ParseUtils.h" -#include -#include - -QDateTime timeFromS3Time(QString str) -{ - return QDateTime::fromString(str, Qt::ISODate); -} - -QString timeToS3Time(QDateTime time) -{ - // this all because Qt can't format timestamps right. - int offsetRaw = time.offsetFromUtc(); - bool negative = offsetRaw < 0; - int offsetAbs = std::abs(offsetRaw); - - int offsetSeconds = offsetAbs % 60; - offsetAbs -= offsetSeconds; - - int offsetMinutes = offsetAbs % 3600; - offsetAbs -= offsetMinutes; - offsetMinutes /= 60; - - int offsetHours = offsetAbs / 3600; - - QString raw = time.toString("yyyy-MM-ddTHH:mm:ss"); - raw += (negative ? QChar('-') : QChar('+')); - raw += QString("%1").arg(offsetHours, 2, 10, QChar('0')); - raw += ":"; - raw += QString("%1").arg(offsetMinutes, 2, 10, QChar('0')); - return raw; -} diff --git a/libraries/logic/minecraft/ParseUtils.h b/libraries/logic/minecraft/ParseUtils.h deleted file mode 100644 index 2b367a10..00000000 --- a/libraries/logic/minecraft/ParseUtils.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include -#include - -#include "multimc_logic_export.h" - -/// take the timestamp used by S3 and turn it into QDateTime -MULTIMC_LOGIC_EXPORT QDateTime timeFromS3Time(QString str); - -/// take a timestamp and convert it into an S3 timestamp -MULTIMC_LOGIC_EXPORT QString timeToS3Time(QDateTime); diff --git a/libraries/logic/minecraft/ProfilePatch.h b/libraries/logic/minecraft/ProfilePatch.h deleted file mode 100644 index f0c65360..00000000 --- a/libraries/logic/minecraft/ProfilePatch.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "JarMod.h" - -class MinecraftProfile; - -enum ProblemSeverity -{ - PROBLEM_NONE, - PROBLEM_WARNING, - PROBLEM_ERROR -}; - -/// where is a version from? -enum VersionSource -{ - Local, //!< version loaded from a file in the cache. - Remote, //!< incomplete version on a remote server. -}; - -class PatchProblem -{ -public: - PatchProblem(ProblemSeverity severity, const QString & description) - { - m_severity = severity; - m_description = description; - } - const QString & getDescription() const - { - return m_description; - } - const ProblemSeverity getSeverity() const - { - return m_severity; - } -private: - ProblemSeverity m_severity; - QString m_description; -}; - -class ProfilePatch : public std::enable_shared_from_this -{ -public: - virtual ~ProfilePatch(){}; - virtual void applyTo(MinecraftProfile *profile) = 0; - - virtual bool isMinecraftVersion() = 0; - virtual bool hasJarMods() = 0; - virtual QList getJarMods() = 0; - - virtual bool isMoveable() = 0; - virtual bool isCustomizable() = 0; - virtual bool isRevertible() = 0; - virtual bool isRemovable() = 0; - virtual bool isCustom() = 0; - virtual bool isEditable() = 0; - virtual bool isVersionChangeable() = 0; - - virtual void setOrder(int order) = 0; - virtual int getOrder() = 0; - - virtual QString getID() = 0; - virtual QString getName() = 0; - virtual QString getVersion() = 0; - virtual QDateTime getReleaseDateTime() = 0; - - virtual QString getFilename() = 0; - - virtual VersionSource getVersionSource() = 0; - - virtual std::shared_ptr getVersionFile() = 0; - - virtual const QList& getProblems() - { - return m_problems; - } - virtual void addProblem(ProblemSeverity severity, const QString &description) - { - if(severity > m_problemSeverity) - { - m_problemSeverity = severity; - } - m_problems.append(PatchProblem(severity, description)); - } - virtual ProblemSeverity getProblemSeverity() - { - return m_problemSeverity; - } - virtual bool hasFailed() - { - return getProblemSeverity() == PROBLEM_ERROR; - } - -protected: - QList m_problems; - ProblemSeverity m_problemSeverity = PROBLEM_NONE; -}; - -typedef std::shared_ptr ProfilePatchPtr; diff --git a/libraries/logic/minecraft/ProfileStrategy.h b/libraries/logic/minecraft/ProfileStrategy.h deleted file mode 100644 index b4dfc4b3..00000000 --- a/libraries/logic/minecraft/ProfileStrategy.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "ProfileUtils.h" - -class MinecraftProfile; - -class ProfileStrategy -{ - friend class MinecraftProfile; -public: - virtual ~ProfileStrategy(){}; - - /// load the patch files into the profile - virtual void load() = 0; - - /// reset the order of patches - virtual bool resetOrder() = 0; - - /// save the order of patches, given the order - virtual bool saveOrder(ProfileUtils::PatchOrder order) = 0; - - /// install a list of jar mods into the instance - virtual bool installJarMods(QStringList filepaths) = 0; - - /// remove any files or records that constitute the version patch - virtual bool removePatch(ProfilePatchPtr jarMod) = 0; - - /// make the patch custom, if possible - virtual bool customizePatch(ProfilePatchPtr patch) = 0; - - /// revert the custom patch to 'vanilla', if possible - virtual bool revertPatch(ProfilePatchPtr patch) = 0; -protected: - MinecraftProfile *profile; -}; diff --git a/libraries/logic/minecraft/ProfileUtils.cpp b/libraries/logic/minecraft/ProfileUtils.cpp deleted file mode 100644 index ef9b3b28..00000000 --- a/libraries/logic/minecraft/ProfileUtils.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "ProfileUtils.h" -#include "minecraft/VersionFilterData.h" -#include "minecraft/onesix/OneSixVersionFormat.h" -#include "Json.h" -#include - -#include -#include -#include -#include - -namespace ProfileUtils -{ - -static const int currentOrderFileVersion = 1; - -bool writeOverrideOrders(QString path, const PatchOrder &order) -{ - QJsonObject obj; - obj.insert("version", currentOrderFileVersion); - QJsonArray orderArray; - for(auto str: order) - { - orderArray.append(str); - } - obj.insert("order", orderArray); - QSaveFile orderFile(path); - if (!orderFile.open(QFile::WriteOnly)) - { - qCritical() << "Couldn't open" << orderFile.fileName() - << "for writing:" << orderFile.errorString(); - return false; - } - auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented); - if(orderFile.write(data) != data.size()) - { - qCritical() << "Couldn't write all the data into" << orderFile.fileName() - << "because:" << orderFile.errorString(); - return false; - } - if(!orderFile.commit()) - { - qCritical() << "Couldn't save" << orderFile.fileName() - << "because:" << orderFile.errorString(); - } - return true; -} - -bool readOverrideOrders(QString path, PatchOrder &order) -{ - QFile orderFile(path); - if (!orderFile.exists()) - { - qWarning() << "Order file doesn't exist. Ignoring."; - return false; - } - if (!orderFile.open(QFile::ReadOnly)) - { - qCritical() << "Couldn't open" << orderFile.fileName() - << " for reading:" << orderFile.errorString(); - qWarning() << "Ignoring overriden order"; - return false; - } - - // and it's valid JSON - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error); - if (error.error != QJsonParseError::NoError) - { - qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString(); - qWarning() << "Ignoring overriden order"; - return false; - } - - // and then read it and process it if all above is true. - try - { - auto obj = Json::requireObject(doc); - // check order file version. - auto version = Json::requireInteger(obj.value("version")); - if (version != currentOrderFileVersion) - { - throw JSONValidationError(QObject::tr("Invalid order file version, expected %1") - .arg(currentOrderFileVersion)); - } - auto orderArray = Json::requireArray(obj.value("order")); - for(auto item: orderArray) - { - order.append(Json::requireString(item)); - } - } - catch (JSONValidationError &err) - { - qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format"; - qWarning() << "Ignoring overriden order"; - order.clear(); - return false; - } - return true; -} - -static VersionFilePtr createErrorVersionFile(QString fileId, QString filepath, QString error) -{ - auto outError = std::make_shared(); - outError->fileId = outError->name = fileId; - outError->filename = filepath; - outError->addProblem(PROBLEM_ERROR, error); - return outError; -} - -static VersionFilePtr guardedParseJson(const QJsonDocument & doc,const QString &fileId,const QString &filepath,const bool &requireOrder) -{ - try - { - return OneSixVersionFormat::versionFileFromJson(doc, filepath, requireOrder); - } - catch (Exception & e) - { - return createErrorVersionFile(fileId, filepath, e.cause()); - } -} - -VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder) -{ - QFile file(fileInfo.absoluteFilePath()); - if (!file.open(QFile::ReadOnly)) - { - auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString()); - return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); - } - QJsonParseError error; - auto data = file.readAll(); - QJsonDocument doc = QJsonDocument::fromJson(data, &error); - file.close(); - if (error.error != QJsonParseError::NoError) - { - int line = 1; - int column = 0; - for(int i = 0; i < error.offset; i++) - { - if(data[i] == '\n') - { - line++; - column = 0; - continue; - } - column++; - } - auto errorStr = QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.") - .arg(fileInfo.fileName(), error.errorString()) - .arg(line).arg(column); - return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); - } - return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); -} - -VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo) -{ - QFile file(fileInfo.absoluteFilePath()); - if (!file.open(QFile::ReadOnly)) - { - auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString()); - return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); - } - QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll()); - file.close(); - if (doc.isNull()) - { - file.remove(); - throw JSONValidationError(QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName())); - } - return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), false); -} - -void removeLwjglFromPatch(VersionFilePtr patch) -{ - auto filter = [](QList& libs) - { - QList filteredLibs; - for (auto lib : libs) - { - if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix())) - { - filteredLibs.append(lib); - } - } - libs = filteredLibs; - }; - filter(patch->libraries); -} -} diff --git a/libraries/logic/minecraft/ProfileUtils.h b/libraries/logic/minecraft/ProfileUtils.h deleted file mode 100644 index 267fd42b..00000000 --- a/libraries/logic/minecraft/ProfileUtils.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "Library.h" -#include "VersionFile.h" - -namespace ProfileUtils -{ -typedef QStringList PatchOrder; - -/// Read and parse a OneSix format order file -bool readOverrideOrders(QString path, PatchOrder &order); - -/// Write a OneSix format order file -bool writeOverrideOrders(QString path, const PatchOrder &order); - - -/// Parse a version file in JSON format -VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder); - -/// Parse a version file in binary JSON format -VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo); - -/// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. -void removeLwjglFromPatch(VersionFilePtr patch); - -} diff --git a/libraries/logic/minecraft/Rule.cpp b/libraries/logic/minecraft/Rule.cpp deleted file mode 100644 index c8ba297b..00000000 --- a/libraries/logic/minecraft/Rule.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include -#include - -#include "Rule.h" - -RuleAction RuleAction_fromString(QString name) -{ - if (name == "allow") - return Allow; - if (name == "disallow") - return Disallow; - return Defer; -} - -QList> rulesFromJsonV4(const QJsonObject &objectWithRules) -{ - QList> rules; - auto rulesVal = objectWithRules.value("rules"); - if (!rulesVal.isArray()) - return rules; - - QJsonArray ruleList = rulesVal.toArray(); - for (auto ruleVal : ruleList) - { - std::shared_ptr rule; - if (!ruleVal.isObject()) - continue; - auto ruleObj = ruleVal.toObject(); - auto actionVal = ruleObj.value("action"); - if (!actionVal.isString()) - continue; - auto action = RuleAction_fromString(actionVal.toString()); - if (action == Defer) - continue; - - auto osVal = ruleObj.value("os"); - if (!osVal.isObject()) - { - // add a new implicit action rule - rules.append(ImplicitRule::create(action)); - continue; - } - - auto osObj = osVal.toObject(); - auto osNameVal = osObj.value("name"); - if (!osNameVal.isString()) - continue; - OpSys requiredOs = OpSys_fromString(osNameVal.toString()); - QString versionRegex = osObj.value("version").toString(); - // add a new OS rule - rules.append(OsRule::create(action, requiredOs, versionRegex)); - } - return rules; -} - -QJsonObject ImplicitRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - return ruleObj; -} - -QJsonObject OsRule::toJson() -{ - QJsonObject ruleObj; - ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); - QJsonObject osObj; - { - osObj.insert("name", OpSys_toString(m_system)); - if(!m_version_regexp.isEmpty()) - { - osObj.insert("version", m_version_regexp); - } - } - ruleObj.insert("os", osObj); - return ruleObj; -} - diff --git a/libraries/logic/minecraft/Rule.h b/libraries/logic/minecraft/Rule.h deleted file mode 100644 index c8bf6eaa..00000000 --- a/libraries/logic/minecraft/Rule.h +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include -#include -#include "OpSys.h" - -class Library; -class Rule; - -enum RuleAction -{ - Allow, - Disallow, - Defer -}; - -QList> rulesFromJsonV4(const QJsonObject &objectWithRules); - -class Rule -{ -protected: - RuleAction m_result; - virtual bool applies(const Library *parent) = 0; - -public: - Rule(RuleAction result) : m_result(result) - { - } - virtual ~Rule() {}; - virtual QJsonObject toJson() = 0; - RuleAction apply(const Library *parent) - { - if (applies(parent)) - return m_result; - else - return Defer; - } -}; - -class OsRule : public Rule -{ -private: - // the OS - OpSys m_system; - // the OS version regexp - QString m_version_regexp; - -protected: - virtual bool applies(const Library *) - { - return (m_system == currentSystem); - } - OsRule(RuleAction result, OpSys system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result, OpSys system, - QString version_regexp) - { - return std::shared_ptr(new OsRule(result, system, version_regexp)); - } -}; - -class ImplicitRule : public Rule -{ -protected: - virtual bool applies(const Library *) - { - return true; - } - ImplicitRule(RuleAction result) : Rule(result) - { - } - -public: - virtual QJsonObject toJson(); - static std::shared_ptr create(RuleAction result) - { - return std::shared_ptr(new ImplicitRule(result)); - } -}; diff --git a/libraries/logic/minecraft/VersionBuildError.h b/libraries/logic/minecraft/VersionBuildError.h deleted file mode 100644 index fda453e5..00000000 --- a/libraries/logic/minecraft/VersionBuildError.h +++ /dev/null @@ -1,58 +0,0 @@ -#include "Exception.h" - -class VersionBuildError : public Exception -{ -public: - explicit VersionBuildError(QString cause) : Exception(cause) {} - virtual ~VersionBuildError() noexcept - { - } -}; - -/** - * the base version file was meant for a newer version of the vanilla launcher than we support - */ -class LauncherVersionError : public VersionBuildError -{ -public: - LauncherVersionError(int actual, int supported) - : VersionBuildError(QObject::tr( - "The base version file of this instance was meant for a newer (%1) " - "version of the vanilla launcher than this version of MultiMC supports (%2).") - .arg(actual) - .arg(supported)) {}; - virtual ~LauncherVersionError() noexcept - { - } -}; - -/** - * some patch was intended for a different version of minecraft - */ -class MinecraftVersionMismatch : public VersionBuildError -{ -public: - MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion) - : VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft " - "(%2) than that of the instance (%3).") - .arg(fileId) - .arg(mcVersion) - .arg(parentMcVersion)) {}; - virtual ~MinecraftVersionMismatch() noexcept - { - } -}; - -/** - * files required for the version are not (yet?) present - */ -class VersionIncomplete : public VersionBuildError -{ -public: - VersionIncomplete(QString missingPatch) - : VersionBuildError(QObject::tr("Version is incomplete: missing %1.") - .arg(missingPatch)) {}; - virtual ~VersionIncomplete() noexcept - { - } -}; diff --git a/libraries/logic/minecraft/VersionFile.cpp b/libraries/logic/minecraft/VersionFile.cpp deleted file mode 100644 index 573c4cb4..00000000 --- a/libraries/logic/minecraft/VersionFile.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include - -#include "minecraft/VersionFile.h" -#include "minecraft/Library.h" -#include "minecraft/MinecraftProfile.h" -#include "minecraft/JarMod.h" -#include "ParseUtils.h" - -#include "VersionBuildError.h" -#include - -bool VersionFile::isMinecraftVersion() -{ - return fileId == "net.minecraft"; -} - -bool VersionFile::hasJarMods() -{ - return !jarMods.isEmpty(); -} - -void VersionFile::applyTo(MinecraftProfile *profile) -{ - auto theirVersion = profile->getMinecraftVersion(); - if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull()) - { - if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1) - { - throw MinecraftVersionMismatch(fileId, dependsOnMinecraftVersion, theirVersion); - } - } - profile->applyMinecraftVersion(minecraftVersion); - profile->applyMainClass(mainClass); - profile->applyAppletClass(appletClass); - profile->applyMinecraftArguments(minecraftArguments); - if (isMinecraftVersion()) - { - profile->applyMinecraftVersionType(type); - } - profile->applyMinecraftAssets(mojangAssetIndex); - profile->applyTweakers(addTweakers); - - profile->applyJarMods(jarMods); - profile->applyTraits(traits); - - for (auto library : libraries) - { - profile->applyLibrary(library); - } - profile->applyProblemSeverity(getProblemSeverity()); - auto iter = mojangDownloads.begin(); - while(iter != mojangDownloads.end()) - { - profile->applyMojangDownload(iter.key(), iter.value()); - iter++; - } -} diff --git a/libraries/logic/minecraft/VersionFile.h b/libraries/logic/minecraft/VersionFile.h deleted file mode 100644 index 1b692f0f..00000000 --- a/libraries/logic/minecraft/VersionFile.h +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include "minecraft/OpSys.h" -#include "minecraft/Rule.h" -#include "ProfilePatch.h" -#include "Library.h" -#include "JarMod.h" - -class MinecraftProfile; -class VersionFile; -struct MojangDownloadInfo; -struct MojangAssetIndexInfo; - -typedef std::shared_ptr VersionFilePtr; -class VersionFile : public ProfilePatch -{ - friend class MojangVersionFormat; - friend class OneSixVersionFormat; -public: /* methods */ - virtual void applyTo(MinecraftProfile *profile) override; - virtual bool isMinecraftVersion() override; - virtual bool hasJarMods() override; - virtual int getOrder() override - { - return order; - } - virtual void setOrder(int order) override - { - this->order = order; - } - virtual QList getJarMods() override - { - return jarMods; - } - virtual QString getID() override - { - return fileId; - } - virtual QString getName() override - { - return name; - } - virtual QString getVersion() override - { - return version; - } - virtual QString getFilename() override - { - return filename; - } - virtual QDateTime getReleaseDateTime() override - { - return m_releaseTime; - } - VersionSource getVersionSource() override - { - return Local; - } - - std::shared_ptr getVersionFile() override - { - return std::dynamic_pointer_cast(shared_from_this()); - } - - virtual bool isCustom() override - { - return !m_isVanilla; - }; - virtual bool isCustomizable() override - { - return m_isCustomizable; - } - virtual bool isRemovable() override - { - return m_isRemovable; - } - virtual bool isRevertible() override - { - return m_isRevertible; - } - virtual bool isMoveable() override - { - return m_isMovable; - } - virtual bool isEditable() override - { - return isCustom(); - } - virtual bool isVersionChangeable() override - { - return false; - } - - void setVanilla (bool state) - { - m_isVanilla = state; - } - void setRemovable (bool state) - { - m_isRemovable = state; - } - void setRevertible (bool state) - { - m_isRevertible = state; - } - void setCustomizable (bool state) - { - m_isCustomizable = state; - } - void setMovable (bool state) - { - m_isMovable = state; - } - - -public: /* data */ - /// MultiMC: order hint for this version file if no explicit order is set - int order = 0; - - // Flags for UI and version file manipulation in general - bool m_isVanilla = false; - bool m_isRemovable = false; - bool m_isRevertible = false; - bool m_isCustomizable = false; - bool m_isMovable = false; - - /// MultiMC: filename of the file this was loaded from - QString filename; - - /// MultiMC: human readable name of this package - QString name; - - /// MultiMC: package ID of this package - QString fileId; - - /// MultiMC: version of this package - QString version; - - /// MultiMC: dependency on a Minecraft version - QString dependsOnMinecraftVersion; - - /// Mojang: used to version the Mojang version format - int minimumLauncherVersion = -1; - - /// Mojang: version of Minecraft this is - QString minecraftVersion; - - /// Mojang: class to launch Minecraft with - QString mainClass; - - /// MultiMC: DEPRECATED class to launch legacy Minecraft with (ambed in a custom window) - QString appletClass; - - /// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution) - QString minecraftArguments; - - /// Mojang: type of the Minecraft version - QString type; - - /// Mojang: the time this version was actually released by Mojang - QDateTime m_releaseTime; - - /// Mojang: the time this version was last updated by Mojang - QDateTime m_updateTime; - - /// Mojang: DEPRECATED asset group to be used with Minecraft - QString assets; - - /// MultiMC: list of tweaker mod arguments for launchwrapper - QStringList addTweakers; - - /// Mojang: list of libraries to add to the version - QList libraries; - - /// MultiMC: list of attached traits of this version file - used to enable features - QSet traits; - - /// MultiMC: list of jar mods added to this version - QList jarMods; - -public: - // Mojang: list of 'downloads' - client jar, server jar, windows server exe, maybe more. - QMap > mojangDownloads; - - // Mojang: extended asset index download information - std::shared_ptr mojangAssetIndex; -}; - - diff --git a/libraries/logic/minecraft/VersionFilterData.cpp b/libraries/logic/minecraft/VersionFilterData.cpp deleted file mode 100644 index 0c4a6e3d..00000000 --- a/libraries/logic/minecraft/VersionFilterData.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "VersionFilterData.h" -#include "ParseUtils.h" - -VersionFilterData g_VersionFilterData = VersionFilterData(); - -VersionFilterData::VersionFilterData() -{ - // 1.3.* - auto libs13 = - QList{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false}, - {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false}, - {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}}; - - fmlLibsMapping["1.3.2"] = libs13; - - // 1.4.* - auto libs14 = QList{ - {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false}, - {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false}, - {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}, - {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}}; - - fmlLibsMapping["1.4"] = libs14; - fmlLibsMapping["1.4.1"] = libs14; - fmlLibsMapping["1.4.2"] = libs14; - fmlLibsMapping["1.4.3"] = libs14; - fmlLibsMapping["1.4.4"] = libs14; - fmlLibsMapping["1.4.5"] = libs14; - fmlLibsMapping["1.4.6"] = libs14; - fmlLibsMapping["1.4.7"] = libs14; - - // 1.5 - fmlLibsMapping["1.5"] = QList{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, - {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; - - // 1.5.1 - fmlLibsMapping["1.5.1"] = QList{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, - {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; - - // 1.5.2 - fmlLibsMapping["1.5.2"] = QList{ - {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, - {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, - {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, - {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, - {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false}, - {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; - - // don't use installers for those. - forgeInstallerBlacklist = QSet({"1.5.2"}); - // these won't show up in version lists because they are extremely bad and dangerous - legacyBlacklist = QSet({"rd-160052"}); - /* - * nothing older than this will be accepted from Mojang servers - * (these versions need to be tested by us first) - */ - legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00"); - lwjglWhitelist = - QSet{"net.java.jinput:jinput", "net.java.jinput:jinput-platform", - "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", - "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"}; - - // Version list magic - recommendedMinecraftVersion = "1.7.10"; -} diff --git a/libraries/logic/minecraft/VersionFilterData.h b/libraries/logic/minecraft/VersionFilterData.h deleted file mode 100644 index f7d4ebe7..00000000 --- a/libraries/logic/minecraft/VersionFilterData.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include "multimc_logic_export.h" - -struct FMLlib -{ - QString filename; - QString checksum; - bool ours; -}; - -struct VersionFilterData -{ - VersionFilterData(); - // mapping between minecraft versions and FML libraries required - QMap> fmlLibsMapping; - // set of minecraft versions for which using forge installers is blacklisted - QSet forgeInstallerBlacklist; - // set of 'legacy' versions that will not show up in the version lists. - QSet legacyBlacklist; - // no new versions below this date will be accepted from Mojang servers - QDateTime legacyCutoffDate; - // Libraries that belong to LWJGL - QSet lwjglWhitelist; - // Currently recommended minecraft version - QString recommendedMinecraftVersion; -}; -extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData; diff --git a/libraries/logic/minecraft/World.cpp b/libraries/logic/minecraft/World.cpp deleted file mode 100644 index 6081a8ec..00000000 --- a/libraries/logic/minecraft/World.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* Copyright 2015 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. - */ - -#include -#include -#include -#include -#include "World.h" - -#include "GZip.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -std::unique_ptr parseLevelDat(QByteArray data) -{ - QByteArray output; - if(!GZip::unzip(data, output)) - { - return nullptr; - } - std::istringstream foo(std::string(output.constData(), output.size())); - auto pair = nbt::io::read_compound(foo); - - if(pair.first != "") - return nullptr; - - if(pair.second == nullptr) - return nullptr; - - return std::move(pair.second); -} - -QByteArray serializeLevelDat(nbt::tag_compound * levelInfo) -{ - std::ostringstream s; - nbt::io::write_tag("", *levelInfo, s); - QByteArray val( s.str().data(), (int) s.str().size() ); - return val; -} - -QString getLevelDatFromFS(const QFileInfo &file) -{ - QDir worldDir(file.filePath()); - if(!file.isDir() || !worldDir.exists("level.dat")) - { - return QString(); - } - return worldDir.absoluteFilePath("level.dat"); -} - -QByteArray getLevelDatDataFromFS(const QFileInfo &file) -{ - auto fullFilePath = getLevelDatFromFS(file); - if(fullFilePath.isNull()) - { - return QByteArray(); - } - QFile f(fullFilePath); - if(!f.open(QIODevice::ReadOnly)) - { - return QByteArray(); - } - return f.readAll(); -} - -bool putLevelDatDataToFS(const QFileInfo &file, QByteArray & data) -{ - auto fullFilePath = getLevelDatFromFS(file); - if(fullFilePath.isNull()) - { - return false; - } - QSaveFile f(fullFilePath); - if(!f.open(QIODevice::WriteOnly)) - { - return false; - } - QByteArray compressed; - if(!GZip::zip(data, compressed)) - { - return false; - } - if(f.write(compressed) != compressed.size()) - { - f.cancelWriting(); - return false; - } - return f.commit(); -} - -World::World(const QFileInfo &file) -{ - repath(file); -} - -void World::repath(const QFileInfo &file) -{ - m_containerFile = file; - m_folderName = file.fileName(); - if(file.isFile() && file.suffix() == "zip") - { - readFromZip(file); - } - else if(file.isDir()) - { - readFromFS(file); - } -} - -void World::readFromFS(const QFileInfo &file) -{ - auto bytes = getLevelDatDataFromFS(file); - if(bytes.isEmpty()) - { - is_valid = false; - return; - } - loadFromLevelDat(bytes); - levelDatTime = file.lastModified(); -} - -void World::readFromZip(const QFileInfo &file) -{ - QuaZip zip(file.absoluteFilePath()); - is_valid = zip.open(QuaZip::mdUnzip); - if (!is_valid) - { - return; - } - auto location = MMCZip::findFileInZip(&zip, "level.dat"); - is_valid = !location.isEmpty(); - if (!is_valid) - { - return; - } - m_containerOffsetPath = location; - QuaZipFile zippedFile(&zip); - // read the install profile - is_valid = zip.setCurrentFile(location + "level.dat"); - if (!is_valid) - { - return; - } - is_valid = zippedFile.open(QIODevice::ReadOnly); - QuaZipFileInfo64 levelDatInfo; - zippedFile.getFileInfo(&levelDatInfo); - auto modTime = levelDatInfo.getNTFSmTime(); - if(!modTime.isValid()) - { - modTime = levelDatInfo.dateTime; - } - levelDatTime = modTime; - if (!is_valid) - { - return; - } - loadFromLevelDat(zippedFile.readAll()); - zippedFile.close(); -} - -bool World::install(const QString &to, const QString &name) -{ - auto finalPath = FS::PathCombine(to, FS::DirNameFromString(m_actualName, to)); - if(!FS::ensureFolderPathExists(finalPath)) - { - return false; - } - bool ok = false; - if(m_containerFile.isFile()) - { - QuaZip zip(m_containerFile.absoluteFilePath()); - if (!zip.open(QuaZip::mdUnzip)) - { - return false; - } - ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty(); - } - else if(m_containerFile.isDir()) - { - QString from = m_containerFile.filePath(); - ok = FS::copy(from, finalPath)(); - } - - if(ok && !name.isEmpty() && m_actualName != name) - { - World newWorld(finalPath); - if(newWorld.isValid()) - { - newWorld.rename(name); - } - } - return ok; -} - -bool World::rename(const QString &newName) -{ - if(m_containerFile.isFile()) - { - return false; - } - - auto data = getLevelDatDataFromFS(m_containerFile); - if(data.isEmpty()) - { - return false; - } - - auto worldData = parseLevelDat(data); - if(!worldData) - { - return false; - } - auto &val = worldData->at("Data"); - if(val.get_type() != nbt::tag_type::Compound) - { - return false; - } - auto &dataCompound = val.as(); - dataCompound.put("LevelName", nbt::value_initializer(newName.toUtf8().data())); - data = serializeLevelDat(worldData.get()); - - putLevelDatDataToFS(m_containerFile, data); - - m_actualName = newName; - - QDir parentDir(m_containerFile.absoluteFilePath()); - parentDir.cdUp(); - QFile container(m_containerFile.absoluteFilePath()); - auto dirName = FS::DirNameFromString(m_actualName, parentDir.absolutePath()); - container.rename(parentDir.absoluteFilePath(dirName)); - - return true; -} - -static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString()) -{ - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::String) - { - return fallback; - } - auto & tag_str = namedValue.as(); - return QString::fromStdString(tag_str.get()); - } - catch(std::out_of_range e) - { - // fallback for old world formats - qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; - } - catch(std::bad_cast e) - { - // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback; - return fallback; - } -}; - -static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0) -{ - try - { - auto &namedValue = parent.at(name); - if(namedValue.get_type() != nbt::tag_type::Long) - { - return fallback; - } - auto & tag_str = namedValue.as(); - return tag_str.get(); - } - catch(std::out_of_range e) - { - // fallback for old world formats - qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; - } - catch(std::bad_cast e) - { - // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback; - return fallback; - } -}; - -void World::loadFromLevelDat(QByteArray data) -{ - try - { - auto levelData = parseLevelDat(data); - if(!levelData) - { - is_valid = false; - return; - } - - auto &val = levelData->at("Data"); - is_valid = val.get_type() == nbt::tag_type::Compound; - if(!is_valid) - return; - - m_actualName = read_string(val, "LevelName", m_folderName); - - - int64_t temp = read_long(val, "LastPlayed", 0); - if(temp == 0) - { - m_lastPlayed = levelDatTime; - } - else - { - m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp); - } - - m_randomSeed = read_long(val, "RandomSeed", 0); - - qDebug() << "World Name:" << m_actualName; - qDebug() << "Last Played:" << m_lastPlayed.toString(); - qDebug() << "Seed:" << m_randomSeed; - } - catch (nbt::io::input_error e) - { - qWarning() << "Unable to load" << m_folderName << ":" << e.what(); - is_valid = false; - return; - } -} - -bool World::replace(World &with) -{ - if (!destroy()) - return false; - bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())(); - if (success) - { - m_folderName = with.m_folderName; - m_containerFile.refresh(); - } - return success; -} - -bool World::destroy() -{ - if(!is_valid) return false; - if (m_containerFile.isDir()) - { - QDir d(m_containerFile.filePath()); - return d.removeRecursively(); - } - else if(m_containerFile.isFile()) - { - QFile file(m_containerFile.absoluteFilePath()); - return file.remove(); - } - return true; -} - -bool World::operator==(const World &other) const -{ - return is_valid == other.is_valid && folderName() == other.folderName(); -} -bool World::strongCompare(const World &other) const -{ - return is_valid == other.is_valid && folderName() == other.folderName(); -} diff --git a/libraries/logic/minecraft/World.h b/libraries/logic/minecraft/World.h deleted file mode 100644 index 3cde5ea4..00000000 --- a/libraries/logic/minecraft/World.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2015 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. - */ - -#pragma once -#include -#include - -#include "multimc_logic_export.h" - -class MULTIMC_LOGIC_EXPORT World -{ -public: - World(const QFileInfo &file); - QString folderName() const - { - return m_folderName; - } - QString name() const - { - return m_actualName; - } - QDateTime lastPlayed() const - { - return m_lastPlayed; - } - int64_t seed() const - { - return m_randomSeed; - } - bool isValid() const - { - return is_valid; - } - bool isOnFS() const - { - return m_containerFile.isDir(); - } - QFileInfo container() const - { - return m_containerFile; - } - // delete all the files of this world - bool destroy(); - // replace this world with a copy of the other - bool replace(World &with); - // change the world's filesystem path (used by world lists for *MAGIC* purposes) - void repath(const QFileInfo &file); - - bool rename(const QString &to); - bool install(const QString &to, const QString &name= QString()); - - // WEAK compare operator - used for replacing worlds - bool operator==(const World &other) const; - bool strongCompare(const World &other) const; - -private: - void readFromZip(const QFileInfo &file); - void readFromFS(const QFileInfo &file); - void loadFromLevelDat(QByteArray data); - -protected: - - QFileInfo m_containerFile; - QString m_containerOffsetPath; - QString m_folderName; - QString m_actualName; - QDateTime levelDatTime; - QDateTime m_lastPlayed; - int64_t m_randomSeed = 0; - bool is_valid = false; -}; diff --git a/libraries/logic/minecraft/WorldList.cpp b/libraries/logic/minecraft/WorldList.cpp deleted file mode 100644 index 42c8a3e6..00000000 --- a/libraries/logic/minecraft/WorldList.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/* Copyright 2015 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. - */ - -#include "WorldList.h" -#include -#include -#include -#include -#include -#include -#include - -WorldList::WorldList(const QString &dir) - : QAbstractListModel(), m_dir(dir) -{ - FS::ensureFolderPathExists(m_dir.absolutePath()); - m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | - QDir::NoSymLinks); - m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); - m_watcher = new QFileSystemWatcher(this); - is_watching = false; - connect(m_watcher, SIGNAL(directoryChanged(QString)), this, - SLOT(directoryChanged(QString))); -} - -void WorldList::startWatching() -{ - update(); - is_watching = m_watcher->addPath(m_dir.absolutePath()); - if (is_watching) - { - qDebug() << "Started watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to start watching " << m_dir.absolutePath(); - } -} - -void WorldList::stopWatching() -{ - is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) - { - qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); - } -} - -bool WorldList::update() -{ - if (!isValid()) - return false; - - QList newWorlds; - m_dir.refresh(); - auto folderContents = m_dir.entryInfoList(); - // if there are any untracked files... - for (QFileInfo entry : folderContents) - { - if(!entry.isDir()) - continue; - - World w(entry); - if(w.isValid()) - { - newWorlds.append(w); - } - } - beginResetModel(); - worlds.swap(newWorlds); - endResetModel(); - return true; -} - -void WorldList::directoryChanged(QString path) -{ - update(); -} - -bool WorldList::isValid() -{ - return m_dir.exists() && m_dir.isReadable(); -} - -bool WorldList::deleteWorld(int index) -{ - if (index >= worlds.size() || index < 0) - return false; - World &m = worlds[index]; - if (m.destroy()) - { - beginRemoveRows(QModelIndex(), index, index); - worlds.removeAt(index); - endRemoveRows(); - emit changed(); - return true; - } - return false; -} - -bool WorldList::deleteWorlds(int first, int last) -{ - for (int i = first; i <= last; i++) - { - World &m = worlds[i]; - m.destroy(); - } - beginRemoveRows(QModelIndex(), first, last); - worlds.erase(worlds.begin() + first, worlds.begin() + last + 1); - endRemoveRows(); - emit changed(); - return true; -} - -int WorldList::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -QVariant WorldList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= worlds.size()) - return QVariant(); - - auto & world = worlds[row]; - switch (role) - { - case Qt::DisplayRole: - switch (column) - { - case NameColumn: - return world.name(); - - case LastPlayedColumn: - return world.lastPlayed(); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - { - return world.folderName(); - } - case ObjectRole: - { - return QVariant::fromValue((void *)&world); - } - case FolderRole: - { - return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName())); - } - case SeedRole: - { - return qVariantFromValue(world.seed()); - } - case NameRole: - { - return world.name(); - } - case LastPlayedRole: - { - return world.lastPlayed(); - } - default: - return QVariant(); - } -} - -QVariant WorldList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case NameColumn: - return tr("Name"); - case LastPlayedColumn: - return tr("Last Played"); - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case NameColumn: - return tr("The name of the world."); - case LastPlayedColumn: - return tr("Date and time the world was last played."); - default: - return QVariant(); - } - default: - return QVariant(); - } - return QVariant(); -} - -QStringList WorldList::mimeTypes() const -{ - QStringList types; - types << "text/uri-list"; - return types; -} - -class WorldMimeData : public QMimeData -{ -Q_OBJECT - -public: - WorldMimeData(QList worlds) - { - m_worlds = worlds; - - } - QStringList formats() const - { - return QMimeData::formats() << "text/uri-list"; - } - -protected: - QVariant retrieveData(const QString &mimetype, QVariant::Type type) const - { - QList urls; - for(auto &world: m_worlds) - { - if(!world.isValid() || !world.isOnFS()) - continue; - QString worldPath = world.container().absoluteFilePath(); - qDebug() << worldPath; - urls.append(QUrl::fromLocalFile(worldPath)); - } - const_cast(this)->setUrls(urls); - return QMimeData::retrieveData(mimetype, type); - } -private: - QList m_worlds; -}; - -QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const -{ - if (indexes.size() == 0) - return new QMimeData(); - - QList worlds; - for(auto idx : indexes) - { - if(idx.column() != 0) - continue; - int row = idx.row(); - if (row < 0 || row >= this->worlds.size()) - continue; - worlds.append(this->worlds[row]); - } - if(!worlds.size()) - { - return new QMimeData(); - } - return new WorldMimeData(worlds); -} - -Qt::ItemFlags WorldList::flags(const QModelIndex &index) const -{ - Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); - if (index.isValid()) - return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | - defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; -} - -Qt::DropActions WorldList::supportedDragActions() const -{ - // move to other mod lists or VOID - return Qt::MoveAction; -} - -Qt::DropActions WorldList::supportedDropActions() const -{ - // copy from outside, move from within and other mod lists - return Qt::CopyAction | Qt::MoveAction; -} - -void WorldList::installWorld(QFileInfo filename) -{ - qDebug() << "installing: " << filename.absoluteFilePath(); - World w(filename); - if(!w.isValid()) - { - return; - } - w.install(m_dir.absolutePath()); -} - -bool WorldList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, - const QModelIndex &parent) -{ - if (action == Qt::IgnoreAction) - return true; - // check if the action is supported - if (!data || !(action & supportedDropActions())) - return false; - // files dropped from outside? - if (data->hasUrls()) - { - bool was_watching = is_watching; - if (was_watching) - stopWatching(); - auto urls = data->urls(); - for (auto url : urls) - { - // only local files may be dropped... - if (!url.isLocalFile()) - continue; - QString filename = url.toLocalFile(); - - QFileInfo worldInfo(filename); - - if(!m_dir.entryInfoList().contains(worldInfo)) - { - installWorld(worldInfo); - } - } - if (was_watching) - startWatching(); - return true; - } - return false; -} - -#include "WorldList.moc" diff --git a/libraries/logic/minecraft/WorldList.h b/libraries/logic/minecraft/WorldList.h deleted file mode 100644 index 34b30e9c..00000000 --- a/libraries/logic/minecraft/WorldList.h +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2015 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include "minecraft/World.h" - -#include "multimc_logic_export.h" - -class QFileSystemWatcher; - -class MULTIMC_LOGIC_EXPORT WorldList : public QAbstractListModel -{ - Q_OBJECT -public: - enum Columns - { - NameColumn, - LastPlayedColumn - }; - - enum Roles - { - ObjectRole = Qt::UserRole + 1, - FolderRole, - SeedRole, - NameRole, - LastPlayedRole - }; - - WorldList(const QString &dir); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const - { - return size(); - }; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex &parent) const; - - size_t size() const - { - return worlds.size(); - }; - bool empty() const - { - return size() == 0; - } - World &operator[](size_t index) - { - return worlds[index]; - } - - /// Reloads the mod list and returns true if the list changed. - virtual bool update(); - - /// Install a world from location - void installWorld(QFileInfo filename); - - /// Deletes the mod at the given index. - virtual bool deleteWorld(int index); - - /// Deletes all the selected mods - virtual bool deleteWorlds(int first, int last); - - /// flags, mostly to support drag&drop - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - /// get data for drag action - virtual QMimeData *mimeData(const QModelIndexList &indexes) const; - /// get the supported mime types - virtual QStringList mimeTypes() const; - /// process data from drop action - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); - /// what drag actions do we support? - virtual Qt::DropActions supportedDragActions() const; - - /// what drop actions do we support? - virtual Qt::DropActions supportedDropActions() const; - - void startWatching(); - void stopWatching(); - - virtual bool isValid(); - - QDir dir() const - { - return m_dir; - } - - const QList &allWorlds() const - { - return worlds; - } - -private slots: - void directoryChanged(QString path); - -signals: - void changed(); - -protected: - QFileSystemWatcher *m_watcher; - bool is_watching; - QDir m_dir; - QList worlds; -}; diff --git a/libraries/logic/minecraft/auth/AuthSession.cpp b/libraries/logic/minecraft/auth/AuthSession.cpp deleted file mode 100644 index 8758bfbd..00000000 --- a/libraries/logic/minecraft/auth/AuthSession.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "AuthSession.h" -#include -#include -#include -#include - -QString AuthSession::serializeUserProperties() -{ - QJsonObject userAttrs; - for (auto key : u.properties.keys()) - { - auto array = QJsonArray::fromStringList(u.properties.values(key)); - userAttrs.insert(key, array); - } - QJsonDocument value(userAttrs); - return value.toJson(QJsonDocument::Compact); - -} - -bool AuthSession::MakeOffline(QString offline_playername) -{ - if (status != PlayableOffline && status != PlayableOnline) - { - return false; - } - session = "-"; - player_name = offline_playername; - status = PlayableOffline; - return true; -} diff --git a/libraries/logic/minecraft/auth/AuthSession.h b/libraries/logic/minecraft/auth/AuthSession.h deleted file mode 100644 index dede90a9..00000000 --- a/libraries/logic/minecraft/auth/AuthSession.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "multimc_logic_export.h" - -struct User -{ - QString id; - QMultiMap properties; -}; - -struct MULTIMC_LOGIC_EXPORT AuthSession -{ - bool MakeOffline(QString offline_playername); - - QString serializeUserProperties(); - - enum Status - { - Undetermined, - RequiresPassword, - PlayableOffline, - PlayableOnline - } status = Undetermined; - - User u; - - // client token - QString client_token; - // account user name - QString username; - // combined session ID - QString session; - // volatile auth token - QString access_token; - // profile name - QString player_name; - // profile ID - QString uuid; - // 'legacy' or 'mojang', depending on account type - QString user_type; - // Did the auth server reply? - bool auth_server_online = false; - // Did the user request online mode? - bool wants_online = true; -}; - -typedef std::shared_ptr AuthSessionPtr; diff --git a/libraries/logic/minecraft/auth/MojangAccount.cpp b/libraries/logic/minecraft/auth/MojangAccount.cpp deleted file mode 100644 index 69a24c09..00000000 --- a/libraries/logic/minecraft/auth/MojangAccount.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Authors: Orochimarufan - * - * 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. - */ - -#include "MojangAccount.h" -#include "flows/RefreshTask.h" -#include "flows/AuthenticateTask.h" - -#include -#include -#include -#include -#include -#include - -#include - -MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) -{ - // The JSON object must at least have a username for it to be valid. - if (!object.value("username").isString()) - { - qCritical() << "Can't load Mojang account info from JSON object. Username field is " - "missing or of the wrong type."; - return nullptr; - } - - QString username = object.value("username").toString(""); - QString clientToken = object.value("clientToken").toString(""); - QString accessToken = object.value("accessToken").toString(""); - - QJsonArray profileArray = object.value("profiles").toArray(); - if (profileArray.size() < 1) - { - qCritical() << "Can't load Mojang account with username \"" << username - << "\". No profiles found."; - return nullptr; - } - - QList profiles; - for (QJsonValue profileVal : profileArray) - { - QJsonObject profileObject = profileVal.toObject(); - QString id = profileObject.value("id").toString(""); - QString name = profileObject.value("name").toString(""); - bool legacy = profileObject.value("legacy").toBool(false); - if (id.isEmpty() || name.isEmpty()) - { - qWarning() << "Unable to load a profile because it was missing an ID or a name."; - continue; - } - profiles.append({id, name, legacy}); - } - - MojangAccountPtr account(new MojangAccount()); - if (object.value("user").isObject()) - { - User u; - QJsonObject userStructure = object.value("user").toObject(); - u.id = userStructure.value("id").toString(); - /* - QJsonObject propMap = userStructure.value("properties").toObject(); - for(auto key: propMap.keys()) - { - auto values = propMap.operator[](key).toArray(); - for(auto value: values) - u.properties.insert(key, value.toString()); - } - */ - account->m_user = u; - } - account->m_username = username; - account->m_clientToken = clientToken; - account->m_accessToken = accessToken; - account->m_profiles = profiles; - - // Get the currently selected profile. - QString currentProfile = object.value("activeProfile").toString(""); - if (!currentProfile.isEmpty()) - account->setCurrentProfile(currentProfile); - - return account; -} - -MojangAccountPtr MojangAccount::createFromUsername(const QString &username) -{ - MojangAccountPtr account(new MojangAccount()); - account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); - account->m_username = username; - return account; -} - -QJsonObject MojangAccount::saveToJson() const -{ - QJsonObject json; - json.insert("username", m_username); - json.insert("clientToken", m_clientToken); - json.insert("accessToken", m_accessToken); - - QJsonArray profileArray; - for (AccountProfile profile : m_profiles) - { - QJsonObject profileObj; - profileObj.insert("id", profile.id); - profileObj.insert("name", profile.name); - profileObj.insert("legacy", profile.legacy); - profileArray.append(profileObj); - } - json.insert("profiles", profileArray); - - QJsonObject userStructure; - { - userStructure.insert("id", m_user.id); - /* - QJsonObject userAttrs; - for(auto key: m_user.properties.keys()) - { - auto array = QJsonArray::fromStringList(m_user.properties.values(key)); - userAttrs.insert(key, array); - } - userStructure.insert("properties", userAttrs); - */ - } - json.insert("user", userStructure); - - if (m_currentProfile != -1) - json.insert("activeProfile", currentProfile()->id); - - return json; -} - -bool MojangAccount::setCurrentProfile(const QString &profileId) -{ - for (int i = 0; i < m_profiles.length(); i++) - { - if (m_profiles[i].id == profileId) - { - m_currentProfile = i; - return true; - } - } - return false; -} - -const AccountProfile *MojangAccount::currentProfile() const -{ - if (m_currentProfile == -1) - return nullptr; - return &m_profiles[m_currentProfile]; -} - -AccountStatus MojangAccount::accountStatus() const -{ - if (m_accessToken.isEmpty()) - return NotVerified; - else - return Verified; -} - -std::shared_ptr MojangAccount::login(AuthSessionPtr session, - QString password) -{ - Q_ASSERT(m_currentTask.get() == nullptr); - - // take care of the true offline status - if (accountStatus() == NotVerified && password.isEmpty()) - { - if (session) - { - session->status = AuthSession::RequiresPassword; - fillSession(session); - } - return nullptr; - } - - if (password.isEmpty()) - { - m_currentTask.reset(new RefreshTask(this)); - } - else - { - m_currentTask.reset(new AuthenticateTask(this, password)); - } - m_currentTask->assignSession(session); - - connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); - connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); - return m_currentTask; -} - -void MojangAccount::authSucceeded() -{ - auto session = m_currentTask->getAssignedSession(); - if (session) - { - session->status = - session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline; - fillSession(session); - session->auth_server_online = true; - } - m_currentTask.reset(); - emit changed(); -} - -void MojangAccount::authFailed(QString reason) -{ - auto session = m_currentTask->getAssignedSession(); - // This is emitted when the yggdrasil tasks time out or are cancelled. - // -> we treat the error as no-op - if (m_currentTask->state() == YggdrasilTask::STATE_FAILED_SOFT) - { - if (session) - { - session->status = accountStatus() == Verified ? AuthSession::PlayableOffline - : AuthSession::RequiresPassword; - session->auth_server_online = false; - fillSession(session); - } - } - else - { - m_accessToken = QString(); - emit changed(); - if (session) - { - session->status = AuthSession::RequiresPassword; - session->auth_server_online = true; - fillSession(session); - } - } - m_currentTask.reset(); -} - -void MojangAccount::fillSession(AuthSessionPtr session) -{ - // the user name. you have to have an user name - session->username = m_username; - // volatile auth token - session->access_token = m_accessToken; - // the semi-permanent client token - session->client_token = m_clientToken; - if (currentProfile()) - { - // profile name - session->player_name = currentProfile()->name; - // profile ID - session->uuid = currentProfile()->id; - // 'legacy' or 'mojang', depending on account type - session->user_type = currentProfile()->legacy ? "legacy" : "mojang"; - if (!session->access_token.isEmpty()) - { - session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id; - } - else - { - session->session = "-"; - } - } - else - { - session->player_name = "Player"; - session->session = "-"; - } - session->u = user(); -} diff --git a/libraries/logic/minecraft/auth/MojangAccount.h b/libraries/logic/minecraft/auth/MojangAccount.h deleted file mode 100644 index 2de0c19c..00000000 --- a/libraries/logic/minecraft/auth/MojangAccount.h +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include "AuthSession.h" - -#include "multimc_logic_export.h" - -class Task; -class YggdrasilTask; -class MojangAccount; - -typedef std::shared_ptr MojangAccountPtr; -Q_DECLARE_METATYPE(MojangAccountPtr) - -/** - * A profile within someone's Mojang account. - * - * Currently, the profile system has not been implemented by Mojang yet, - * but we might as well add some things for it in MultiMC right now so - * we don't have to rip the code to pieces to add it later. - */ -struct AccountProfile -{ - QString id; - QString name; - bool legacy; -}; - -enum AccountStatus -{ - NotVerified, - Verified -}; - -/** - * Object that stores information about a certain Mojang account. - * - * Said information may include things such as that account's username, client token, and access - * token if the user chose to stay logged in. - */ -class MULTIMC_LOGIC_EXPORT MojangAccount : public QObject -{ - Q_OBJECT -public: /* construction */ - //! Do not copy accounts. ever. - explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete; - - //! Default constructor - explicit MojangAccount(QObject *parent = 0) : QObject(parent) {}; - - //! Creates an empty account for the specified user name. - static MojangAccountPtr createFromUsername(const QString &username); - - //! Loads a MojangAccount from the given JSON object. - static MojangAccountPtr loadFromJson(const QJsonObject &json); - - //! Saves a MojangAccount to a JSON object and returns it. - QJsonObject saveToJson() const; - -public: /* manipulation */ - /** - * Sets the currently selected profile to the profile with the given ID string. - * If profileId is not in the list of available profiles, the function will simply return - * false. - */ - bool setCurrentProfile(const QString &profileId); - - /** - * Attempt to login. Empty password means we use the token. - * If the attempt fails because we already are performing some task, it returns false. - */ - std::shared_ptr login(AuthSessionPtr session, - QString password = QString()); - -public: /* queries */ - const QString &username() const - { - return m_username; - } - - const QString &clientToken() const - { - return m_clientToken; - } - - const QString &accessToken() const - { - return m_accessToken; - } - - const QList &profiles() const - { - return m_profiles; - } - - const User &user() - { - return m_user; - } - - //! Returns the currently selected profile (if none, returns nullptr) - const AccountProfile *currentProfile() const; - - //! Returns whether the account is NotVerified, Verified or Online - AccountStatus accountStatus() const; - -signals: - /** - * This signal is emitted when the account changes - */ - void changed(); - - // TODO: better signalling for the various possible state changes - especially errors - -protected: /* variables */ - QString m_username; - - // Used to identify the client - the user can have multiple clients for the same account - // Think: different launchers, all connecting to the same account/profile - QString m_clientToken; - - // Blank if not logged in. - QString m_accessToken; - - // Index of the selected profile within the list of available - // profiles. -1 if nothing is selected. - int m_currentProfile = -1; - - // List of available profiles. - QList m_profiles; - - // the user structure, whatever it is. - User m_user; - - // current task we are executing here - std::shared_ptr m_currentTask; - -private -slots: - void authSucceeded(); - void authFailed(QString reason); - -private: - void fillSession(AuthSessionPtr session); - -public: - friend class YggdrasilTask; - friend class AuthenticateTask; - friend class ValidateTask; - friend class RefreshTask; -}; diff --git a/libraries/logic/minecraft/auth/MojangAccountList.cpp b/libraries/logic/minecraft/auth/MojangAccountList.cpp deleted file mode 100644 index 26cbc81a..00000000 --- a/libraries/logic/minecraft/auth/MojangAccountList.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "MojangAccountList.h" -#include "MojangAccount.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define ACCOUNT_LIST_FORMAT_VERSION 2 - -MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent) -{ -} - -MojangAccountPtr MojangAccountList::findAccount(const QString &username) const -{ - for (int i = 0; i < count(); i++) - { - MojangAccountPtr account = at(i); - if (account->username() == username) - return account; - } - return nullptr; -} - -const MojangAccountPtr MojangAccountList::at(int i) const -{ - return MojangAccountPtr(m_accounts.at(i)); -} - -void MojangAccountList::addAccount(const MojangAccountPtr account) -{ - beginResetModel(); - connect(account.get(), SIGNAL(changed()), SLOT(accountChanged())); - m_accounts.append(account); - endResetModel(); - onListChanged(); -} - -void MojangAccountList::removeAccount(const QString &username) -{ - beginResetModel(); - for (auto account : m_accounts) - { - if (account->username() == username) - { - m_accounts.removeOne(account); - return; - } - } - endResetModel(); - onListChanged(); -} - -void MojangAccountList::removeAccount(QModelIndex index) -{ - beginResetModel(); - m_accounts.removeAt(index.row()); - endResetModel(); - onListChanged(); -} - -MojangAccountPtr MojangAccountList::activeAccount() const -{ - return m_activeAccount; -} - -void MojangAccountList::setActiveAccount(const QString &username) -{ - beginResetModel(); - if (username.isEmpty()) - { - m_activeAccount = nullptr; - } - else - { - for (MojangAccountPtr account : m_accounts) - { - if (account->username() == username) - m_activeAccount = account; - } - } - endResetModel(); - onActiveChanged(); -} - -void MojangAccountList::accountChanged() -{ - // the list changed. there is no doubt. - onListChanged(); -} - -void MojangAccountList::onListChanged() -{ - if (m_autosave) - // TODO: Alert the user if this fails. - saveList(); - - emit listChanged(); -} - -void MojangAccountList::onActiveChanged() -{ - if (m_autosave) - saveList(); - - emit activeAccountChanged(); -} - -int MojangAccountList::count() const -{ - return m_accounts.count(); -} - -QVariant MojangAccountList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - MojangAccountPtr account = at(index.row()); - - switch (role) - { - case Qt::DisplayRole: - switch (index.column()) - { - case NameColumn: - return account->username(); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - return account->username(); - - case PointerRole: - return qVariantFromValue(account); - - case Qt::CheckStateRole: - switch (index.column()) - { - case ActiveColumn: - return account == m_activeAccount; - } - - default: - return QVariant(); - } -} - -QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case ActiveColumn: - return tr("Active?"); - - case NameColumn: - return tr("Name"); - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case NameColumn: - return tr("The name of the version."); - - default: - return QVariant(); - } - - default: - return QVariant(); - } -} - -int MojangAccountList::rowCount(const QModelIndex &parent) const -{ - // Return count - return count(); -} - -int MojangAccountList::columnCount(const QModelIndex &parent) const -{ - return 2; -} - -Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const -{ - if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) - { - return Qt::NoItemFlags; - } - - return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) - { - return false; - } - - if(role == Qt::CheckStateRole) - { - if(value == Qt::Checked) - { - MojangAccountPtr account = this->at(index.row()); - this->setActiveAccount(account->username()); - } - } - - emit dataChanged(index, index); - return true; -} - -void MojangAccountList::updateListData(QList versions) -{ - beginResetModel(); - m_accounts = versions; - endResetModel(); -} - -bool MojangAccountList::loadList(const QString &filePath) -{ - QString path = filePath; - if (path.isEmpty()) - path = m_listFilePath; - if (path.isEmpty()) - { - qCritical() << "Can't load Mojang account list. No file path given and no default set."; - return false; - } - - QFile file(path); - - // Try to open the file and fail if we can't. - // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::ReadOnly)) - { - qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8(); - return false; - } - - // Read the file and close it. - QByteArray jsonData = file.readAll(); - file.close(); - - QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); - - // Fail if the JSON is invalid. - if (parseError.error != QJsonParseError::NoError) - { - qCritical() << QString("Failed to parse account list file: %1 at offset %2") - .arg(parseError.errorString(), QString::number(parseError.offset)) - .toUtf8(); - return false; - } - - // Make sure the root is an object. - if (!jsonDoc.isObject()) - { - qCritical() << "Invalid account list JSON: Root should be an array."; - return false; - } - - QJsonObject root = jsonDoc.object(); - - // Make sure the format version matches. - if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION) - { - QString newName = "accounts-old.json"; - qWarning() << "Format version mismatch when loading account list. Existing one will be renamed to" - << newName; - - // Attempt to rename the old version. - file.rename(newName); - return false; - } - - // Now, load the accounts array. - beginResetModel(); - QJsonArray accounts = root.value("accounts").toArray(); - for (QJsonValue accountVal : accounts) - { - QJsonObject accountObj = accountVal.toObject(); - MojangAccountPtr account = MojangAccount::loadFromJson(accountObj); - if (account.get() != nullptr) - { - connect(account.get(), SIGNAL(changed()), SLOT(accountChanged())); - m_accounts.append(account); - } - else - { - qWarning() << "Failed to load an account."; - } - } - // Load the active account. - m_activeAccount = findAccount(root.value("activeAccount").toString("")); - endResetModel(); - return true; -} - -bool MojangAccountList::saveList(const QString &filePath) -{ - QString path(filePath); - if (path.isEmpty()) - path = m_listFilePath; - if (path.isEmpty()) - { - qCritical() << "Can't save Mojang account list. No file path given and no default set."; - return false; - } - - // make sure the parent folder exists - if(!FS::ensureFilePathExists(path)) - return false; - - // make sure the file wasn't overwritten with a folder before (fixes a bug) - QFileInfo finfo(path); - if(finfo.isDir()) - { - QDir badDir(path); - badDir.removeRecursively(); - } - - qDebug() << "Writing account list to" << path; - - qDebug() << "Building JSON data structure."; - // Build the JSON document to write to the list file. - QJsonObject root; - - root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION); - - // Build a list of accounts. - qDebug() << "Building account array."; - QJsonArray accounts; - for (MojangAccountPtr account : m_accounts) - { - QJsonObject accountObj = account->saveToJson(); - accounts.append(accountObj); - } - - // Insert the account list into the root object. - root.insert("accounts", accounts); - - if(m_activeAccount) - { - // Save the active account. - root.insert("activeAccount", m_activeAccount->username()); - } - - // Create a JSON document object to convert our JSON to bytes. - QJsonDocument doc(root); - - // Now that we're done building the JSON object, we can write it to the file. - qDebug() << "Writing account list to file."; - QFile file(path); - - // Try to open the file and fail if we can't. - // TODO: We should probably report this error to the user. - if (!file.open(QIODevice::WriteOnly)) - { - qCritical() << QString("Failed to read the account list file (%1).").arg(path).toUtf8(); - return false; - } - - // Write the JSON to the file. - file.write(doc.toJson()); - file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser); - file.close(); - - qDebug() << "Saved account list to" << path; - - return true; -} - -void MojangAccountList::setListFilePath(QString path, bool autosave) -{ - m_listFilePath = path; - m_autosave = autosave; -} - -bool MojangAccountList::anyAccountIsValid() -{ - for(auto account:m_accounts) - { - if(account->accountStatus() != NotVerified) - return true; - } - return false; -} diff --git a/libraries/logic/minecraft/auth/MojangAccountList.h b/libraries/logic/minecraft/auth/MojangAccountList.h deleted file mode 100644 index c40fa6a3..00000000 --- a/libraries/logic/minecraft/auth/MojangAccountList.h +++ /dev/null @@ -1,201 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include "MojangAccount.h" - -#include -#include -#include -#include - -#include "multimc_logic_export.h" - -/*! - * \brief List of available Mojang accounts. - * This should be loaded in the background by MultiMC on startup. - * - * This class also inherits from QAbstractListModel. Methods from that - * class determine how this list shows up in a list view. Said methods - * all have a default implementation, but they can be overridden by subclasses to - * change the behavior of the list. - */ -class MULTIMC_LOGIC_EXPORT MojangAccountList : public QAbstractListModel -{ - Q_OBJECT -public: - enum ModelRoles - { - PointerRole = 0x34B1CB48 - }; - - enum VListColumns - { - // TODO: Add icon column. - - // First column - Active? - ActiveColumn = 0, - - // Second column - Name - NameColumn, - }; - - explicit MojangAccountList(QObject *parent = 0); - - //! Gets the account at the given index. - virtual const MojangAccountPtr at(int i) const; - - //! Returns the number of accounts in the list. - virtual int count() const; - - //////// List Model Functions //////// - virtual QVariant data(const QModelIndex &index, int role) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; - virtual int rowCount(const QModelIndex &parent) const; - virtual int columnCount(const QModelIndex &parent) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role); - - /*! - * Adds a the given Mojang account to the account list. - */ - virtual void addAccount(const MojangAccountPtr account); - - /*! - * Removes the mojang account with the given username from the account list. - */ - virtual void removeAccount(const QString &username); - - /*! - * Removes the account at the given QModelIndex. - */ - virtual void removeAccount(QModelIndex index); - - /*! - * \brief Finds an account by its username. - * \param The username of the account to find. - * \return A const pointer to the account with the given username. NULL if - * one doesn't exist. - */ - virtual MojangAccountPtr findAccount(const QString &username) const; - - /*! - * Sets the default path to save the list file to. - * If autosave is true, this list will automatically save to the given path whenever it changes. - * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately - * after calling this function to ensure an autosaved change doesn't overwrite the list you intended - * to load. - */ - virtual void setListFilePath(QString path, bool autosave = false); - - /*! - * \brief Loads the account list from the given file path. - * If the given file is an empty string (default), will load from the default account list file. - * \return True if successful, otherwise false. - */ - virtual bool loadList(const QString &file = ""); - - /*! - * \brief Saves the account list to the given file. - * If the given file is an empty string (default), will save from the default account list file. - * \return True if successful, otherwise false. - */ - virtual bool saveList(const QString &file = ""); - - /*! - * \brief Gets a pointer to the account that the user has selected as their "active" account. - * Which account is active can be overridden on a per-instance basis, but this will return the one that - * is set as active globally. - * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer. - */ - virtual MojangAccountPtr activeAccount() const; - - /*! - * Sets the given account as the current active account. - * If the username given is an empty string, sets the active account to nothing. - */ - virtual void setActiveAccount(const QString &username); - - /*! - * Returns true if any of the account is at least Validated - */ - bool anyAccountIsValid(); - -signals: - /*! - * Signal emitted to indicate that the account list has changed. - * This will also fire if the value of an element in the list changes (will be implemented - * later). - */ - void listChanged(); - - /*! - * Signal emitted to indicate that the active account has changed. - */ - void activeAccountChanged(); - -public -slots: - /** - * This is called when one of the accounts changes and the list needs to be updated - */ - void accountChanged(); - -protected: - /*! - * Called whenever the list changes. - * This emits the listChanged() signal and autosaves the list (if autosave is enabled). - */ - void onListChanged(); - - /*! - * Called whenever the active account changes. - * Emits the activeAccountChanged() signal and autosaves the list if enabled. - */ - void onActiveChanged(); - - QList m_accounts; - - /*! - * Account that is currently active. - */ - MojangAccountPtr m_activeAccount; - - //! Path to the account list file. Empty string if there isn't one. - QString m_listFilePath; - - /*! - * If true, the account list will automatically save to the account list path when it changes. - * Ignored if m_listFilePath is blank. - */ - bool m_autosave = false; - -protected -slots: - /*! - * Updates this list with the given list of accounts. - * This is done by copying each account in the given list and inserting it - * into this one. - * We need to do this so that we can set the parents of the accounts are set to this - * account list. This can't be done in the load task, because the accounts the load - * task creates are on the load task's thread and Qt won't allow their parents - * to be set to something created on another thread. - * To get around that problem, we invoke this method on the GUI thread, which - * then copies the accounts and sets their parents correctly. - * \param accounts List of accounts whose parents should be set. - */ - virtual void updateListData(QList versions); -}; diff --git a/libraries/logic/minecraft/auth/YggdrasilTask.cpp b/libraries/logic/minecraft/auth/YggdrasilTask.cpp deleted file mode 100644 index c6971c9f..00000000 --- a/libraries/logic/minecraft/auth/YggdrasilTask.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "YggdrasilTask.h" -#include "MojangAccount.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent) - : Task(parent), m_account(account) -{ - changeState(STATE_CREATED); -} - -void YggdrasilTask::executeTask() -{ - changeState(STATE_SENDING_REQUEST); - - // Get the content of the request we're going to send to the server. - QJsonDocument doc(getRequestContent()); - - auto worker = ENV.qnam(); - QUrl reqUrl("https://" + URLConstants::AUTH_BASE + getEndpoint()); - QNetworkRequest netRequest(reqUrl); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - - QByteArray requestData = doc.toJson(); - m_netReply = worker->post(netRequest, requestData); - connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply); - connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers); - connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers); - connect(m_netReply, &QNetworkReply::sslErrors, this, &YggdrasilTask::sslErrors); - timeout_keeper.setSingleShot(true); - timeout_keeper.start(timeout_max); - counter.setSingleShot(false); - counter.start(time_step); - progress(0, timeout_max); - connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abortByTimeout); - connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat); -} - -void YggdrasilTask::refreshTimers(qint64, qint64) -{ - timeout_keeper.stop(); - timeout_keeper.start(timeout_max); - progress(count = 0, timeout_max); -} -void YggdrasilTask::heartbeat() -{ - count += time_step; - progress(count, timeout_max); -} - -bool YggdrasilTask::abort() -{ - progress(timeout_max, timeout_max); - // TODO: actually use this in a meaningful way - m_aborted = YggdrasilTask::BY_USER; - m_netReply->abort(); - return true; -} - -void YggdrasilTask::abortByTimeout() -{ - progress(timeout_max, timeout_max); - // TODO: actually use this in a meaningful way - m_aborted = YggdrasilTask::BY_TIMEOUT; - m_netReply->abort(); -} - -void YggdrasilTask::sslErrors(QList errors) -{ - int i = 1; - for (auto error : errors) - { - qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void YggdrasilTask::processReply() -{ - changeState(STATE_PROCESSING_RESPONSE); - - switch (m_netReply->error()) - { - case QNetworkReply::NoError: - break; - case QNetworkReply::TimeoutError: - changeState(STATE_FAILED_SOFT, tr("Authentication operation timed out.")); - return; - case QNetworkReply::OperationCanceledError: - changeState(STATE_FAILED_SOFT, tr("Authentication operation cancelled.")); - return; - case QNetworkReply::SslHandshakeFailedError: - changeState( - STATE_FAILED_SOFT, - tr("SSL Handshake failed.
    There might be a few causes for it:
    " - "
      " - "
    • You use Windows XP and need to update " - "your root certificates
    • " - "
    • Some device on your network is interfering with SSL traffic. In that case, " - "you have bigger worries than Minecraft not starting.
    • " - "
    • Possibly something else. Check the MultiMC log file for details
    • " - "
    ")); - return; - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::ContentOperationNotPermittedError: - break; - default: - changeState(STATE_FAILED_SOFT, - tr("Authentication operation failed due to a network error: %1 (%2)") - .arg(m_netReply->errorString()).arg(m_netReply->error())); - return; - } - - // Try to parse the response regardless of the response code. - // Sometimes the auth server will give more information and an error code. - QJsonParseError jsonError; - QByteArray replyData = m_netReply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); - // Check the response code. - int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) - { - // If the response code was 200, then there shouldn't be an error. Make sure - // anyways. - // Also, sometimes an empty reply indicates success. If there was no data received, - // pass an empty json object to the processResponse function. - if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) - { - processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()); - return; - } - else - { - changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response " - "JSON response: %1 at offset %2.") - .arg(jsonError.errorString()) - .arg(jsonError.offset)); - qCritical() << replyData; - } - return; - } - - // If the response code was not 200, then Yggdrasil may have given us information - // about the error. - // If we can parse the response, then get information from it. Otherwise just say - // there was an unknown error. - if (jsonError.error == QJsonParseError::NoError) - { - // We were able to parse the server's response. Woo! - // Call processError. If a subclass has overridden it then they'll handle their - // stuff there. - qDebug() << "The request failed, but the server gave us an error message. " - "Processing error."; - processError(doc.object()); - } - else - { - // The server didn't say anything regarding the error. Give the user an unknown - // error. - qDebug() - << "The request failed and the server gave no error message. Unknown error."; - changeState(STATE_FAILED_SOFT, - tr("An unknown error occurred when trying to communicate with the " - "authentication server: %1").arg(m_netReply->errorString())); - } -} - -void YggdrasilTask::processError(QJsonObject responseData) -{ - QJsonValue errorVal = responseData.value("error"); - QJsonValue errorMessageValue = responseData.value("errorMessage"); - QJsonValue causeVal = responseData.value("cause"); - - if (errorVal.isString() && errorMessageValue.isString()) - { - m_error = std::shared_ptr(new Error{ - errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")}); - changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose); - } - else - { - // Error is not in standard format. Don't set m_error and return unknown error. - changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred.")); - } -} - -QString YggdrasilTask::getStateMessage() const -{ - switch (m_state) - { - case STATE_CREATED: - return "Waiting..."; - case STATE_SENDING_REQUEST: - return tr("Sending request to auth servers..."); - case STATE_PROCESSING_RESPONSE: - return tr("Processing response from servers..."); - case STATE_SUCCEEDED: - return tr("Authentication task succeeded."); - case STATE_FAILED_SOFT: - return tr("Failed to contact the authentication server."); - case STATE_FAILED_HARD: - return tr("Failed to authenticate."); - default: - return tr("..."); - } -} - -void YggdrasilTask::changeState(YggdrasilTask::State newState, QString reason) -{ - m_state = newState; - setStatus(getStateMessage()); - if (newState == STATE_SUCCEEDED) - { - emitSucceeded(); - } - else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT) - { - emitFailed(reason); - } -} - -YggdrasilTask::State YggdrasilTask::state() -{ - return m_state; -} diff --git a/libraries/logic/minecraft/auth/YggdrasilTask.h b/libraries/logic/minecraft/auth/YggdrasilTask.h deleted file mode 100644 index c84cfc06..00000000 --- a/libraries/logic/minecraft/auth/YggdrasilTask.h +++ /dev/null @@ -1,150 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include - -#include -#include -#include -#include - -#include "MojangAccount.h" - -class QNetworkReply; - -/** - * A Yggdrasil task is a task that performs an operation on a given mojang account. - */ -class YggdrasilTask : public Task -{ - Q_OBJECT -public: - explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0); - - /** - * assign a session to this task. the session will be filled with required infomration - * upon completion - */ - void assignSession(AuthSessionPtr session) - { - m_session = session; - } - - /// get the assigned session for filling with information. - AuthSessionPtr getAssignedSession() - { - return m_session; - } - - /** - * Class describing a Yggdrasil error response. - */ - struct Error - { - QString m_errorMessageShort; - QString m_errorMessageVerbose; - QString m_cause; - }; - - enum AbortedBy - { - BY_NOTHING, - BY_USER, - BY_TIMEOUT - } m_aborted = BY_NOTHING; - - /** - * Enum for describing the state of the current task. - * Used by the getStateMessage function to determine what the status message should be. - */ - enum State - { - STATE_CREATED, - STATE_SENDING_REQUEST, - STATE_PROCESSING_RESPONSE, - STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated - STATE_FAILED_HARD, //!< hard failure. auth is invalid - STATE_SUCCEEDED - } m_state = STATE_CREATED; - -protected: - - virtual void executeTask() override; - - /** - * Gets the JSON object that will be sent to the authentication server. - * Should be overridden by subclasses. - */ - virtual QJsonObject getRequestContent() const = 0; - - /** - * Gets the endpoint to POST to. - * No leading slash. - */ - virtual QString getEndpoint() const = 0; - - /** - * Processes the response received from the server. - * If an error occurred, this should emit a failed signal and return false. - * If Yggdrasil gave an error response, it should call setError() first, and then return false. - * Otherwise, it should return true. - * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with - * an empty QJsonObject. - */ - virtual void processResponse(QJsonObject responseData) = 0; - - /** - * Processes an error response received from the server. - * The default implementation will read data from Yggdrasil's standard error response format and set it as this task's Error. - * \returns a QString error message that will be passed to emitFailed. - */ - virtual void processError(QJsonObject responseData); - - /** - * Returns the state message for the given state. - * Used to set the status message for the task. - * Should be overridden by subclasses that want to change messages for a given state. - */ - virtual QString getStateMessage() const; - -protected -slots: - void processReply(); - void refreshTimers(qint64, qint64); - void heartbeat(); - void sslErrors(QList); - - void changeState(State newState, QString reason=QString()); -public -slots: - virtual bool abort() override; - void abortByTimeout(); - State state(); -protected: - // FIXME: segfault disaster waiting to happen - MojangAccount *m_account = nullptr; - QNetworkReply *m_netReply = nullptr; - std::shared_ptr m_error; - QTimer timeout_keeper; - QTimer counter; - int count = 0; // num msec since time reset - - const int timeout_max = 30000; - const int time_step = 50; - - AuthSessionPtr m_session; -}; diff --git a/libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp b/libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp deleted file mode 100644 index 8d136f0b..00000000 --- a/libraries/logic/minecraft/auth/flows/AuthenticateTask.cpp +++ /dev/null @@ -1,202 +0,0 @@ - -/* Copyright 2013-2015 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. - */ - -#include "AuthenticateTask.h" -#include "../MojangAccount.h" - -#include -#include -#include -#include - -#include -#include - -AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password, - QObject *parent) - : YggdrasilTask(account, parent), m_password(password) -{ -} - -QJsonObject AuthenticateTask::getRequestContent() const -{ - /* - * { - * "agent": { // optional - * "name": "Minecraft", // So far this is the only encountered value - * "version": 1 // This number might be increased - * // by the vanilla client in the future - * }, - * "username": "mojang account name", // Can be an email address or player name for - // unmigrated accounts - * "password": "mojang account password", - * "clientToken": "client identifier" // optional - * "requestUser": true/false // request the user structure - * } - */ - QJsonObject req; - - { - QJsonObject agent; - // C++ makes string literals void* for some stupid reason, so we have to tell it - // QString... Thanks Obama. - agent.insert("name", QString("Minecraft")); - agent.insert("version", 1); - req.insert("agent", agent); - } - - req.insert("username", m_account->username()); - req.insert("password", m_password); - req.insert("requestUser", true); - - // If we already have a client token, give it to the server. - // Otherwise, let the server give us one. - - if(m_account->m_clientToken.isEmpty()) - { - auto uuid = QUuid::createUuid(); - auto uuidString = uuid.toString().remove('{').remove('-').remove('}'); - m_account->m_clientToken = uuidString; - } - req.insert("clientToken", m_account->m_clientToken); - - return req; -} - -void AuthenticateTask::processResponse(QJsonObject responseData) -{ - // Read the response data. We need to get the client token, access token, and the selected - // profile. - qDebug() << "Processing authentication response."; - // qDebug() << responseData; - // If we already have a client token, make sure the one the server gave us matches our - // existing one. - qDebug() << "Getting client token."; - QString clientToken = responseData.value("clientToken").toString(""); - if (clientToken.isEmpty()) - { - // Fail if the server gave us an empty client token - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); - return; - } - if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) - { - changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); - return; - } - // Set the client token. - m_account->m_clientToken = clientToken; - - // Now, we set the access token. - qDebug() << "Getting access token."; - QString accessToken = responseData.value("accessToken").toString(""); - if (accessToken.isEmpty()) - { - // Fail if the server didn't give us an access token. - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); - return; - } - // Set the access token. - m_account->m_accessToken = accessToken; - - // Now we load the list of available profiles. - // Mojang hasn't yet implemented the profile system, - // but we might as well support what's there so we - // don't have trouble implementing it later. - qDebug() << "Loading profile list."; - QJsonArray availableProfiles = responseData.value("availableProfiles").toArray(); - QList loadedProfiles; - for (auto iter : availableProfiles) - { - QJsonObject profile = iter.toObject(); - // Profiles are easy, we just need their ID and name. - QString id = profile.value("id").toString(""); - QString name = profile.value("name").toString(""); - bool legacy = profile.value("legacy").toBool(false); - - if (id.isEmpty() || name.isEmpty()) - { - // This should never happen, but we might as well - // warn about it if it does so we can debug it easily. - // You never know when Mojang might do something truly derpy. - qWarning() << "Found entry in available profiles list with missing ID or name " - "field. Ignoring it."; - } - - // Now, add a new AccountProfile entry to the list. - loadedProfiles.append({id, name, legacy}); - } - // Put the list of profiles we loaded into the MojangAccount object. - m_account->m_profiles = loadedProfiles; - - // Finally, we set the current profile to the correct value. This is pretty simple. - // We do need to make sure that the current profile that the server gave us - // is actually in the available profiles list. - // If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know). - qDebug() << "Setting current profile."; - QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); - QString currentProfileId = currentProfile.value("id").toString(""); - if (currentProfileId.isEmpty()) - { - changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium.")); - return; - } - if (!m_account->setCurrentProfile(currentProfileId)) - { - changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list.")); - return; - } - - // this is what the vanilla launcher passes to the userProperties launch param - if (responseData.contains("user")) - { - User u; - auto obj = responseData.value("user").toObject(); - u.id = obj.value("id").toString(); - auto propArray = obj.value("properties").toArray(); - for (auto prop : propArray) - { - auto propTuple = prop.toObject(); - auto name = propTuple.value("name").toString(); - auto value = propTuple.value("value").toString(); - u.properties.insert(name, value); - } - m_account->m_user = u; - } - - // We've made it through the minefield of possible errors. Return true to indicate that - // we've succeeded. - qDebug() << "Finished reading authentication response."; - changeState(STATE_SUCCEEDED); -} - -QString AuthenticateTask::getEndpoint() const -{ - return "authenticate"; -} - -QString AuthenticateTask::getStateMessage() const -{ - switch (m_state) - { - case STATE_SENDING_REQUEST: - return tr("Authenticating: Sending request..."); - case STATE_PROCESSING_RESPONSE: - return tr("Authenticating: Processing response..."); - default: - return YggdrasilTask::getStateMessage(); - } -} diff --git a/libraries/logic/minecraft/auth/flows/AuthenticateTask.h b/libraries/logic/minecraft/auth/flows/AuthenticateTask.h deleted file mode 100644 index 398fab98..00000000 --- a/libraries/logic/minecraft/auth/flows/AuthenticateTask.h +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include "../YggdrasilTask.h" - -#include -#include -#include - -/** - * The authenticate task takes a MojangAccount with no access token and password and attempts to - * authenticate with Mojang's servers. - * If successful, it will set the MojangAccount's access token. - */ -class AuthenticateTask : public YggdrasilTask -{ - Q_OBJECT -public: - AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0); - -protected: - virtual QJsonObject getRequestContent() const override; - - virtual QString getEndpoint() const override; - - virtual void processResponse(QJsonObject responseData) override; - - virtual QString getStateMessage() const override; - -private: - QString m_password; -}; diff --git a/libraries/logic/minecraft/auth/flows/RefreshTask.cpp b/libraries/logic/minecraft/auth/flows/RefreshTask.cpp deleted file mode 100644 index a0fb2e48..00000000 --- a/libraries/logic/minecraft/auth/flows/RefreshTask.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "RefreshTask.h" -#include "../MojangAccount.h" - -#include -#include -#include -#include - -#include - -RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account) -{ -} - -QJsonObject RefreshTask::getRequestContent() const -{ - /* - * { - * "clientToken": "client identifier" - * "accessToken": "current access token to be refreshed" - * "selectedProfile": // specifying this causes errors - * { - * "id": "profile ID" - * "name": "profile name" - * } - * "requestUser": true/false // request the user structure - * } - */ - QJsonObject req; - req.insert("clientToken", m_account->m_clientToken); - req.insert("accessToken", m_account->m_accessToken); - /* - { - auto currentProfile = m_account->currentProfile(); - QJsonObject profile; - profile.insert("id", currentProfile->id()); - profile.insert("name", currentProfile->name()); - req.insert("selectedProfile", profile); - } - */ - req.insert("requestUser", true); - - return req; -} - -void RefreshTask::processResponse(QJsonObject responseData) -{ - // Read the response data. We need to get the client token, access token, and the selected - // profile. - qDebug() << "Processing authentication response."; - - // qDebug() << responseData; - // If we already have a client token, make sure the one the server gave us matches our - // existing one. - QString clientToken = responseData.value("clientToken").toString(""); - if (clientToken.isEmpty()) - { - // Fail if the server gave us an empty client token - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); - return; - } - if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) - { - changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); - return; - } - - // Now, we set the access token. - qDebug() << "Getting new access token."; - QString accessToken = responseData.value("accessToken").toString(""); - if (accessToken.isEmpty()) - { - // Fail if the server didn't give us an access token. - changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); - return; - } - - // we validate that the server responded right. (our current profile = returned current - // profile) - QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); - QString currentProfileId = currentProfile.value("id").toString(""); - if (m_account->currentProfile()->id != currentProfileId) - { - changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected.")); - return; - } - - // this is what the vanilla launcher passes to the userProperties launch param - if (responseData.contains("user")) - { - User u; - auto obj = responseData.value("user").toObject(); - u.id = obj.value("id").toString(); - auto propArray = obj.value("properties").toArray(); - for (auto prop : propArray) - { - auto propTuple = prop.toObject(); - auto name = propTuple.value("name").toString(); - auto value = propTuple.value("value").toString(); - u.properties.insert(name, value); - } - m_account->m_user = u; - } - - // We've made it through the minefield of possible errors. Return true to indicate that - // we've succeeded. - qDebug() << "Finished reading refresh response."; - // Reset the access token. - m_account->m_accessToken = accessToken; - changeState(STATE_SUCCEEDED); -} - -QString RefreshTask::getEndpoint() const -{ - return "refresh"; -} - -QString RefreshTask::getStateMessage() const -{ - switch (m_state) - { - case STATE_SENDING_REQUEST: - return tr("Refreshing login token..."); - case STATE_PROCESSING_RESPONSE: - return tr("Refreshing login token: Processing response..."); - default: - return YggdrasilTask::getStateMessage(); - } -} diff --git a/libraries/logic/minecraft/auth/flows/RefreshTask.h b/libraries/logic/minecraft/auth/flows/RefreshTask.h deleted file mode 100644 index 17714b4f..00000000 --- a/libraries/logic/minecraft/auth/flows/RefreshTask.h +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include "../YggdrasilTask.h" - -#include -#include -#include - -/** - * The authenticate task takes a MojangAccount with a possibly timed-out access token - * and attempts to authenticate with Mojang's servers. - * If successful, it will set the new access token. The token is considered validated. - */ -class RefreshTask : public YggdrasilTask -{ - Q_OBJECT -public: - RefreshTask(MojangAccount * account); - -protected: - virtual QJsonObject getRequestContent() const override; - - virtual QString getEndpoint() const override; - - virtual void processResponse(QJsonObject responseData) override; - - virtual QString getStateMessage() const override; -}; - diff --git a/libraries/logic/minecraft/auth/flows/ValidateTask.cpp b/libraries/logic/minecraft/auth/flows/ValidateTask.cpp deleted file mode 100644 index 4deceb6a..00000000 --- a/libraries/logic/minecraft/auth/flows/ValidateTask.cpp +++ /dev/null @@ -1,61 +0,0 @@ - -/* Copyright 2013-2015 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. - */ - -#include "ValidateTask.h" -#include "../MojangAccount.h" - -#include -#include -#include -#include - -#include - -ValidateTask::ValidateTask(MojangAccount * account, QObject *parent) - : YggdrasilTask(account, parent) -{ -} - -QJsonObject ValidateTask::getRequestContent() const -{ - QJsonObject req; - req.insert("accessToken", m_account->m_accessToken); - return req; -} - -void ValidateTask::processResponse(QJsonObject responseData) -{ - // Assume that if processError wasn't called, then the request was successful. - changeState(YggdrasilTask::STATE_SUCCEEDED); -} - -QString ValidateTask::getEndpoint() const -{ - return "validate"; -} - -QString ValidateTask::getStateMessage() const -{ - switch (m_state) - { - case YggdrasilTask::STATE_SENDING_REQUEST: - return tr("Validating access token: Sending request..."); - case YggdrasilTask::STATE_PROCESSING_RESPONSE: - return tr("Validating access token: Processing response..."); - default: - return YggdrasilTask::getStateMessage(); - } -} diff --git a/libraries/logic/minecraft/auth/flows/ValidateTask.h b/libraries/logic/minecraft/auth/flows/ValidateTask.h deleted file mode 100644 index 77d628a0..00000000 --- a/libraries/logic/minecraft/auth/flows/ValidateTask.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -/* - * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME: - */ - -#pragma once - -#include "../YggdrasilTask.h" - -#include -#include -#include - -/** - * The validate task takes a MojangAccount and checks to make sure its access token is valid. - */ -class ValidateTask : public YggdrasilTask -{ - Q_OBJECT -public: - ValidateTask(MojangAccount *account, QObject *parent = 0); - -protected: - virtual QJsonObject getRequestContent() const override; - - virtual QString getEndpoint() const override; - - virtual void processResponse(QJsonObject responseData) override; - - virtual QString getStateMessage() const override; - -private: -}; diff --git a/libraries/logic/minecraft/forge/ForgeInstaller.cpp b/libraries/logic/minecraft/forge/ForgeInstaller.cpp deleted file mode 100644 index 353328ab..00000000 --- a/libraries/logic/minecraft/forge/ForgeInstaller.cpp +++ /dev/null @@ -1,458 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "ForgeInstaller.h" -#include "ForgeVersionList.h" - -#include "minecraft/MinecraftProfile.h" -#include "minecraft/GradleSpecifier.h" -#include "net/HttpMetaCache.h" -#include "tasks/Task.h" -#include "minecraft/onesix/OneSixInstance.h" -#include -#include "minecraft/VersionFilterData.h" -#include "minecraft/MinecraftVersion.h" -#include "Env.h" -#include "Exception.h" -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -ForgeInstaller::ForgeInstaller() : BaseInstaller() -{ -} - -void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl) -{ - VersionFilePtr newVersion; - m_universal_url = universalUrl; - - QuaZip zip(filename); - if (!zip.open(QuaZip::mdUnzip)) - return; - - QuaZipFile file(&zip); - - // read the install profile - if (!zip.setCurrentFile("install_profile.json")) - return; - - QJsonParseError jsonError; - if (!file.open(QIODevice::ReadOnly)) - return; - QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &jsonError); - file.close(); - if (jsonError.error != QJsonParseError::NoError) - return; - - if (!jsonDoc.isObject()) - return; - - QJsonObject root = jsonDoc.object(); - - auto installVal = root.value("install"); - auto versionInfoVal = root.value("versionInfo"); - if (!installVal.isObject() || !versionInfoVal.isObject()) - return; - - try - { - newVersion = OneSixVersionFormat::versionFileFromJson(QJsonDocument(versionInfoVal.toObject()), QString(), false); - } - catch(Exception &err) - { - qWarning() << "Forge: Fatal error while parsing version file:" << err.what(); - return; - } - - for(auto problem: newVersion->getProblems()) - { - qWarning() << "Forge: Problem found: " << problem.getDescription(); - } - if(newVersion->getProblemSeverity() == ProblemSeverity::PROBLEM_ERROR) - { - qWarning() << "Forge: Errors found while parsing version file"; - return; - } - - QJsonObject installObj = installVal.toObject(); - QString libraryName = installObj.value("path").toString(); - internalPath = installObj.value("filePath").toString(); - m_forgeVersionString = installObj.value("version").toString().remove("Forge", Qt::CaseInsensitive).trimmed(); - - // where do we put the library? decode the mojang path - GradleSpecifier lib(libraryName); - - auto cacheentry = ENV.metacache()->resolveEntry("libraries", lib.toPath()); - finalPath = "libraries/" + lib.toPath(); - if (!FS::ensureFilePathExists(finalPath)) - return; - - if (!zip.setCurrentFile(internalPath)) - return; - if (!file.open(QIODevice::ReadOnly)) - return; - { - QByteArray data = file.readAll(); - // extract file - QSaveFile extraction(finalPath); - if (!extraction.open(QIODevice::WriteOnly)) - return; - if (extraction.write(data) != data.size()) - return; - if (!extraction.commit()) - return; - QCryptographicHash md5sum(QCryptographicHash::Md5); - md5sum.addData(data); - - cacheentry->setStale(false); - cacheentry->setMD5Sum(md5sum.result().toHex().constData()); - ENV.metacache()->updateEntry(cacheentry); - } - file.close(); - - m_forge_json = newVersion; -} - -bool ForgeInstaller::add(OneSixInstance *to) -{ - if (!BaseInstaller::add(to)) - { - return false; - } - - if (!m_forge_json) - { - return false; - } - - // A blacklist - QSet blacklist{"authlib", "realms"}; - QList xzlist{"org.scala-lang", "com.typesafe"}; - - // get the minecraft version from the instance - VersionFilePtr minecraft; - auto minecraftPatch = to->getMinecraftProfile()->versionPatch("net.minecraft"); - if(minecraftPatch) - { - minecraft = std::dynamic_pointer_cast(minecraftPatch); - if(!minecraft) - { - auto mcWrap = std::dynamic_pointer_cast(minecraftPatch); - if(mcWrap) - { - minecraft = mcWrap->getVersionFile(); - } - } - } - - // for each library in the version we are adding (except for the blacklisted) - QMutableListIterator iter(m_forge_json->libraries); - while (iter.hasNext()) - { - auto library = iter.next(); - QString libName = library->artifactId(); - QString libVersion = library->version(); - QString rawName = library->rawName(); - - // ignore lwjgl libraries. - if (g_VersionFilterData.lwjglWhitelist.contains(library->artifactPrefix())) - { - iter.remove(); - continue; - } - // ignore other blacklisted (realms, authlib) - if (blacklist.contains(libName)) - { - iter.remove(); - continue; - } - // if minecraft version was found, ignore everything that is already in the minecraft version - if(minecraft) - { - bool found = false; - for (auto & lib: minecraft->libraries) - { - if(library->artifactPrefix() == lib->artifactPrefix() && library->version() == lib->version()) - { - found = true; - break; - } - } - if (found) - continue; - } - - // if this is the actual forge lib, set an absolute url for the download - if (m_forge_version->type == ForgeVersion::Gradle) - { - if (libName == "forge") - { - library->setClassifier("universal"); - } - else if (libName == "minecraftforge") - { - QString forgeCoord("net.minecraftforge:forge:%1:universal"); - // using insane form of the MC version... - QString longVersion = m_forge_version->mcver + "-" + m_forge_version->jobbuildver; - GradleSpecifier spec(forgeCoord.arg(longVersion)); - library->setRawName(spec); - } - } - else - { - if (libName.contains("minecraftforge")) - { - library->setAbsoluteUrl(m_universal_url); - } - } - - // mark bad libraries based on the xzlist above - for (auto entry : xzlist) - { - qDebug() << "Testing " << rawName << " : " << entry; - if (rawName.startsWith(entry)) - { - library->setHint("forge-pack-xz"); - break; - } - } - } - QString &args = m_forge_json->minecraftArguments; - QStringList tweakers; - { - QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)"); - QRegularExpressionMatch match = expression.match(args); - while (match.hasMatch()) - { - tweakers.append(match.captured(1)); - args.remove(match.capturedStart(), match.capturedLength()); - match = expression.match(args); - } - if(tweakers.size()) - { - args.operator=(args.trimmed()); - m_forge_json->addTweakers = tweakers; - } - } - if(minecraft && args == minecraft->minecraftArguments) - { - args.clear(); - } - - m_forge_json->name = "Forge"; - m_forge_json->fileId = id(); - m_forge_json->version = m_forgeVersionString; - m_forge_json->dependsOnMinecraftVersion = to->intendedVersionId(); - m_forge_json->order = 5; - - // reset some things we do not want to be passed along. - m_forge_json->m_releaseTime = QDateTime(); - m_forge_json->m_updateTime = QDateTime(); - m_forge_json->minimumLauncherVersion = -1; - m_forge_json->type.clear(); - m_forge_json->minecraftArguments.clear(); - m_forge_json->minecraftVersion.clear(); - - QSaveFile file(filename(to->instanceRoot())); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(OneSixVersionFormat::versionFileToJson(m_forge_json, true).toJson()); - file.commit(); - - return true; -} - -bool ForgeInstaller::addLegacy(OneSixInstance *to) -{ - if (!BaseInstaller::add(to)) - { - return false; - } - auto entry = ENV.metacache()->resolveEntry("minecraftforge", m_forge_version->filename()); - finalPath = FS::PathCombine(to->jarModsDir(), m_forge_version->filename()); - if (!FS::ensureFilePathExists(finalPath)) - { - return false; - } - if (!QFile::copy(entry->getFullPath(), finalPath)) - { - return false; - } - QJsonObject obj; - obj.insert("order", 5); - { - QJsonArray jarmodsPlus; - { - QJsonObject libObj; - libObj.insert("name", m_forge_version->universal_filename); - jarmodsPlus.append(libObj); - } - obj.insert("+jarMods", jarmodsPlus); - } - - obj.insert("name", QString("Forge")); - obj.insert("fileId", id()); - obj.insert("version", m_forge_version->jobbuildver); - obj.insert("mcVersion", to->intendedVersionId()); - if (g_VersionFilterData.fmlLibsMapping.contains(m_forge_version->mcver)) - { - QJsonArray traitsPlus; - traitsPlus.append(QString("legacyFML")); - obj.insert("+traits", traitsPlus); - } - auto fullversion = to->getMinecraftProfile(); - fullversion->remove("net.minecraftforge"); - - QFile file(filename(to->instanceRoot())); - if (!file.open(QFile::WriteOnly)) - { - qCritical() << "Error opening" << file.fileName() - << "for reading:" << file.errorString(); - return false; - } - file.write(QJsonDocument(obj).toJson()); - file.close(); - return true; -} - -class ForgeInstallTask : public Task -{ - Q_OBJECT -public: - ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, - BaseVersionPtr version, QObject *parent = 0) - : Task(parent), m_installer(installer), m_instance(instance), m_version(version) - { - } - -protected: - void executeTask() override - { - setStatus(tr("Installing Forge...")); - ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast(m_version); - if (!forgeVersion) - { - emitFailed(tr("Unknown error occured")); - return; - } - prepare(forgeVersion); - } - void prepare(ForgeVersionPtr forgeVersion) - { - auto entry = ENV.metacache()->resolveEntry("minecraftforge", forgeVersion->filename()); - auto installFunction = [this, entry, forgeVersion]() - { - if (!install(entry, forgeVersion)) - { - qCritical() << "Failure installing Forge"; - emitFailed(tr("Failure to install Forge")); - } - else - { - reload(); - } - }; - - /* - * HACK IF the local non-stale file is too small, mark is as stale - * - * This fixes some problems with bad files acquired because of unhandled HTTP redirects - * in old versions of MultiMC. - */ - if (!entry->isStale()) - { - QFileInfo localFile(entry->getFullPath()); - if (localFile.size() <= 0x4000) - { - entry->setStale(true); - } - } - - if (entry->isStale()) - { - NetJob *fjob = new NetJob("Forge download"); - fjob->addNetAction(CacheDownload::make(forgeVersion->url(), entry)); - connect(fjob, &NetJob::progress, this, &Task::setProgress); - connect(fjob, &NetJob::status, this, &Task::setStatus); - connect(fjob, &NetJob::failed, [this](QString reason) - { emitFailed(tr("Failure to download Forge:\n%1").arg(reason)); }); - connect(fjob, &NetJob::succeeded, installFunction); - fjob->start(); - } - else - { - installFunction(); - } - } - bool install(const std::shared_ptr &entry, const ForgeVersionPtr &forgeVersion) - { - if (forgeVersion->usesInstaller()) - { - QString forgePath = entry->getFullPath(); - m_installer->prepare(forgePath, forgeVersion->universal_url); - return m_installer->add(m_instance); - } - else - return m_installer->addLegacy(m_instance); - } - void reload() - { - try - { - m_instance->reloadProfile(); - emitSucceeded(); - } - catch (Exception &e) - { - emitFailed(e.cause()); - } - catch (...) - { - emitFailed(tr("Failed to load the version description file for reasons unknown.")); - } - } - -private: - ForgeInstaller *m_installer; - OneSixInstance *m_instance; - BaseVersionPtr m_version; -}; - -Task *ForgeInstaller::createInstallTask(OneSixInstance *instance, - BaseVersionPtr version, QObject *parent) -{ - if (!version) - { - return nullptr; - } - m_forge_version = std::dynamic_pointer_cast(version); - return new ForgeInstallTask(this, instance, version, parent); -} - -#include "ForgeInstaller.moc" diff --git a/libraries/logic/minecraft/forge/ForgeInstaller.h b/libraries/logic/minecraft/forge/ForgeInstaller.h deleted file mode 100644 index 499a6fb3..00000000 --- a/libraries/logic/minecraft/forge/ForgeInstaller.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#pragma once - -#include "BaseInstaller.h" - -#include -#include - -#include "multimc_logic_export.h" - -class VersionFile; -class ForgeInstallTask; -struct ForgeVersion; - -class MULTIMC_LOGIC_EXPORT ForgeInstaller : public BaseInstaller -{ - friend class ForgeInstallTask; -public: - ForgeInstaller(); - virtual ~ForgeInstaller(){} - virtual Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override; - virtual QString id() const override { return "net.minecraftforge"; } - -protected: - void prepare(const QString &filename, const QString &universalUrl); - bool add(OneSixInstance *to) override; - bool addLegacy(OneSixInstance *to); - -private: - // the parsed version json, read from the installer - std::shared_ptr m_forge_json; - // the actual forge version - std::shared_ptr m_forge_version; - QString internalPath; - QString finalPath; - QString m_forgeVersionString; - QString m_universal_url; -}; diff --git a/libraries/logic/minecraft/forge/ForgeVersion.cpp b/libraries/logic/minecraft/forge/ForgeVersion.cpp deleted file mode 100644 index b859a28c..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersion.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "ForgeVersion.h" -#include "minecraft/VersionFilterData.h" -#include - -QString ForgeVersion::name() -{ - return "Forge " + jobbuildver; -} - -QString ForgeVersion::descriptor() -{ - return universal_filename; -} - -QString ForgeVersion::typeString() const -{ - if (is_recommended) - return QObject::tr("Recommended"); - return QString(); -} - -bool ForgeVersion::operator<(BaseVersion &a) -{ - ForgeVersion *pa = dynamic_cast(&a); - if (!pa) - return true; - return m_buildnr < pa->m_buildnr; -} - -bool ForgeVersion::operator>(BaseVersion &a) -{ - ForgeVersion *pa = dynamic_cast(&a); - if (!pa) - return false; - return m_buildnr > pa->m_buildnr; -} - -bool ForgeVersion::usesInstaller() -{ - if(installer_url.isEmpty()) - return false; - if(g_VersionFilterData.forgeInstallerBlacklist.contains(mcver)) - return false; - return true; -} - -QString ForgeVersion::filename() -{ - return usesInstaller() ? installer_filename : universal_filename; -} - -QString ForgeVersion::url() -{ - return usesInstaller() ? installer_url : universal_url; -} diff --git a/libraries/logic/minecraft/forge/ForgeVersion.h b/libraries/logic/minecraft/forge/ForgeVersion.h deleted file mode 100644 index e77d32f1..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersion.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include -#include -#include "BaseVersion.h" - -struct ForgeVersion; -typedef std::shared_ptr ForgeVersionPtr; - -struct ForgeVersion : public BaseVersion -{ - virtual QString descriptor() override; - virtual QString name() override; - virtual QString typeString() const override; - virtual bool operator<(BaseVersion &a) override; - virtual bool operator>(BaseVersion &a) override; - - QString filename(); - QString url(); - - enum - { - Invalid, - Legacy, - Gradle - } type = Invalid; - - bool usesInstaller(); - - int m_buildnr = 0; - QString branch; - QString universal_url; - QString changelog_url; - QString installer_url; - QString jobbuildver; - QString mcver; - QString mcver_sane; - QString universal_filename; - QString installer_filename; - bool is_recommended = false; -}; - -Q_DECLARE_METATYPE(ForgeVersionPtr) diff --git a/libraries/logic/minecraft/forge/ForgeVersionList.cpp b/libraries/logic/minecraft/forge/ForgeVersionList.cpp deleted file mode 100644 index de185e5f..00000000 --- a/libraries/logic/minecraft/forge/ForgeVersionList.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* Copyright 2013-2015 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. - */ - -#include "ForgeVersionList.h" -#include "ForgeVersion.h" - -#include "net/NetJob.h" -#include "net/URLConstants.h" -#include "Env.h" - -#include -#include -#include - -#include - -ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) -{ -} - -Task *ForgeVersionList::getLoadTask() -{ - return new ForgeListLoadTask(this); -} - -bool ForgeVersionList::isLoaded() -{ - return m_loaded; -} - -const BaseVersionPtr ForgeVersionList::at(int i) const -{ - return m_vlist.at(i); -} - -int ForgeVersionList::count() const -{ - return m_vlist.count(); -} - -int ForgeVersionList::columnCount(const QModelIndex &parent) const -{ - return 1; -} - -QVariant ForgeVersionList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); - switch (role) - { - case VersionPointerRole: - return qVariantFromValue(m_vlist[index.row()]); - - case VersionRole: - return version->name(); - - case VersionIdRole: - return version->descriptor(); - - case ParentGameVersionRole: - return version->mcver_sane; - - case RecommendedRole: - return version->is_recommended; - - case BranchRole: - return version->branch; - - default: - return QVariant(); - } -} - -BaseVersionList::RoleList ForgeVersionList::providesRoles() const -{ - return {VersionPointerRole, VersionRole, VersionIdRole, ParentGameVersionRole, RecommendedRole, BranchRole}; -} - -BaseVersionPtr ForgeVersionList::getLatestStable() const -{ - return BaseVersionPtr(); -} - -void ForgeVersionList::updateListData(QList versions) -{ - beginResetModel(); - m_vlist = versions; - m_loaded = true; - endResetModel(); - // NOW SORT!! - // sort(); -} - -void ForgeVersionList::sortVersions() -{ - // NO-OP for now -} - -ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() -{ - m_list = vlist; -} - -void ForgeListLoadTask::executeTask() -{ - setStatus(tr("Fetching Forge version lists...")); - auto job = new NetJob("Version index"); - // we do not care if the version is stale or not. - auto forgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "list.json"); - auto gradleForgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "json"); - - // verify by poking the server. - forgeListEntry->setStale(true); - gradleForgeListEntry->setStale(true); - - job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::FORGE_LEGACY_URL), - forgeListEntry)); - job->addNetAction(gradleListDownload = CacheDownload::make( - QUrl(URLConstants::FORGE_GRADLE_URL), gradleForgeListEntry)); - - connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); - connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed())); - - listJob.reset(job); - connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded())); - connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - listJob->start(); -} - -bool ForgeListLoadTask::abort() -{ - return listJob->abort(); -} - -bool ForgeListLoadTask::parseForgeList(QList &out) -{ - QByteArray data; - { - auto dlJob = listDownload; - auto filename = std::dynamic_pointer_cast(dlJob)->getTargetFilepath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - { - return false; - } - data = listFile.readAll(); - dlJob.reset(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed("Error parsing version list JSON:" + jsonError.errorString()); - return false; - } - - if (!jsonDoc.isObject()) - { - emitFailed("Error parsing version list JSON: JSON root is not an object"); - return false; - } - - QJsonObject root = jsonDoc.object(); - - // Now, get the array of versions. - if (!root.value("builds").isArray()) - { - emitFailed( - "Error parsing version list JSON: version list object is missing 'builds' array"); - return false; - } - QJsonArray builds = root.value("builds").toArray(); - - for (int i = 0; i < builds.count(); i++) - { - // Load the version info. - if (!builds[i].isObject()) - { - // FIXME: log this somewhere - continue; - } - QJsonObject obj = builds[i].toObject(); - int build_nr = obj.value("build").toDouble(0); - if (!build_nr) - continue; - QJsonArray files = obj.value("files").toArray(); - QString url, jobbuildver, mcver, buildtype, universal_filename; - QString changelog_url, installer_url; - QString installer_filename; - bool valid = false; - for (int j = 0; j < files.count(); j++) - { - if (!files[j].isObject()) - { - continue; - } - QJsonObject file = files[j].toObject(); - buildtype = file.value("buildtype").toString(); - if ((buildtype == "client" || buildtype == "universal") && !valid) - { - mcver = file.value("mcver").toString(); - url = file.value("url").toString(); - jobbuildver = file.value("jobbuildver").toString(); - int lastSlash = url.lastIndexOf('/'); - universal_filename = url.mid(lastSlash + 1); - valid = true; - } - else if (buildtype == "changelog") - { - QString ext = file.value("ext").toString(); - if (ext.isEmpty()) - { - continue; - } - changelog_url = file.value("url").toString(); - } - else if (buildtype == "installer") - { - installer_url = file.value("url").toString(); - int lastSlash = installer_url.lastIndexOf('/'); - installer_filename = installer_url.mid(lastSlash + 1); - } - } - if (valid) - { - // Now, we construct the version object and add it to the list. - std::shared_ptr fVersion(new ForgeVersion()); - fVersion->universal_url = url; - fVersion->changelog_url = changelog_url; - fVersion->installer_url = installer_url; - fVersion->jobbuildver = jobbuildver; - fVersion->mcver = fVersion->mcver_sane = mcver; - fVersion->installer_filename = installer_filename; - fVersion->universal_filename = universal_filename; - fVersion->m_buildnr = build_nr; - fVersion->type = ForgeVersion::Legacy; - out.append(fVersion); - } - } - - return true; -} - -bool ForgeListLoadTask::parseForgeGradleList(QList &out) -{ - QMap> lookup; - QByteArray data; - { - auto dlJob = gradleListDownload; - auto filename = std::dynamic_pointer_cast(dlJob)->getTargetFilepath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - { - return false; - } - data = listFile.readAll(); - dlJob.reset(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - emitFailed("Error parsing gradle version list JSON:" + jsonError.errorString()); - return false; - } - - if (!jsonDoc.isObject()) - { - emitFailed("Error parsing gradle version list JSON: JSON root is not an object"); - return false; - } - - QJsonObject root = jsonDoc.object(); - - // we probably could hard code these, but it might still be worth doing it this way - const QString webpath = root.value("webpath").toString(); - const QString artifact = root.value("artifact").toString(); - - QJsonObject numbers = root.value("number").toObject(); - for (auto it = numbers.begin(); it != numbers.end(); ++it) - { - QJsonObject number = it.value().toObject(); - std::shared_ptr fVersion(new ForgeVersion()); - fVersion->m_buildnr = number.value("build").toDouble(); - if(fVersion->m_buildnr >= 953 && fVersion->m_buildnr <= 965) - { - qDebug() << fVersion->m_buildnr; - } - fVersion->jobbuildver = number.value("version").toString(); - fVersion->branch = number.value("branch").toString(""); - fVersion->mcver = number.value("mcversion").toString(); - fVersion->universal_filename = ""; - fVersion->installer_filename = ""; - // HACK: here, we fix the minecraft version used by forge. - // HACK: this will inevitably break (later) - // FIXME: replace with a dictionary - fVersion->mcver_sane = fVersion->mcver; - fVersion->mcver_sane.replace("_pre", "-pre"); - - QString universal_filename, installer_filename; - QJsonArray files = number.value("files").toArray(); - for (auto fIt = files.begin(); fIt != files.end(); ++fIt) - { - // TODO with gradle we also get checksums, use them - QJsonArray file = (*fIt).toArray(); - if (file.size() < 3) - { - continue; - } - - QString extension = file.at(0).toString(); - QString part = file.at(1).toString(); - QString checksum = file.at(2).toString(); - - // insane form of mcver is used here - QString longVersion = fVersion->mcver + "-" + fVersion->jobbuildver; - if (!fVersion->branch.isEmpty()) - { - longVersion = longVersion + "-" + fVersion->branch; - } - QString filename = artifact + "-" + longVersion + "-" + part + "." + extension; - - QString url = QString("%1/%2/%3") - .arg(webpath) - .arg(longVersion) - .arg(filename); - - if (part == "installer") - { - fVersion->installer_url = url; - installer_filename = filename; - } - else if (part == "universal") - { - fVersion->universal_url = url; - universal_filename = filename; - } - else if (part == "changelog") - { - fVersion->changelog_url = url; - } - } - if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty()) - { - continue; - } - fVersion->universal_filename = universal_filename; - fVersion->installer_filename = installer_filename; - fVersion->type = ForgeVersion::Gradle; - out.append(fVersion); - lookup[fVersion->m_buildnr] = fVersion; - } - QJsonObject promos = root.value("promos").toObject(); - for (auto it = promos.begin(); it != promos.end(); ++it) - { - QString key = it.key(); - int build = it.value().toInt(); - QRegularExpression regexp("^(?[0-9]+(.[0-9]+)*)-(?