summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.arcconfig5
-rw-r--r--.gitignore30
-rw-r--r--BUILD.md125
-rw-r--r--CMakeLists.txt740
-rw-r--r--MultiMC.cpp635
-rw-r--r--MultiMC.h214
-rw-r--r--MultiMCVersion.h96
-rw-r--r--README.md29
-rw-r--r--changelog.yaml33
-rw-r--r--cmake/MacOSXBundleInfo.plist.in40
-rw-r--r--cmake/UseJava.cmake881
-rw-r--r--cmake/UseJavaClassFilelist.cmake52
-rw-r--r--cmake/UseJavaSymlinks.cmake32
-rw-r--r--config.h.in40
-rw-r--r--dependencies.cmake.in33
-rw-r--r--depends/classparser/CMakeLists.txt41
-rw-r--r--depends/classparser/include/classparser_config.h22
-rw-r--r--depends/classparser/include/javautils.h29
-rw-r--r--depends/classparser/src/annotations.cpp85
-rw-r--r--depends/classparser/src/annotations.h277
-rw-r--r--depends/classparser/src/classfile.h156
-rw-r--r--depends/classparser/src/constants.h220
-rw-r--r--depends/classparser/src/errors.h8
-rw-r--r--depends/classparser/src/javaendian.h76
-rw-r--r--depends/classparser/src/javautils.cpp83
-rw-r--r--depends/classparser/src/membuffer.h63
-rw-r--r--depends/groupview/.clang-format24
-rw-r--r--depends/groupview/.gitignore2
-rw-r--r--depends/groupview/CMakeLists.txt40
-rw-r--r--depends/groupview/Group.cpp (renamed from Group.cpp)0
-rw-r--r--depends/groupview/Group.h (renamed from Group.h)0
-rw-r--r--depends/groupview/GroupView.cpp (renamed from GroupView.cpp)0
-rw-r--r--depends/groupview/GroupView.h (renamed from GroupView.h)0
-rw-r--r--depends/groupview/GroupedProxyModel.cpp (renamed from GroupedProxyModel.cpp)0
-rw-r--r--depends/groupview/GroupedProxyModel.h (renamed from GroupedProxyModel.h)0
-rw-r--r--depends/groupview/InstanceDelegate.cpp (renamed from InstanceDelegate.cpp)0
-rw-r--r--depends/groupview/InstanceDelegate.h (renamed from InstanceDelegate.h)0
-rw-r--r--depends/groupview/main.cpp98
-rw-r--r--depends/groupview/main.h (renamed from main.h)0
-rw-r--r--depends/javacheck/.gitignore6
-rw-r--r--depends/javacheck/CMakeLists.txt15
-rw-r--r--depends/javacheck/JavaCheck.java24
-rw-r--r--depends/launcher/.gitignore6
-rw-r--r--depends/launcher/CMakeLists.txt35
-rw-r--r--depends/launcher/net/minecraft/Launcher.java154
-rw-r--r--depends/launcher/org/multimc/EntryPoint.java147
-rw-r--r--depends/launcher/org/multimc/IconLoader.java132
-rw-r--r--depends/launcher/org/multimc/Launcher.java22
-rw-r--r--depends/launcher/org/multimc/NotFoundException.java21
-rw-r--r--depends/launcher/org/multimc/ParamBucket.java86
-rw-r--r--depends/launcher/org/multimc/ParseException.java22
-rw-r--r--depends/launcher/org/multimc/Utils.java242
-rw-r--r--depends/launcher/org/multimc/legacy/LegacyFrame.java112
-rw-r--r--depends/launcher/org/multimc/legacy/LegacyLauncher.java178
-rw-r--r--depends/launcher/org/multimc/onesix/OneSixLauncher.java223
-rw-r--r--depends/launcher/org/simplericity/macify/eawt/Application.java176
-rw-r--r--depends/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java48
-rw-r--r--depends/launcher/org/simplericity/macify/eawt/ApplicationEvent.java25
-rw-r--r--depends/launcher/org/simplericity/macify/eawt/ApplicationListener.java27
-rw-r--r--depends/launcher/org/simplericity/macify/eawt/DefaultApplication.java418
-rw-r--r--depends/pack200/CMakeLists.txt59
-rw-r--r--depends/pack200/LICENSE347
-rw-r--r--depends/pack200/anti200.cpp43
-rw-r--r--depends/pack200/include/unpack200.h37
-rw-r--r--depends/pack200/src/bands.cpp423
-rw-r--r--depends/pack200/src/bands.h489
-rw-r--r--depends/pack200/src/bytes.cpp217
-rw-r--r--depends/pack200/src/bytes.h284
-rw-r--r--depends/pack200/src/coding.cpp1041
-rw-r--r--depends/pack200/src/coding.h247
-rw-r--r--depends/pack200/src/constants.h442
-rw-r--r--depends/pack200/src/defines.h65
-rw-r--r--depends/pack200/src/unpack.cpp4793
-rw-r--r--depends/pack200/src/unpack.h547
-rw-r--r--depends/pack200/src/unpack200.cpp162
-rw-r--r--depends/pack200/src/utils.cpp71
-rw-r--r--depends/pack200/src/utils.h53
-rw-r--r--depends/pack200/src/zip.cpp589
-rw-r--r--depends/pack200/src/zip.h110
-rw-r--r--depends/quazip/CMakeLists.txt42
-rw-r--r--depends/quazip/JlCompress.cpp522
-rw-r--r--depends/quazip/JlCompress.h125
-rw-r--r--depends/quazip/crypt.h135
-rw-r--r--depends/quazip/ioapi.h77
-rw-r--r--depends/quazip/qioapi.cpp146
-rw-r--r--depends/quazip/quaadler32.cpp28
-rw-r--r--depends/quazip/quaadler32.h29
-rw-r--r--depends/quazip/quachecksum32.h54
-rw-r--r--depends/quazip/quacrc32.cpp28
-rw-r--r--depends/quazip/quacrc32.h26
-rw-r--r--depends/quazip/quagzipfile.cpp141
-rw-r--r--depends/quazip/quagzipfile.h35
-rw-r--r--depends/quazip/quaziodevice.cpp283
-rw-r--r--depends/quazip/quaziodevice.h27
-rw-r--r--depends/quazip/quazip.cpp554
-rw-r--r--depends/quazip/quazip.h411
-rw-r--r--depends/quazip/quazip_global.h55
-rw-r--r--depends/quazip/quazipdir.cpp507
-rw-r--r--depends/quazip/quazipdir.h171
-rw-r--r--depends/quazip/quazipfile.cpp488
-rw-r--r--depends/quazip/quazipfile.h442
-rw-r--r--depends/quazip/quazipfileinfo.h66
-rw-r--r--depends/quazip/quazipnewinfo.cpp51
-rw-r--r--depends/quazip/quazipnewinfo.h102
-rw-r--r--depends/quazip/unzip.c1603
-rw-r--r--depends/quazip/unzip.h356
-rw-r--r--depends/quazip/zip.c1281
-rw-r--r--depends/quazip/zip.h245
-rw-r--r--depends/settings/CMakeLists.txt47
-rw-r--r--depends/settings/inifile.cpp106
-rw-r--r--depends/settings/inifile.h38
-rw-r--r--depends/settings/inisettingsobject.cpp76
-rw-r--r--depends/settings/inisettingsobject.h61
-rw-r--r--depends/settings/libsettings_config.h29
-rw-r--r--depends/settings/overridesetting.cpp30
-rw-r--r--depends/settings/overridesetting.h41
-rw-r--r--depends/settings/setting.cpp53
-rw-r--r--depends/settings/setting.h119
-rw-r--r--depends/settings/settingsobject.cpp138
-rw-r--r--depends/settings/settingsobject.h173
-rw-r--r--depends/util/CMakeLists.txt61
-rw-r--r--depends/util/include/cmdutils.h255
-rw-r--r--depends/util/include/libutil_config.h28
-rw-r--r--depends/util/include/osutils.h26
-rw-r--r--depends/util/include/pathutils.h59
-rw-r--r--depends/util/include/userutils.h17
-rw-r--r--depends/util/src/cmdutils.cpp486
-rw-r--r--depends/util/src/pathutils.cpp147
-rw-r--r--depends/util/src/userutils.cpp127
-rw-r--r--depends/xz-embedded/CMakeLists.txt32
-rw-r--r--depends/xz-embedded/include/xz.h321
-rw-r--r--depends/xz-embedded/src/xz_config.h119
-rw-r--r--depends/xz-embedded/src/xz_crc32.c61
-rw-r--r--depends/xz-embedded/src/xz_crc64.c52
-rw-r--r--depends/xz-embedded/src/xz_dec_bcj.c588
-rw-r--r--depends/xz-embedded/src/xz_dec_lzma2.c1231
-rw-r--r--depends/xz-embedded/src/xz_dec_stream.c860
-rw-r--r--depends/xz-embedded/src/xz_lzma2.h204
-rw-r--r--depends/xz-embedded/src/xz_private.h150
-rw-r--r--depends/xz-embedded/src/xz_stream.h62
-rw-r--r--depends/xz-embedded/xzminidec.c144
-rw-r--r--gui/ConsoleWindow.cpp280
-rw-r--r--gui/ConsoleWindow.h94
-rw-r--r--gui/ConsoleWindow.ui86
-rw-r--r--gui/MainWindow.cpp1501
-rw-r--r--gui/MainWindow.h211
-rw-r--r--gui/MainWindow.ui539
-rw-r--r--gui/Platform.h32
-rw-r--r--gui/Platform_Other.cpp27
-rw-r--r--gui/Platform_X11.cpp62
-rw-r--r--gui/dialogs/AboutDialog.cpp54
-rw-r--r--gui/dialogs/AboutDialog.h35
-rw-r--r--gui/dialogs/AboutDialog.ui543
-rw-r--r--gui/dialogs/AccountListDialog.cpp160
-rw-r--r--gui/dialogs/AccountListDialog.h65
-rw-r--r--gui/dialogs/AccountListDialog.ui96
-rw-r--r--gui/dialogs/AccountSelectDialog.cpp84
-rw-r--r--gui/dialogs/AccountSelectDialog.h90
-rw-r--r--gui/dialogs/AccountSelectDialog.ui56
-rw-r--r--gui/dialogs/CopyInstanceDialog.cpp84
-rw-r--r--gui/dialogs/CopyInstanceDialog.h50
-rw-r--r--gui/dialogs/CopyInstanceDialog.ui134
-rw-r--r--gui/dialogs/CustomMessageBox.cpp34
-rw-r--r--gui/dialogs/CustomMessageBox.h26
-rw-r--r--gui/dialogs/EditAccountDialog.cpp51
-rw-r--r--gui/dialogs/EditAccountDialog.h60
-rw-r--r--gui/dialogs/EditAccountDialog.ui94
-rw-r--r--gui/dialogs/EditNotesDialog.cpp42
-rw-r--r--gui/dialogs/EditNotesDialog.h38
-rw-r--r--gui/dialogs/EditNotesDialog.ui77
-rw-r--r--gui/dialogs/IconPickerDialog.cpp156
-rw-r--r--gui/dialogs/IconPickerDialog.h48
-rw-r--r--gui/dialogs/IconPickerDialog.ui67
-rw-r--r--gui/dialogs/InstanceSettings.cpp243
-rw-r--r--gui/dialogs/InstanceSettings.h60
-rw-r--r--gui/dialogs/InstanceSettings.ui419
-rw-r--r--gui/dialogs/LegacyModEditDialog.cpp393
-rw-r--r--gui/dialogs/LegacyModEditDialog.h78
-rw-r--r--gui/dialogs/LegacyModEditDialog.ui321
-rw-r--r--gui/dialogs/LwjglSelectDialog.cpp72
-rw-r--r--gui/dialogs/LwjglSelectDialog.h44
-rw-r--r--gui/dialogs/LwjglSelectDialog.ui85
-rw-r--r--gui/dialogs/ModEditDialogCommon.cpp57
-rw-r--r--gui/dialogs/ModEditDialogCommon.h22
-rw-r--r--gui/dialogs/NewInstanceDialog.cpp125
-rw-r--r--gui/dialogs/NewInstanceDialog.h55
-rw-r--r--gui/dialogs/NewInstanceDialog.ui179
-rw-r--r--gui/dialogs/OneSixModEditDialog.cpp367
-rw-r--r--gui/dialogs/OneSixModEditDialog.h69
-rw-r--r--gui/dialogs/OneSixModEditDialog.ui340
-rw-r--r--gui/dialogs/ProgressDialog.cpp125
-rw-r--r--gui/dialogs/ProgressDialog.h64
-rw-r--r--gui/dialogs/ProgressDialog.ui66
-rw-r--r--gui/dialogs/SettingsDialog.cpp505
-rw-r--r--gui/dialogs/SettingsDialog.h99
-rw-r--r--gui/dialogs/SettingsDialog.ui944
-rw-r--r--gui/dialogs/UpdateDialog.cpp28
-rw-r--r--gui/dialogs/UpdateDialog.h46
-rw-r--r--gui/dialogs/UpdateDialog.ui70
-rw-r--r--gui/dialogs/VersionSelectDialog.cpp116
-rw-r--r--gui/dialogs/VersionSelectDialog.h62
-rw-r--r--gui/dialogs/VersionSelectDialog.ui110
-rw-r--r--gui/widgets/Common.cpp27
-rw-r--r--gui/widgets/Common.h6
-rw-r--r--gui/widgets/InstanceDelegate.cpp231
-rw-r--r--gui/widgets/InstanceDelegate.h27
-rw-r--r--gui/widgets/LabeledToolButton.cpp86
-rw-r--r--gui/widgets/LabeledToolButton.h37
-rw-r--r--gui/widgets/MCModInfoFrame.cpp111
-rw-r--r--gui/widgets/MCModInfoFrame.h46
-rw-r--r--gui/widgets/MCModInfoFrame.ui68
-rw-r--r--gui/widgets/ModListView.cpp62
-rw-r--r--gui/widgets/ModListView.h27
-rw-r--r--gui/widgets/VersionListView.cpp150
-rw-r--r--gui/widgets/VersionListView.h43
-rw-r--r--install_prereqs.cmake.in19
-rw-r--r--logger/QsDebugOutput.cpp52
-rw-r--r--logger/QsDebugOutput.h34
-rw-r--r--logger/QsLog.cpp142
-rw-r--r--logger/QsLog.h132
-rw-r--r--logger/QsLogDest.cpp104
-rw-r--r--logger/QsLogDest.h53
-rw-r--r--logic/BaseInstance.cpp268
-rw-r--r--logic/BaseInstance.h200
-rw-r--r--logic/BaseInstance_p.h29
-rw-r--r--logic/BaseVersion.h55
-rw-r--r--logic/EnabledItemFilter.cpp43
-rw-r--r--logic/EnabledItemFilter.h32
-rw-r--r--logic/ForgeInstaller.cpp155
-rw-r--r--logic/ForgeInstaller.h36
-rw-r--r--logic/InstanceFactory.cpp196
-rw-r--r--logic/InstanceFactory.h105
-rw-r--r--logic/InstanceLauncher.cpp94
-rw-r--r--logic/InstanceLauncher.h44
-rw-r--r--logic/JavaChecker.cpp124
-rw-r--r--logic/JavaChecker.h42
-rw-r--r--logic/JavaCheckerJob.cpp47
-rw-r--r--logic/JavaCheckerJob.h100
-rw-r--r--logic/JavaUtils.cpp208
-rw-r--r--logic/JavaUtils.h43
-rw-r--r--logic/LegacyFTBInstance.cpp21
-rw-r--r--logic/LegacyFTBInstance.h14
-rw-r--r--logic/LegacyForge.cpp56
-rw-r--r--logic/LegacyForge.h25
-rw-r--r--logic/LegacyInstance.cpp282
-rw-r--r--logic/LegacyInstance.h94
-rw-r--r--logic/LegacyInstance_p.h30
-rw-r--r--logic/LegacyUpdate.cpp495
-rw-r--r--logic/LegacyUpdate.h75
-rw-r--r--logic/LiteLoaderInstaller.cpp102
-rw-r--r--logic/LiteLoaderInstaller.h39
-rw-r--r--logic/MinecraftProcess.cpp374
-rw-r--r--logic/MinecraftProcess.h142
-rw-r--r--logic/MinecraftVersion.h89
-rw-r--r--logic/Mod.cpp358
-rw-r--r--logic/Mod.h129
-rw-r--r--logic/ModList.cpp599
-rw-r--r--logic/ModList.h152
-rw-r--r--logic/NagUtils.cpp38
-rw-r--r--logic/NagUtils.h23
-rw-r--r--logic/NostalgiaInstance.cpp32
-rw-r--r--logic/NostalgiaInstance.h28
-rw-r--r--logic/OneSixFTBInstance.cpp125
-rw-r--r--logic/OneSixFTBInstance.h22
-rw-r--r--logic/OneSixInstance.cpp406
-rw-r--r--logic/OneSixInstance.h78
-rw-r--r--logic/OneSixInstance_p.h30
-rw-r--r--logic/OneSixLibrary.cpp268
-rw-r--r--logic/OneSixLibrary.h132
-rw-r--r--logic/OneSixRule.cpp89
-rw-r--r--logic/OneSixRule.h98
-rw-r--r--logic/OneSixUpdate.cpp326
-rw-r--r--logic/OneSixUpdate.h59
-rw-r--r--logic/OneSixVersion.cpp340
-rw-r--r--logic/OneSixVersion.h106
-rw-r--r--logic/OpSys.cpp42
-rw-r--r--logic/OpSys.h37
-rw-r--r--logic/SkinUtils.cpp47
-rw-r--r--logic/SkinUtils.h23
-rw-r--r--logic/assets/AssetsMigrateTask.cpp143
-rw-r--r--logic/assets/AssetsMigrateTask.h18
-rw-r--r--logic/assets/AssetsUtils.cpp154
-rw-r--r--logic/assets/AssetsUtils.h39
-rw-r--r--logic/auth/AuthSession.cpp30
-rw-r--r--logic/auth/AuthSession.h49
-rw-r--r--logic/auth/MojangAccount.cpp278
-rw-r--r--logic/auth/MojangAccount.h171
-rw-r--r--logic/auth/MojangAccountList.cpp426
-rw-r--r--logic/auth/MojangAccountList.h199
-rw-r--r--logic/auth/YggdrasilTask.cpp211
-rw-r--r--logic/auth/YggdrasilTask.h137
-rw-r--r--logic/auth/flows/AuthenticateTask.cpp203
-rw-r--r--logic/auth/flows/AuthenticateTask.h46
-rw-r--r--logic/auth/flows/RefreshTask.cpp152
-rw-r--r--logic/auth/flows/RefreshTask.h44
-rw-r--r--logic/auth/flows/ValidateTask.cpp64
-rw-r--r--logic/auth/flows/ValidateTask.h47
-rw-r--r--logic/icons/IconList.cpp368
-rw-r--r--logic/icons/IconList.h78
-rw-r--r--logic/icons/MMCIcon.cpp89
-rw-r--r--logic/icons/MMCIcon.h52
-rw-r--r--logic/lists/BaseVersionList.cpp121
-rw-r--r--logic/lists/BaseVersionList.h120
-rw-r--r--logic/lists/ForgeVersionList.cpp439
-rw-r--r--logic/lists/ForgeVersionList.h128
-rw-r--r--logic/lists/InstanceList.cpp608
-rw-r--r--logic/lists/InstanceList.h139
-rw-r--r--logic/lists/JavaVersionList.cpp242
-rw-r--r--logic/lists/JavaVersionList.h96
-rw-r--r--logic/lists/LwjglVersionList.cpp199
-rw-r--r--logic/lists/LwjglVersionList.h148
-rw-r--r--logic/lists/MinecraftVersionList.cpp286
-rw-r--r--logic/lists/MinecraftVersionList.h74
-rw-r--r--logic/net/ByteArrayDownload.cpp82
-rw-r--r--logic/net/ByteArrayDownload.h44
-rw-r--r--logic/net/CacheDownload.cpp169
-rw-r--r--logic/net/CacheDownload.h58
-rw-r--r--logic/net/ForgeMirror.h10
-rw-r--r--logic/net/ForgeMirrors.cpp118
-rw-r--r--logic/net/ForgeMirrors.h58
-rw-r--r--logic/net/ForgeXzDownload.cpp389
-rw-r--r--logic/net/ForgeXzDownload.h65
-rw-r--r--logic/net/HttpMetaCache.cpp253
-rw-r--r--logic/net/HttpMetaCache.h75
-rw-r--r--logic/net/MD5EtagDownload.cpp156
-rw-r--r--logic/net/MD5EtagDownload.h51
-rw-r--r--logic/net/NetAction.h89
-rw-r--r--logic/net/NetJob.cpp112
-rw-r--r--logic/net/NetJob.h124
-rw-r--r--logic/net/PasteUpload.cpp86
-rw-r--r--logic/net/PasteUpload.h26
-rw-r--r--logic/net/URLConstants.h36
-rw-r--r--logic/news/NewsChecker.cpp135
-rw-r--r--logic/news/NewsChecker.h105
-rw-r--r--logic/news/NewsEntry.cpp77
-rw-r--r--logic/news/NewsEntry.h65
-rw-r--r--logic/status/StatusChecker.cpp137
-rw-r--r--logic/status/StatusChecker.h57
-rw-r--r--logic/tasks/ProgressProvider.h42
-rw-r--r--logic/tasks/SequentialTask.cpp77
-rw-r--r--logic/tasks/SequentialTask.h32
-rw-r--r--logic/tasks/Task.cpp84
-rw-r--r--logic/tasks/Task.h66
-rw-r--r--logic/tasks/ThreadTask.cpp41
-rw-r--r--logic/tasks/ThreadTask.h25
-rw-r--r--logic/updater/DownloadUpdateTask.cpp543
-rw-r--r--logic/updater/DownloadUpdateTask.h217
-rw-r--r--logic/updater/NotificationChecker.cpp121
-rw-r--r--logic/updater/NotificationChecker.h54
-rw-r--r--logic/updater/UpdateChecker.cpp263
-rw-r--r--logic/updater/UpdateChecker.h111
-rw-r--r--main.cpp111
-rw-r--r--mmc_updater/CMakeLists.txt52
-rw-r--r--mmc_updater/LICENSE19
-rw-r--r--mmc_updater/README.md138
-rw-r--r--mmc_updater/cmake/modules/GenerateCppResourceFile.cmake23
-rw-r--r--mmc_updater/depends/AnyOption/CMakeLists.txt9
-rw-r--r--mmc_updater/depends/AnyOption/README16
-rw-r--r--mmc_updater/depends/AnyOption/anyoption.cpp1176
-rw-r--r--mmc_updater/depends/AnyOption/anyoption.h270
-rw-r--r--mmc_updater/depends/tinyxml/CMakeLists.txt24
-rw-r--r--mmc_updater/depends/tinyxml/readme.txt530
-rw-r--r--mmc_updater/depends/tinyxml/tinystr.cpp111
-rw-r--r--mmc_updater/depends/tinyxml/tinystr.h305
-rw-r--r--mmc_updater/depends/tinyxml/tinyxml.cpp1886
-rw-r--r--mmc_updater/depends/tinyxml/tinyxml.h1805
-rw-r--r--mmc_updater/depends/tinyxml/tinyxmlerror.cpp52
-rw-r--r--mmc_updater/depends/tinyxml/tinyxmlparser.cpp1638
-rw-r--r--mmc_updater/depends/win32cpp/controls.h1074
-rw-r--r--mmc_updater/depends/win32cpp/copyright.txt33
-rw-r--r--mmc_updater/depends/win32cpp/cstring.h905
-rw-r--r--mmc_updater/depends/win32cpp/default_resource.h94
-rw-r--r--mmc_updater/depends/win32cpp/default_resource.rc250
-rw-r--r--mmc_updater/depends/win32cpp/dialog.h876
-rw-r--r--mmc_updater/depends/win32cpp/docking.h4214
-rw-r--r--mmc_updater/depends/win32cpp/file.h392
-rw-r--r--mmc_updater/depends/win32cpp/frame.h3303
-rw-r--r--mmc_updater/depends/win32cpp/gdi.h3944
-rw-r--r--mmc_updater/depends/win32cpp/info.txt205
-rw-r--r--mmc_updater/depends/win32cpp/listview.h867
-rw-r--r--mmc_updater/depends/win32cpp/mdi.h783
-rw-r--r--mmc_updater/depends/win32cpp/menu.h600
-rw-r--r--mmc_updater/depends/win32cpp/propertysheet.h960
-rw-r--r--mmc_updater/depends/win32cpp/rebar.h709
-rw-r--r--mmc_updater/depends/win32cpp/release notes.txt116
-rw-r--r--mmc_updater/depends/win32cpp/ribbon.h527
-rw-r--r--mmc_updater/depends/win32cpp/shared_ptr.h199
-rw-r--r--mmc_updater/depends/win32cpp/socket.h778
-rw-r--r--mmc_updater/depends/win32cpp/statusbar.h226
-rw-r--r--mmc_updater/depends/win32cpp/stdcontrols.h1000
-rw-r--r--mmc_updater/depends/win32cpp/tab.h1658
-rw-r--r--mmc_updater/depends/win32cpp/taskdialog.h811
-rw-r--r--mmc_updater/depends/win32cpp/thread.h241
-rw-r--r--mmc_updater/depends/win32cpp/toolbar.h1361
-rw-r--r--mmc_updater/depends/win32cpp/treeview.h624
-rw-r--r--mmc_updater/depends/win32cpp/wceframe.h420
-rw-r--r--mmc_updater/depends/win32cpp/wcestddef.h58
-rw-r--r--mmc_updater/depends/win32cpp/webbrowser.h760
-rw-r--r--mmc_updater/depends/win32cpp/wincore.h2977
-rw-r--r--mmc_updater/depends/win32cpp/winutils.h649
-rw-r--r--mmc_updater/src/AppInfo.cpp23
-rw-r--r--mmc_updater/src/AppInfo.h39
-rw-r--r--mmc_updater/src/CMakeLists.txt121
-rw-r--r--mmc_updater/src/DirIterator.cpp85
-rw-r--r--mmc_updater/src/DirIterator.h43
-rw-r--r--mmc_updater/src/FileUtils.cpp517
-rw-r--r--mmc_updater/src/FileUtils.h141
-rw-r--r--mmc_updater/src/Log.cpp65
-rw-r--r--mmc_updater/src/Log.h46
-rw-r--r--mmc_updater/src/MacBundle.cpp53
-rw-r--r--mmc_updater/src/MacBundle.h35
-rw-r--r--mmc_updater/src/Platform.h34
-rw-r--r--mmc_updater/src/ProcessUtils.cpp536
-rw-r--r--mmc_updater/src/ProcessUtils.h101
-rw-r--r--mmc_updater/src/StandardDirs.cpp63
-rw-r--r--mmc_updater/src/StandardDirs.h22
-rw-r--r--mmc_updater/src/StandardDirs.mm18
-rw-r--r--mmc_updater/src/StlSymbolsLeopard.cpp81
-rw-r--r--mmc_updater/src/StringUtils.h46
-rw-r--r--mmc_updater/src/UpdateDialog.cpp25
-rw-r--r--mmc_updater/src/UpdateDialog.h29
-rw-r--r--mmc_updater/src/UpdateDialogAscii.cpp70
-rw-r--r--mmc_updater/src/UpdateDialogAscii.h32
-rw-r--r--mmc_updater/src/UpdateDialogCocoa.h32
-rw-r--r--mmc_updater/src/UpdateDialogCocoa.mm194
-rw-r--r--mmc_updater/src/UpdateDialogGtk.cpp155
-rw-r--r--mmc_updater/src/UpdateDialogGtk.h42
-rw-r--r--mmc_updater/src/UpdateDialogGtkFactory.cpp59
-rw-r--r--mmc_updater/src/UpdateDialogGtkFactory.h13
-rw-r--r--mmc_updater/src/UpdateDialogWin32.cpp215
-rw-r--r--mmc_updater/src/UpdateDialogWin32.h39
-rw-r--r--mmc_updater/src/UpdateInstaller.cpp479
-rw-r--r--mmc_updater/src/UpdateInstaller.h74
-rw-r--r--mmc_updater/src/UpdateMessage.h42
-rw-r--r--mmc_updater/src/UpdateObserver.h15
-rw-r--r--mmc_updater/src/UpdateScript.cpp99
-rw-r--r--mmc_updater/src/UpdateScript.h82
-rw-r--r--mmc_updater/src/UpdaterOptions.cpp87
-rw-r--r--mmc_updater/src/UpdaterOptions.h26
-rw-r--r--mmc_updater/src/main.cpp206
-rw-r--r--mmc_updater/src/resources/Info.plist38
-rw-r--r--mmc_updater/src/resources/icon128.pngbin0 -> 3802 bytes
-rw-r--r--mmc_updater/src/resources/icon64.pngbin0 -> 2182 bytes
-rw-r--r--mmc_updater/src/resources/mac.icnsbin0 -> 43606 bytes
-rw-r--r--mmc_updater/src/resources/updater.icobin0 -> 82726 bytes
-rw-r--r--mmc_updater/src/resources/updater.manifest27
-rw-r--r--mmc_updater/src/resources/updater.rc30
-rw-r--r--mmc_updater/src/tests/CMakeLists.txt47
-rw-r--r--mmc_updater/src/tests/TestFileUtils.cpp79
-rw-r--r--mmc_updater/src/tests/TestFileUtils.h10
-rw-r--r--mmc_updater/src/tests/TestParseScript.cpp24
-rw-r--r--mmc_updater/src/tests/TestParseScript.h8
-rw-r--r--mmc_updater/src/tests/TestUtils.h108
-rw-r--r--mmc_updater/src/tests/file_list.xml37
-rw-r--r--mmc_updater/src/tests/new_app.cpp8
-rw-r--r--mmc_updater/src/tests/old_app.cpp7
-rw-r--r--mmc_updater/src/tests/test.manifest27
-rw-r--r--mmc_updater/src/tests/test.rc28
-rwxr-xr-xpackage/linux/MultiMC77
-rw-r--r--resources/MultiMC.icnsbin0 -> 177400 bytes
-rw-r--r--resources/MultiMC.icobin0 -> 76126 bytes
-rw-r--r--resources/MultiMC.manifest27
-rw-r--r--resources/backgrounds/backgrounds.qrc6
-rw-r--r--resources/backgrounds/catbgrnd2.pngbin0 -> 78285 bytes
-rw-r--r--resources/instances/brick.pngbin0 -> 713 bytes
-rw-r--r--resources/instances/chicken.pngbin0 -> 1181 bytes
-rw-r--r--resources/instances/chicken128.pngbin0 -> 6369 bytes
-rw-r--r--resources/instances/creeper.pngbin0 -> 1524 bytes
-rw-r--r--resources/instances/creeper128.pngbin0 -> 9046 bytes
-rw-r--r--resources/instances/derp.pngbin0 -> 5225 bytes
-rw-r--r--resources/instances/diamond.pngbin0 -> 708 bytes
-rw-r--r--resources/instances/dirt.pngbin0 -> 482 bytes
-rw-r--r--resources/instances/enderman.pngbin0 -> 2429 bytes
-rw-r--r--resources/instances/enderpearl.pngbin0 -> 2120 bytes
-rw-r--r--resources/instances/enderpearl128.pngbin0 -> 21425 bytes
-rw-r--r--resources/instances/ftb_glow.pngbin0 -> 1747 bytes
-rw-r--r--resources/instances/ftb_glow128.pngbin0 -> 12708 bytes
-rw-r--r--resources/instances/ftb_logo.pngbin0 -> 1607 bytes
-rw-r--r--resources/instances/ftb_logo128.pngbin0 -> 7883 bytes
-rw-r--r--resources/instances/gear.pngbin0 -> 2414 bytes
-rw-r--r--resources/instances/gear128.pngbin0 -> 18321 bytes
-rw-r--r--resources/instances/gold.pngbin0 -> 978 bytes
-rw-r--r--resources/instances/grass.pngbin0 -> 618 bytes
-rw-r--r--resources/instances/herobrine.pngbin0 -> 1034 bytes
-rw-r--r--resources/instances/herobrine128.pngbin0 -> 4937 bytes
-rw-r--r--resources/instances/infinity.pngbin0 -> 1714 bytes
-rw-r--r--resources/instances/infinity128.pngbin0 -> 9237 bytes
-rw-r--r--resources/instances/instances.qrc35
-rw-r--r--resources/instances/iron.pngbin0 -> 532 bytes
-rw-r--r--resources/instances/magitech.pngbin0 -> 2646 bytes
-rw-r--r--resources/instances/magitech128.pngbin0 -> 23097 bytes
-rw-r--r--resources/instances/meat.pngbin0 -> 1514 bytes
-rw-r--r--resources/instances/meat128.pngbin0 -> 10583 bytes
-rw-r--r--resources/instances/netherstar.pngbin0 -> 1942 bytes
-rw-r--r--resources/instances/netherstar128.pngbin0 -> 14062 bytes
-rw-r--r--resources/instances/planks.pngbin0 -> 461 bytes
-rw-r--r--resources/instances/skeleton.pngbin0 -> 696 bytes
-rw-r--r--resources/instances/skeleton128.pngbin0 -> 3673 bytes
-rw-r--r--resources/instances/squarecreeper.pngbin0 -> 1623 bytes
-rw-r--r--resources/instances/squarecreeper128.pngbin0 -> 9136 bytes
-rw-r--r--resources/instances/steve.pngbin0 -> 969 bytes
-rw-r--r--resources/instances/steve128.pngbin0 -> 4312 bytes
-rw-r--r--resources/instances/stone.pngbin0 -> 438 bytes
-rw-r--r--resources/instances/tnt.pngbin0 -> 378 bytes
-rw-r--r--resources/multimc.rc29
-rw-r--r--resources/multimc/16x16/about.pngbin0 -> 1270 bytes
-rw-r--r--resources/multimc/16x16/bug.pngbin0 -> 734 bytes
-rw-r--r--resources/multimc/16x16/cat.pngbin0 -> 736 bytes
-rw-r--r--resources/multimc/16x16/centralmods.pngbin0 -> 1145 bytes
-rw-r--r--resources/multimc/16x16/checkupdate.pngbin0 -> 1212 bytes
-rw-r--r--resources/multimc/16x16/copy.pngbin0 -> 957 bytes
-rw-r--r--resources/multimc/16x16/help.pngbin0 -> 1297 bytes
-rw-r--r--resources/multimc/16x16/new.pngbin0 -> 1175 bytes
-rw-r--r--resources/multimc/16x16/news.pngbin0 -> 727 bytes
-rw-r--r--resources/multimc/16x16/noaccount.pngbin0 -> 334 bytes
-rw-r--r--resources/multimc/16x16/refresh.pngbin0 -> 931 bytes
-rw-r--r--resources/multimc/16x16/settings.pngbin0 -> 1410 bytes
-rw-r--r--resources/multimc/16x16/viewfolder.pngbin0 -> 852 bytes
-rw-r--r--resources/multimc/22x22/about.pngbin0 -> 1693 bytes
-rw-r--r--resources/multimc/22x22/bug.pngbin0 -> 1180 bytes
-rw-r--r--resources/multimc/22x22/cat.pngbin0 -> 1034 bytes
-rw-r--r--resources/multimc/22x22/centralmods.pngbin0 -> 1561 bytes
-rw-r--r--resources/multimc/22x22/checkupdate.pngbin0 -> 1635 bytes
-rw-r--r--resources/multimc/22x22/copy.pngbin0 -> 1004 bytes
-rw-r--r--resources/multimc/22x22/help.pngbin0 -> 1735 bytes
-rw-r--r--resources/multimc/22x22/new.pngbin0 -> 1440 bytes
-rw-r--r--resources/multimc/22x22/news.pngbin0 -> 1173 bytes
-rw-r--r--resources/multimc/22x22/refresh.pngbin0 -> 1283 bytes
-rw-r--r--resources/multimc/22x22/settings.pngbin0 -> 1964 bytes
-rw-r--r--resources/multimc/22x22/viewfolder.pngbin0 -> 1006 bytes
-rw-r--r--resources/multimc/24x24/cat.pngbin0 -> 1252 bytes
-rw-r--r--resources/multimc/24x24/noaccount.pngbin0 -> 344 bytes
-rw-r--r--resources/multimc/32x32/about.pngbin0 -> 2658 bytes
-rw-r--r--resources/multimc/32x32/bug.pngbin0 -> 1772 bytes
-rw-r--r--resources/multimc/32x32/cat.pngbin0 -> 1678 bytes
-rw-r--r--resources/multimc/32x32/centralmods.pngbin0 -> 2119 bytes
-rw-r--r--resources/multimc/32x32/checkupdate.pngbin0 -> 2480 bytes
-rw-r--r--resources/multimc/32x32/copy.pngbin0 -> 1401 bytes
-rw-r--r--resources/multimc/32x32/help.pngbin0 -> 2720 bytes
-rw-r--r--resources/multimc/32x32/new.pngbin0 -> 1769 bytes
-rw-r--r--resources/multimc/32x32/news.pngbin0 -> 1752 bytes
-rw-r--r--resources/multimc/32x32/noaccount.pngbin0 -> 363 bytes
-rw-r--r--resources/multimc/32x32/refresh.pngbin0 -> 2182 bytes
-rw-r--r--resources/multimc/32x32/settings.pngbin0 -> 2983 bytes
-rw-r--r--resources/multimc/32x32/viewfolder.pngbin0 -> 1518 bytes
-rw-r--r--resources/multimc/48x48/about.pngbin0 -> 3995 bytes
-rw-r--r--resources/multimc/48x48/bug.pngbin0 -> 3124 bytes
-rw-r--r--resources/multimc/48x48/cat.pngbin0 -> 2733 bytes
-rw-r--r--resources/multimc/48x48/centralmods.pngbin0 -> 3201 bytes
-rw-r--r--resources/multimc/48x48/checkupdate.pngbin0 -> 4196 bytes
-rw-r--r--resources/multimc/48x48/copy.pngbin0 -> 1952 bytes
-rw-r--r--resources/multimc/48x48/help.pngbin0 -> 4170 bytes
-rw-r--r--resources/multimc/48x48/new.pngbin0 -> 2870 bytes
-rw-r--r--resources/multimc/48x48/news.pngbin0 -> 3333 bytes
-rw-r--r--resources/multimc/48x48/noaccount.pngbin0 -> 387 bytes
-rw-r--r--resources/multimc/48x48/refresh.pngbin0 -> 3743 bytes
-rw-r--r--resources/multimc/48x48/settings.pngbin0 -> 4797 bytes
-rw-r--r--resources/multimc/48x48/viewfolder.pngbin0 -> 1945 bytes
-rw-r--r--resources/multimc/64x64/about.pngbin0 -> 5513 bytes
-rw-r--r--resources/multimc/64x64/bug.pngbin0 -> 4263 bytes
-rw-r--r--resources/multimc/64x64/cat.pngbin0 -> 4033 bytes
-rw-r--r--resources/multimc/64x64/centralmods.pngbin0 -> 4408 bytes
-rw-r--r--resources/multimc/64x64/checkupdate.pngbin0 -> 5858 bytes
-rw-r--r--resources/multimc/64x64/copy.pngbin0 -> 2884 bytes
-rw-r--r--resources/multimc/64x64/help.pngbin0 -> 5402 bytes
-rw-r--r--resources/multimc/64x64/new.pngbin0 -> 3949 bytes
-rw-r--r--resources/multimc/64x64/news.pngbin0 -> 4968 bytes
-rw-r--r--resources/multimc/64x64/refresh.pngbin0 -> 5745 bytes
-rw-r--r--resources/multimc/64x64/settings.pngbin0 -> 7125 bytes
-rw-r--r--resources/multimc/64x64/viewfolder.pngbin0 -> 2134 bytes
-rw-r--r--resources/multimc/8x8/noaccount.pngbin0 -> 284 bytes
-rw-r--r--resources/multimc/index.theme33
-rw-r--r--resources/multimc/multimc.qrc111
-rw-r--r--resources/multimc/scalable/apps/multimc.svg1993
-rw-r--r--resources/multimc/scalable/bug.svg387
-rw-r--r--resources/multimc/scalable/centralmods.svg346
-rw-r--r--resources/multimc/scalable/checkupdate.svg167
-rw-r--r--resources/multimc/scalable/console.svg228
-rw-r--r--resources/multimc/scalable/console_error.svg247
-rw-r--r--resources/multimc/scalable/new.svg127
-rw-r--r--resources/multimc/scalable/news.svg296
-rw-r--r--resources/multimc/scalable/viewfolder.svg122
-rw-r--r--resources/sources/clucker.svg404
-rw-r--r--resources/sources/creeper.svg775
-rw-r--r--resources/sources/enderpearl.svg271
-rw-r--r--resources/sources/ftb-glow.svg606
-rw-r--r--resources/sources/ftb-logo.svg257
-rw-r--r--resources/sources/gear.svg434
-rw-r--r--resources/sources/herobrine.svg583
-rw-r--r--resources/sources/magitech.svg886
-rw-r--r--resources/sources/meat.svg371
-rw-r--r--resources/sources/netherstar.svg342
-rw-r--r--resources/sources/pskeleton.svg581
-rw-r--r--resources/sources/skeleton.svg610
-rw-r--r--resources/sources/squarecreeper.svg828
-rw-r--r--resources/sources/status-bad.svg262
-rw-r--r--resources/sources/status-good.svg293
-rw-r--r--resources/sources/status-terrible.svg262
-rw-r--r--resources/sources/steve.svg534
-rw-r--r--tests/CMakeLists.txt106
-rw-r--r--tests/TestUtil.h48
-rw-r--r--tests/data/.gitattributes2
-rw-r--r--tests/data/1.json43
-rw-r--r--tests/data/2.json31
-rw-r--r--tests/data/channels.json23
-rw-r--r--tests/data/errorChannels.json23
-rw-r--r--tests/data/fileOneA1
-rw-r--r--tests/data/fileOneB3
-rw-r--r--tests/data/fileThree1
-rw-r--r--tests/data/fileTwo1
-rw-r--r--tests/data/garbageChannels.json22
-rw-r--r--tests/data/index.json9
-rw-r--r--tests/data/noChannels.json5
-rw-r--r--tests/data/oneChannel.json11
-rw-r--r--tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml17
-rwxr-xr-xtests/data/tst_userutils-test_createShortcut-unix6
-rw-r--r--tests/test.manifest27
-rw-r--r--tests/test.rc28
-rw-r--r--tests/test_config.h.in3
-rw-r--r--tests/tst_DownloadUpdateTask.cpp273
-rw-r--r--tests/tst_UpdateChecker.cpp183
-rw-r--r--tests/tst_pathutils.cpp74
-rw-r--r--tests/tst_userutils.cpp71
-rw-r--r--translations/mmc_de.ts2556
624 files changed, 119344 insertions, 106 deletions
diff --git a/.arcconfig b/.arcconfig
new file mode 100644
index 00000000..0e628a13
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,5 @@
+{
+ "project_id": "MultiMC5",
+ "conduit_uri": "http://ph.multimc.org"
+}
+
diff --git a/.gitignore b/.gitignore
index a5d18fa3..2ef0d673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,28 @@
-build/
-*.user*
+Thumbs.db
+.kdev4
+.user
+.directory
+resources/CMakeFiles
+resources/MultiMCLauncher.jar
+*~
+*.swp
+html/
+
+# Project Files
+MultiMC5.kdev4
+MultiMC.pro.user
+CMakeLists.txt.user
+CMakeLists.txt.user.*
+
+# Build dirs
+build
+/build-*
+
+# Ctags File
+tags
+
+# YouCompleteMe config stuff.
+.ycm_extra_conf.*
+
+#OSX Stuff
+.DS_Store
diff --git a/BUILD.md b/BUILD.md
new file mode 100644
index 00000000..32f4a385
--- /dev/null
+++ b/BUILD.md
@@ -0,0 +1,125 @@
+Build Instructions
+==================
+
+# Contents
+* [Linux](#linux)
+* [Windows](#windows)
+* [OS X](#os-x)
+
+# Linux
+
+Getting the project to build and run on Linux is easy if you use Ubuntu 13.10 (or 13.04) and Qt's IDE, Qt Creator.
+
+## Dependencies
+* Qt 5.1.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)")
+* A copy of the MultiMC source (clone it with git)
+* cmake
+* build-essential
+* zlib (for example, zlib1g-dev)
+* java (for example, openjdk-7-jdk)
+* GL headers (for example, libgl1-mesa-dev)
+
+## Getting set up
+
+### Installing dependencies
+Just run `sudo apt-get install <dependency>` for each dependency (other than Qt and the MultiMC source) from above.
+
+### Installing Qt
+1. Run the Qt installer
+2. Choose a place to install Qt,
+3. Choose the components you want to install
+ - You need Qt 5.1.1/gcc 64-bit ticked,
+ - You need Tools/Qt Creator ticked,
+ - Other components are selected by default, you can untick them if you don't need them.
+4. Accept the license agreements,
+5. Double check the install details and then click "Install"
+ - Installation can take a very long time, go grab a cup of tea or something and let it work.
+
+### Loading the project
+1. Open Qt Creator,
+2. Choose File->Open File or Project,
+3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt,
+4. Read the instructions that just popped up about a build location and choose one,
+5. You should see "Run CMake" in the window,
+ - Make sure that Generator is set to "Unix Generator (Desktop Qt 5.1.1 GCC 64bit)",
+ - Hit the "Run CMake" button,
+ - You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
+ - Hit "Finish" if CMake ran successfully.
+6. Cross your fingers and press the Run button (bottom left of Qt Creator)!
+ - If the project builds successfully it will run and the MultiMC5 window will pop up.
+
+*These build instructions worked for me (Drayshak) on a fresh Ubuntu 13.10 x64 install. If they don't work for you, let us know on IRC (Esper/#MultiMC)!*
+
+# Windows
+
+Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using VC's build tools as it uses some C++11 features that aren't implemented in it at the time of writing.
+
+## Dependencies
+* Qt 5.1.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Windows")
+* OpenSSL (http://slproweb.com/products/Win32OpenSSL.html) ("Win32 OpenSSL v1.0.1e Light")
+ - Microsoft Visual C++ 2008 Redist. is required for this, there's a link on the OpenSSL download page above next to the main download.
+* CMake (http://www.cmake.org/cmake/resources/software.html) ("Windows (Win32 Installer)")
+* A copy of the MultiMC source (clone it with git)
+
+## Getting set up
+
+### Installing Qt
+1. Run the Qt installer
+2. Choose a place to install Qt (C:\Qt is the default),
+3. Choose the components you want to install
+ - You need Qt 5.1.1/MinGW 4.8 (32 bit) ticked,
+ - You need Tools/Qt Creator ticked,
+ - Other components are selected by default, you can untick them if you don't need them.
+4. Accept the license agreements,
+5. Double check the install details and then click "Install"
+ - Installation can take a very long time, go grab a cup of tea or something and let it work.
+
+### Installing OpenSSL
+1. Run the OpenSSL installer,
+2. It's best to choose the option to copy OpenSSL DLLs to the /bin directory
+ - If you do this you'll need to add that directory (the default being C:\OpenSSL-Win32\bin) to your PATH system variable (Google how to do this, or use this guide for Java: http://www.java.com/en/download/help/path.xml).
+
+### Installing CMake
+1. Run the CMake installer,
+2. It's easiest if you choose to add CMake to the PATH for all users,
+ - If you don't choose to do this, remember where you installed CMake.
+
+### Loading the project
+1. Open Qt Creator,
+2. Choose File->Open File or Project,
+3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt,
+4. Read the instructions that just popped up about a build location and choose one,
+5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
+ - Otherwise you can skip this step.
+6. You should see "Run CMake" in the window,
+ - Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.1.1 MinGW 32bit)",
+ - Hit the "Run CMake" button,
+ - You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
+ - Hit "Finish" if CMake ran successfully.
+7. Cross your fingers and press the Run button (bottom left of Qt Creator)!
+ - If the project builds successfully it will run and the MultiMC5 window will pop up,
+ - Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
+
+*These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC (Esper/#MultiMC)!*
+
+# OS X
+
+### Install prerequisites:
+1. install homebrew
+2. brew install qt5
+3. brew tap homebrew/versions
+4. brew install gcc48
+5. brew install cmake
+
+### Build
+1. git clone https://github.com/MultiMC/MultiMC5.git
+2. cd MultiMC5
+3. mkdir build
+4. cd build
+5. export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
+6. export CC=/usr/local/bin/gcc-4.8
+7. export CXX=/usr/local/bin/g++-4.8
+8. cmake ..
+9. make
+
+*These build instructions were taken and adapted from https://gist.github.com/number5/7250865 If they don't work for you, let us know on IRC (Esper/#MultiMC)!*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e2a85950..c34f363f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,33 @@
cmake_minimum_required(VERSION 2.8.9)
-project(GroupView)
+IF(WIN32)
+ # In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
+ cmake_policy(SET CMP0020 OLD)
+ENDIF()
+
+project(MultiMC)
+enable_testing()
+
+######## Set CMake options ########
+SET(CMAKE_AUTOMOC ON)
+SET(CMAKE_INCLUDE_CURRENT_DIR ON)
+SET(FILES_TO_TRANSLATE )
+
+######## Set module path ########
+SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
+SET(MMC_SRC "${PROJECT_SOURCE_DIR}")
+SET(MMC_BIN "${PROJECT_BINARY_DIR}")
+
+# Output all executables and shared libs in the main build folder, not in subfolders.
+SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-set(CMAKE_AUTOMOC ON)
+IF(UNIX)
+ SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+ENDIF()
+
+set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
+######## Set compiler flags ########
IF(APPLE)
message(STATUS "Using APPLE CMAKE_CXX_FLAGS")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
@@ -16,25 +40,707 @@ ELSEIF(MINGW)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall")
ENDIF()
+################################ 3rd Party Libs ################################
+
+# Find the required Qt parts
find_package(Qt5Core REQUIRED)
-find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
+find_package(Qt5Network REQUIRED)
+find_package(Qt5Test REQUIRED)
+find_package(Qt5Concurrent REQUIRED)
+find_package(Qt5LinguistTools REQUIRED)
+
+include_directories(
+ ${Qt5Core_INCLUDE_DIRS}
+ ${Qt5Widgets_INCLUDE_DIRS}
+ ${Qt5Concurrent_INCLUDE_DIRS}
+ ${Qt5Network_INCLUDE_DIRS}
+ ${Qt5Test_INCLUDE_DIRS}
+ )
+
+# The Qt5 cmake files don't provide its install paths, so ask qmake.
+get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
+function(QUERY_QMAKE VAR RESULT)
+ exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
+ if(NOT return_code)
+ file(TO_CMAKE_PATH "${output}" output)
+ set(${RESULT} ${output} PARENT_SCOPE)
+ endif(NOT return_code)
+endfunction(QUERY_QMAKE)
+
+query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
+query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
+query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
+query_qmake(QT_HOST_DATA QT_DATA_DIR)
+set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
+
+
+################################ SET UP BUILD OPTIONS ################################
+
+######## Check endianness ########
+INCLUDE(TestBigEndian)
+TEST_BIG_ENDIAN(BIGENDIAN)
+IF(${BIGENDIAN})
+ ADD_DEFINITIONS(-DMULTIMC_BIG_ENDIAN)
+ENDIF(${BIGENDIAN})
+
+
+######## Set URLs ########
+
+SET(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
+
+
+######## Set version numbers ########
+SET(MultiMC_VERSION_MAJOR 0)
+SET(MultiMC_VERSION_MINOR 2)
+SET(MultiMC_VERSION_HOTFIX 1)
-include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS})
+# Build number
+SET(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
-set(SOURCES
- main.cpp
- main.h
+# Version type
+SET(MultiMC_VERSION_TYPE "Custom" CACHE STRING "MultiMC's version type. This should be one of 'Custom', 'Release', 'ReleaseCandidate', or 'Development', depending on what type of version this is.")
- GroupView.h
- GroupView.cpp
- Group.h
- Group.cpp
- GroupedProxyModel.h
- GroupedProxyModel.cpp
- InstanceDelegate.h
- InstanceDelegate.cpp
+# Build platform.
+SET(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
+
+# Version channel
+SET(MultiMC_VERSION_CHANNEL "" CACHE STRING "The current build's channel. Included in the version string.")
+
+# Channel list URL
+SET(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
+
+# Updater enabled?
+SET(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.")
+
+# Notification URL
+SET(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
+
+SET(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
+IF (MultiMC_VERSION_HOTFIX GREATER 0)
+ SET(MultiMC_RELEASE_VERSION_NAME "${MultiMC_RELEASE_VERSION_NAME}.${MultiMC_VERSION_HOTFIX}")
+ENDIF()
+
+# Build a version string to display in the configure logs.
+IF (MultiMC_VERSION_TYPE STREQUAL "Custom")
+ MESSAGE(STATUS "Version Type: Custom")
+ SET(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}")
+ELSEIF (MultiMC_VERSION_TYPE STREQUAL "Release")
+ MESSAGE(STATUS "Version Type: Stable Release")
+ SET(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}")
+ELSEIF (MultiMC_VERSION_TYPE STREQUAL "ReleaseCandidate")
+ MESSAGE(STATUS "Version Type: Release Candidate")
+ SET(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}-rc${MultiMC_VERSION_BUILD}")
+ELSEIF (MultiMC_VERSION_TYPE STREQUAL "Development")
+ MESSAGE(STATUS "Version Type: Development")
+ SET(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}-dev${MultiMC_VERSION_BUILD}")
+ELSE ()
+ MESSAGE(ERROR "Invalid build type.")
+ENDIF ()
+
+MESSAGE(STATUS "MultiMC 5 Version: ${MultiMC_VERSION_STRING}")
+
+# If the update system is enabled, make sure MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL are set.
+IF (MultiMC_UPDATER)
+ IF (MultiMC_VERSION_CHANNEL STREQUAL "")
+ MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_CHANNEL is not set.\n"
+ "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
+ ENDIF ()
+ IF (MultiMC_CHANLIST_URL STREQUAL "")
+ MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_CHANLIST_URL is not set.\n"
+ "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
+ ENDIF ()
+ IF (MultiMC_VERSION_BUILD LESS 0)
+ MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_BUILD is not set.\n"
+ "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
+ ENDIF ()
+
+ MESSAGE(STATUS "Updater is enabled. Channel list URL: ${MultiMC_CHANLIST_URL}")
+ENDIF ()
+
+#### Custom target to just print the version.
+ADD_CUSTOM_TARGET(version echo "Version: ${MultiMC_VERSION_STRING}")
+
+#### Check the current Git commit
+execute_process(COMMAND git rev-parse HEAD
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ RESULT_VARIABLE GIT_COMMIT_CHECK_RESULTVAR
+ OUTPUT_VARIABLE GIT_COMMIT_CHECK_OUTVAR
+ OUTPUT_STRIP_TRAILING_WHITESPACE
)
-add_executable(GroupView ${SOURCES})
-qt5_use_modules(GroupView Core Gui Widgets)
+IF(GIT_COMMIT_CHECK_RESULTVAR EQUAL 0)
+ SET(MultiMC_GIT_COMMIT "${GIT_COMMIT_CHECK_OUTVAR}")
+ MESSAGE(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
+ELSE()
+ SET(MultiMC_GIT_COMMIT "Unknown")
+ MESSAGE(STATUS "Failed to check Git commit. ${GIT_COMMIT_CHECK_RESULTVAR}")
+ENDIF()
+
+######## Configure header ########
+configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/include/config.h")
+
+
+######## Other Stuff ########
+
+ADD_DEFINITIONS(-DQUAZIP_STATIC)
+ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
+ADD_DEFINITIONS(-DLIBUTIL_STATIC)
+ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC)
+
+######## Packaging/install paths setup ########
+
+IF(UNIX AND APPLE)
+ SET(BINARY_DEST_DIR MultiMC.app/Contents/MacOS)
+ SET(PLUGIN_DEST_DIR MultiMC.app/Contents/MacOS)
+ SET(QTCONF_DEST_DIR MultiMC.app/Contents/Resources)
+ SET(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
+
+ SET(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
+ SET(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
+ SET(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
+ SET(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
+ SET(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
+ SET(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
+ SET(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
+ SET(MACOSX_BUNDLE_COPYRIGHT "Copyright 2013 MultiMC Contributors")
+ELSEIF(UNIX)
+ SET(BINARY_DEST_DIR bin)
+ SET(PLUGIN_DEST_DIR plugins)
+ SET(QTCONF_DEST_DIR .)
+ SET(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
+ELSEIF(WIN32)
+ SET(BINARY_DEST_DIR .)
+ SET(PLUGIN_DEST_DIR .)
+ SET(QTCONF_DEST_DIR .)
+ SET(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
+ENDIF()
+
+# directories to look for dependencies
+SET(DIRS "${QT_LIBS_DIR}")
+
+################################ Included Libs ################################
+
+# Add quazip
+add_subdirectory(depends/quazip)
+include_directories(depends/quazip)
+
+# Add the java launcher and checker
+add_subdirectory(depends/launcher)
+add_subdirectory(depends/javacheck)
+
+# Add xz decompression
+add_subdirectory(depends/xz-embedded)
+include_directories(${XZ_INCLUDE_DIR})
+
+# Add pack200 decompression
+add_subdirectory(depends/pack200)
+include_directories(${PACK200_INCLUDE_DIR})
+
+######## MultiMC Libs ########
+
+# Add the util library.
+add_subdirectory(depends/util)
+include_directories(${LIBUTIL_INCLUDE_DIR})
+
+# Add the settings library.
+add_subdirectory(depends/settings)
+include_directories(${LIBSETTINGS_INCLUDE_DIR})
+
+# Add the group view library.
+add_subdirectory(depends/groupview)
+include_directories(${LIBGROUPVIEW_INCLUDE_DIR})
+
+# Add the updater
+add_subdirectory(mmc_updater)
+
+################################ FILES ################################
+
+######## Sources and headers ########
+SET(MULTIMC_SOURCES
+# Application base
+MultiMC.h
+MultiMC.cpp
+MultiMCVersion.h
+
+# Logging
+logger/QsDebugOutput.cpp
+logger/QsDebugOutput.h
+logger/QsLog.cpp
+logger/QsLog.h
+logger/QsLogDest.cpp
+logger/QsLogDest.h
+
+# GUI - windows
+gui/MainWindow.h
+gui/MainWindow.cpp
+gui/ConsoleWindow.h
+gui/ConsoleWindow.cpp
+
+# GUI - dialogs
+gui/dialogs/SettingsDialog.h
+gui/dialogs/SettingsDialog.cpp
+gui/dialogs/CopyInstanceDialog.h
+gui/dialogs/CopyInstanceDialog.cpp
+gui/dialogs/NewInstanceDialog.cpp
+gui/dialogs/ProgressDialog.h
+gui/dialogs/ProgressDialog.cpp
+gui/dialogs/AboutDialog.h
+gui/dialogs/AboutDialog.cpp
+gui/dialogs/VersionSelectDialog.h
+gui/dialogs/VersionSelectDialog.cpp
+gui/dialogs/LwjglSelectDialog.h
+gui/dialogs/LwjglSelectDialog.cpp
+gui/dialogs/InstanceSettings.h
+gui/dialogs/InstanceSettings.cpp
+gui/dialogs/IconPickerDialog.h
+gui/dialogs/IconPickerDialog.cpp
+gui/dialogs/LegacyModEditDialog.h
+gui/dialogs/LegacyModEditDialog.cpp
+gui/dialogs/OneSixModEditDialog.h
+gui/dialogs/OneSixModEditDialog.cpp
+gui/dialogs/ModEditDialogCommon.h
+gui/dialogs/ModEditDialogCommon.cpp
+gui/dialogs/EditNotesDialog.h
+gui/dialogs/EditNotesDialog.cpp
+gui/dialogs/CustomMessageBox.h
+gui/dialogs/CustomMessageBox.cpp
+gui/dialogs/EditAccountDialog.h
+gui/dialogs/EditAccountDialog.cpp
+gui/dialogs/AccountListDialog.h
+gui/dialogs/AccountListDialog.cpp
+gui/dialogs/AccountSelectDialog.h
+gui/dialogs/AccountSelectDialog.cpp
+gui/dialogs/UpdateDialog.h
+gui/dialogs/UpdateDialog.cpp
+
+# GUI - widgets
+gui/widgets/Common.h
+gui/widgets/Common.cpp
+gui/widgets/InstanceDelegate.h
+gui/widgets/InstanceDelegate.cpp
+gui/widgets/ModListView.h
+gui/widgets/ModListView.cpp
+gui/widgets/VersionListView.h
+gui/widgets/VersionListView.cpp
+gui/widgets/LabeledToolButton.h
+gui/widgets/LabeledToolButton.cpp
+gui/widgets/MCModInfoFrame.h
+gui/widgets/MCModInfoFrame.cpp
+
+# Base classes and infrastructure
+logic/BaseVersion.h
+logic/MinecraftVersion.h
+logic/InstanceFactory.h
+logic/InstanceFactory.cpp
+logic/BaseInstance.h
+logic/BaseInstance.cpp
+logic/BaseInstance_p.h
+
+logic/MinecraftProcess.h
+logic/MinecraftProcess.cpp
+logic/Mod.h
+logic/Mod.cpp
+logic/ModList.h
+logic/ModList.cpp
+
+# Basic instance launcher for starting from terminal
+logic/InstanceLauncher.h
+logic/InstanceLauncher.cpp
+
+# network stuffs
+logic/net/NetAction.h
+logic/net/MD5EtagDownload.h
+logic/net/MD5EtagDownload.cpp
+logic/net/ByteArrayDownload.h
+logic/net/ByteArrayDownload.cpp
+logic/net/CacheDownload.h
+logic/net/CacheDownload.cpp
+logic/net/ForgeMirrors.h
+logic/net/ForgeMirrors.cpp
+logic/net/ForgeXzDownload.h
+logic/net/ForgeXzDownload.cpp
+logic/net/NetJob.h
+logic/net/NetJob.cpp
+logic/net/HttpMetaCache.h
+logic/net/HttpMetaCache.cpp
+logic/net/PasteUpload.h
+logic/net/PasteUpload.cpp
+logic/net/URLConstants.h
+
+# Yggdrasil login stuff
+logic/auth/AuthSession.h
+logic/auth/AuthSession.cpp
+logic/auth/MojangAccountList.h
+logic/auth/MojangAccountList.cpp
+logic/auth/MojangAccount.h
+logic/auth/MojangAccount.cpp
+logic/auth/YggdrasilTask.h
+logic/auth/YggdrasilTask.cpp
+logic/auth/flows/AuthenticateTask.h
+logic/auth/flows/AuthenticateTask.cpp
+logic/auth/flows/RefreshTask.cpp
+logic/auth/flows/RefreshTask.cpp
+logic/auth/flows/ValidateTask.h
+logic/auth/flows/ValidateTask.cpp
+
+# Update system
+logic/updater/UpdateChecker.h
+logic/updater/UpdateChecker.cpp
+logic/updater/DownloadUpdateTask.h
+logic/updater/DownloadUpdateTask.cpp
+logic/updater/NotificationChecker.h
+logic/updater/NotificationChecker.cpp
+
+# News System
+logic/news/NewsChecker.h
+logic/news/NewsChecker.cpp
+logic/news/NewsEntry.h
+logic/news/NewsEntry.cpp
+
+# Status system
+logic/status/StatusChecker.h
+logic/status/StatusChecker.cpp
+
+# legacy instances
+logic/LegacyInstance.h
+logic/LegacyInstance.cpp
+logic/LegacyInstance_p.h
+logic/LegacyUpdate.h
+logic/LegacyUpdate.cpp
+logic/LegacyForge.h
+logic/LegacyForge.cpp
+
+# 1.6 instances
+logic/OneSixInstance.h
+logic/OneSixInstance.cpp
+logic/OneSixInstance_p.h
+logic/OneSixUpdate.h
+logic/OneSixUpdate.cpp
+logic/OneSixVersion.h
+logic/OneSixVersion.cpp
+logic/OneSixLibrary.h
+logic/OneSixLibrary.cpp
+logic/OneSixRule.h
+logic/OneSixRule.cpp
+logic/OpSys.h
+logic/OpSys.cpp
+logic/ForgeInstaller.h
+logic/ForgeInstaller.cpp
+logic/LiteLoaderInstaller.h
+logic/LiteLoaderInstaller.cpp
+
+# Nostalgia
+logic/NostalgiaInstance.h
+logic/NostalgiaInstance.cpp
+
+# FTB
+logic/OneSixFTBInstance.h
+logic/OneSixFTBInstance.cpp
+logic/LegacyFTBInstance.h
+logic/LegacyFTBInstance.cpp
+
+# Lists
+logic/lists/InstanceList.h
+logic/lists/InstanceList.cpp
+logic/lists/BaseVersionList.h
+logic/lists/BaseVersionList.cpp
+logic/lists/MinecraftVersionList.h
+logic/lists/MinecraftVersionList.cpp
+logic/lists/LwjglVersionList.h
+logic/lists/LwjglVersionList.cpp
+logic/lists/ForgeVersionList.h
+logic/lists/ForgeVersionList.cpp
+logic/lists/JavaVersionList.h
+logic/lists/JavaVersionList.cpp
+
+# Icons
+logic/icons/MMCIcon.h
+logic/icons/MMCIcon.cpp
+logic/icons/IconList.h
+logic/icons/IconList.cpp
+
+
+# misc model/view
+logic/EnabledItemFilter.h
+logic/EnabledItemFilter.cpp
+
+# Tasks
+logic/tasks/ProgressProvider.h
+logic/tasks/Task.h
+logic/tasks/Task.cpp
+logic/tasks/ThreadTask.h
+logic/tasks/ThreadTask.cpp
+logic/tasks/SequentialTask.h
+logic/tasks/SequentialTask.cpp
+
+# Utilities
+logic/JavaChecker.h
+logic/JavaChecker.cpp
+logic/JavaUtils.h
+logic/JavaUtils.cpp
+logic/NagUtils.h
+logic/NagUtils.cpp
+logic/SkinUtils.h
+logic/SkinUtils.cpp
+logic/JavaCheckerJob.h
+logic/JavaCheckerJob.cpp
+
+# Assets
+logic/assets/AssetsMigrateTask.h
+logic/assets/AssetsMigrateTask.cpp
+logic/assets/AssetsUtils.h
+logic/assets/AssetsUtils.cpp
+)
+
+
+######## UIs ########
+SET(MULTIMC_UIS
+
+# Windows
+gui/MainWindow.ui
+gui/ConsoleWindow.ui
+
+# Dialogs
+gui/dialogs/SettingsDialog.ui
+gui/dialogs/CopyInstanceDialog.ui
+gui/dialogs/NewInstanceDialog.ui
+gui/dialogs/AboutDialog.ui
+gui/dialogs/VersionSelectDialog.ui
+gui/dialogs/LwjglSelectDialog.ui
+gui/dialogs/InstanceSettings.ui
+gui/dialogs/ProgressDialog.ui
+gui/dialogs/IconPickerDialog.ui
+gui/dialogs/LegacyModEditDialog.ui
+gui/dialogs/OneSixModEditDialog.ui
+gui/dialogs/EditNotesDialog.ui
+gui/dialogs/AccountListDialog.ui
+gui/dialogs/AccountSelectDialog.ui
+gui/dialogs/EditAccountDialog.ui
+gui/dialogs/UpdateDialog.ui
+
+# Widgets/other
+gui/widgets/MCModInfoFrame.ui
+)
+
+set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${MULTIMC_SOURCES} ${MULTIMC_UIS})
+
+SET(MULTIMC_QRCS
+resources/backgrounds/backgrounds.qrc
+resources/multimc/multimc.qrc
+resources/instances/instances.qrc
+)
+
+
+######## Windows resource files ########
+IF(WIN32)
+SET(MULTIMC_RCS resources/multimc.rc)
+ENDIF()
+
+####### X11 Stuff #######
+IF(UNIX AND NOT APPLE)
+ SET(MultiMC_QT_ADDITIONAL_MODULES ${MultiMC_QT_ADDITIONAL_MODULES} X11Extras)
+ SET(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} xcb)
+ LIST(APPEND MULTIMC_SOURCES gui/Platform_X11.cpp)
+ELSE()
+ LIST(APPEND MULTIMC_SOURCES gui/Platform_Other.cpp)
+ENDIF()
+
+
+################################ COMPILE ################################
+
+# Link additional libraries
+IF(WIN32)
+ SET(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS}
+ Qt5::WinMain # Link WinMain
+ )
+ENDIF(WIN32)
+
+OPTION(MultiMC_UPDATER_DRY_RUN "Enable updater dry-run mode -- for updater development." OFF)
+OPTION(MultiMC_UPDATER_FORCE_LOCAL "Do not download updated updater -- for updater development." OFF)
+
+OPTION(MultiMC_CODE_COVERAGE "Compiles for code coverage" OFF)
+IF(MultiMC_CODE_COVERAGE)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O0 --coverage")
+ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
+ENDIF(MultiMC_CODE_COVERAGE)
+
+# Tell CMake that MultiMCLauncher.jar is generated.
+#SET_SOURCE_FILES_PROPERTIES(${PROJECT_BINARY_DIR}/depends/launcher/MultiMCLauncher.jar GENERATED)
+#SET_SOURCE_FILES_PROPERTIES(${PROJECT_BINARY_DIR}/depends/javacheck/JavaCheck.jar GENERATED)
+
+# Qt 5 stuff
+QT5_WRAP_UI(MULTIMC_UI ${MULTIMC_UIS})
+QT5_ADD_RESOURCES(MULTIMC_RESOURCES ${MULTIMC_QRCS})
+
+# Add common library
+ADD_LIBRARY(MultiMC_common STATIC ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES})
+
+# Add executable
+ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
+
+# Link
+TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
+TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS})
+QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
+QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
+
+################################ INSTALLATION AND PACKAGING ################################
+
+######## Install ########
+
+#### Executable ####
+IF(APPLE AND UNIX) ## OSX
+ INSTALL(TARGETS MultiMC
+ BUNDLE DESTINATION . COMPONENT Runtime
+ RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime
+ )
+
+ELSEIF(UNIX) ## LINUX and similar
+ INSTALL(TARGETS MultiMC
+ BUNDLE DESTINATION . COMPONENT Runtime
+ RUNTIME DESTINATION bin COMPONENT Runtime
+ )
+ INSTALL(PROGRAMS package/linux/MultiMC DESTINATION .)
+
+ELSEIF(WIN32) ## WINDOWS
+ INSTALL(TARGETS MultiMC
+ BUNDLE DESTINATION . COMPONENT Runtime
+ LIBRARY DESTINATION . COMPONENT Runtime
+ RUNTIME DESTINATION . COMPONENT Runtime
+ )
+ENDIF()
+
+#### Dist package logic ####
+
+if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
+# Image formats
+INSTALL(
+ DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
+ DESTINATION ${PLUGIN_DEST_DIR}
+ COMPONENT Runtime
+ REGEX "tga|svg|tiff|mng" EXCLUDE
+)
+
+# Platform plugins
+INSTALL(
+ DIRECTORY "${QT_PLUGINS_DIR}/platforms"
+ DESTINATION ${PLUGIN_DEST_DIR}
+ COMPONENT Runtime
+ REGEX "minimal|linuxfb|offscreen" EXCLUDE
+)
+else()
+# Image formats
+INSTALL(
+ DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
+ DESTINATION ${PLUGIN_DEST_DIR}
+ COMPONENT Runtime
+ REGEX "tga|svg|tiff|mng" EXCLUDE
+ REGEX "d\\." EXCLUDE
+ REGEX "_debug\\." EXCLUDE
+)
+
+# Platform plugins
+INSTALL(
+ DIRECTORY "${QT_PLUGINS_DIR}/platforms"
+ DESTINATION ${PLUGIN_DEST_DIR}
+ COMPONENT Runtime
+ REGEX "minimal|linuxfb|offscreen" EXCLUDE
+ REGEX "d\\." EXCLUDE
+ REGEX "_debug\\." EXCLUDE
+)
+IF(APPLE)
+ # Accessible plugin to make buttons look decent on osx
+ INSTALL(
+ DIRECTORY "${QT_PLUGINS_DIR}/accessible"
+ DESTINATION ${PLUGIN_DEST_DIR}
+ COMPONENT Runtime
+ REGEX "quick" EXCLUDE
+ REGEX "d\\." EXCLUDE
+ REGEX "_debug\\." EXCLUDE
+ )
+ENDIF()
+
+endif()
+
+# qtconf
+INSTALL(
+ CODE "
+FILE(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\")
+"
+ COMPONENT Runtime
+)
+
+# ICNS file for OS X
+IF(APPLE)
+ INSTALL(FILES resources/MultiMC.icns DESTINATION MultiMC.app/Contents/Resources)
+ENDIF()
+
+CONFIGURE_FILE(
+ "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
+ @ONLY)
+INSTALL(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
+
+
+
+######## Package ########
+
+# Package with CPack
+IF(UNIX)
+ if(APPLE)
+ SET(CPACK_GENERATOR "ZIP")
+ else()
+ SET(CPACK_GENERATOR "TGZ")
+ endif()
+ELSEIF(WIN32)
+ SET(CPACK_GENERATOR "ZIP")
+ENDIF()
+SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
+
+SET(CPACK_PACKAGE_NAME "MultiMC 5")
+SET(CPACK_PACKAGE_VENDOR "")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MultiMC - Minecraft launcher and management tool.")
+SET(CPACK_PACKAGE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
+SET(CPACK_PACKAGE_VERSION_MAJOR ${MultiMC_VERSION_MAJOR})
+SET(CPACK_PACKAGE_VERSION_MINOR ${MultiMC_VERSION_MINOR})
+SET(CPACK_PACKAGE_VERSION_PATCH ${MultiMC_VERSION_REV})
+
+IF(CPACK_GENERATOR STREQUAL "NSIS")
+SET(CPACK_PACKAGE_FILE_NAME "Setup-MultiMC")
+ELSE()
+SET(CPACK_PACKAGE_FILE_NAME "MultiMC")
+ENDIF()
+
+IF(WIN32)
+SET(CPACK_PACKAGE_INSTALL_DIRECTORY "MultiMC 5")
+ENDIF()
+
+INCLUDE(CPack)
+
+include_directories(${PROJECT_BINARY_DIR}/include)
+
+### translation stuff
+
+file (GLOB TRANSLATIONS_FILES translations/*.ts)
+
+option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts files (WARNING: make clean will delete the source .ts files! Danger!)")
+IF(UPDATE_TRANSLATIONS)
+ qt5_create_translation(QM_FILES ${FILES_TO_TRANSLATE} ${TRANSLATIONS_FILES})
+ELSE()
+ qt5_add_translation(QM_FILES ${TRANSLATIONS_FILES})
+ENDIF()
+
+add_custom_target (translations DEPENDS ${QM_FILES})
+IF(APPLE AND UNIX) ## OSX
+ install(FILES ${QM_FILES} DESTINATION MultiMC.app/Contents/MacOS/translations)
+ELSE()
+ install(FILES ${QM_FILES} DESTINATION translations)
+ENDIF()
+
+
+# Tests
+add_subdirectory(tests)
diff --git a/MultiMC.cpp b/MultiMC.cpp
new file mode 100644
index 00000000..e4a30f22
--- /dev/null
+++ b/MultiMC.cpp
@@ -0,0 +1,635 @@
+
+#include "MultiMC.h"
+#include <iostream>
+#include <QDir>
+#include <QFileInfo>
+#include <QNetworkAccessManager>
+#include <QTranslator>
+#include <QLibraryInfo>
+#include <QMessageBox>
+#include <QStringList>
+#include <QDesktopServices>
+
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "logic/lists/InstanceList.h"
+#include "logic/auth/MojangAccountList.h"
+#include "logic/icons/IconList.h"
+#include "logic/lists/LwjglVersionList.h"
+#include "logic/lists/MinecraftVersionList.h"
+#include "logic/lists/ForgeVersionList.h"
+
+#include "logic/news/NewsChecker.h"
+
+#include "logic/status/StatusChecker.h"
+
+#include "logic/InstanceLauncher.h"
+#include "logic/net/HttpMetaCache.h"
+#include "logic/net/URLConstants.h"
+
+#include "logic/JavaUtils.h"
+
+#include "logic/updater/UpdateChecker.h"
+#include "logic/updater/NotificationChecker.h"
+
+#include "pathutils.h"
+#include "cmdutils.h"
+#include <inisettingsobject.h>
+#include <setting.h>
+#include "logger/QsLog.h"
+#include <logger/QsLogDest.h>
+
+using namespace Util::Commandline;
+
+MultiMC::MultiMC(int &argc, char **argv, bool root_override)
+ : QApplication(argc, argv), m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_HOTFIX,
+ VERSION_BUILD, MultiMCVersion::VERSION_TYPE, VERSION_CHANNEL, BUILD_PLATFORM}
+{
+ setOrganizationName("MultiMC");
+ setApplicationName("MultiMC5");
+
+ setAttribute(Qt::AA_UseHighDpiPixmaps);
+ // Don't quit on hiding the last window
+ this->setQuitOnLastWindowClosed(false);
+
+ // Commandline parsing
+ QHash<QString, QVariant> args;
+ {
+ Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
+
+ // --help
+ parser.addSwitch("help");
+ parser.addShortOpt("help", 'h');
+ parser.addDocumentation("help", "display this help and exit.");
+ // --version
+ parser.addSwitch("version");
+ parser.addShortOpt("version", 'V');
+ parser.addDocumentation("version", "display program version and exit.");
+ // --dir
+ parser.addOption("dir", applicationDirPath());
+ parser.addShortOpt("dir", 'd');
+ parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of "
+ "the binary location (use '.' for current)");
+ // WARNING: disabled until further notice
+ /*
+ // --launch
+ parser.addOption("launch");
+ parser.addShortOpt("launch", 'l');
+ parser.addDocumentation("launch", "tries to launch the given instance", "<inst>");
+*/
+ // parse the arguments
+ try
+ {
+ args = parser.parse(arguments());
+ }
+ catch (ParsingError e)
+ {
+ std::cerr << "CommandLineError: " << e.what() << std::endl;
+ std::cerr << "Try '%1 -h' to get help on MultiMC's command line parameters."
+ << std::endl;
+ m_status = MultiMC::Failed;
+ return;
+ }
+
+ // display help and exit
+ if (args["help"].toBool())
+ {
+ std::cout << qPrintable(parser.compileHelp(arguments()[0]));
+ m_status = MultiMC::Succeeded;
+ return;
+ }
+
+ // display version and exit
+ if (args["version"].toBool())
+ {
+ std::cout << "Version " << VERSION_STR << std::endl;
+ std::cout << "Git " << GIT_COMMIT << std::endl;
+ m_status = MultiMC::Succeeded;
+ return;
+ }
+ }
+ origcwdPath = QDir::currentPath();
+ binPath = applicationDirPath();
+ QString adjustedBy;
+ // change directory
+ QString dirParam = args["dir"].toString();
+ if (!dirParam.isEmpty())
+ {
+ // the dir param. it makes multimc data path point to whatever the user specified
+ // on command line
+ adjustedBy += "Command line " + dirParam;
+ dataPath = dirParam;
+ }
+ else
+ {
+ dataPath = applicationDirPath();
+ adjustedBy += "Fallback to binary path " + dataPath;
+ }
+
+ if(!ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
+ {
+ // BAD STUFF. WHAT DO?
+ initLogger();
+ QLOG_ERROR() << "Failed to set work path. Will exit. NOW.";
+ m_status = MultiMC::Failed;
+ return;
+ }
+
+ if (root_override)
+ {
+ rootPath = binPath;
+ }
+ else
+ {
+ #ifdef Q_OS_LINUX
+ QDir foo(PathCombine(binPath, ".."));
+ rootPath = foo.absolutePath();
+ #elif defined(Q_OS_WIN32)
+ rootPath = binPath;
+ #elif defined(Q_OS_MAC)
+ QDir foo(PathCombine(binPath, "../.."));
+ rootPath = foo.absolutePath();
+ #endif
+ }
+
+ // init the logger
+ initLogger();
+
+ QLOG_INFO() << "MultiMC 5, (c) 2013 MultiMC Contributors";
+ QLOG_INFO() << "Version : " << VERSION_STR;
+ QLOG_INFO() << "Git commit : " << GIT_COMMIT;
+ if (adjustedBy.size())
+ {
+ QLOG_INFO() << "Work dir before adjustment : " << origcwdPath;
+ QLOG_INFO() << "Work dir after adjustment : " << QDir::currentPath();
+ QLOG_INFO() << "Adjusted by : " << adjustedBy;
+ }
+ else
+ {
+ QLOG_INFO() << "Work dir : " << QDir::currentPath();
+ }
+ QLOG_INFO() << "Binary path : " << binPath;
+ QLOG_INFO() << "Application root path : " << rootPath;
+
+ // load settings
+ initGlobalSettings();
+
+ // load translations
+ initTranslations();
+
+ // initialize the updater
+ m_updateChecker.reset(new UpdateChecker());
+
+ // initialize the notification checker
+ m_notificationChecker.reset(new NotificationChecker());
+
+ // initialize the news checker
+ m_newsChecker.reset(new NewsChecker(NEWS_RSS_URL));
+
+ // initialize the status checker
+ m_statusChecker.reset(new StatusChecker());
+
+ // and instances
+ auto InstDirSetting = m_settings->getSetting("InstanceDir");
+ m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
+ QLOG_INFO() << "Loading Instances...";
+ m_instances->loadList();
+ connect(InstDirSetting.get(), SIGNAL(settingChanged(const Setting &, QVariant)),
+ m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant)));
+
+ // and accounts
+ m_accounts.reset(new MojangAccountList(this));
+ QLOG_INFO() << "Loading accounts...";
+ m_accounts->setListFilePath("accounts.json", true);
+ m_accounts->loadList();
+
+ // init the http meta cache
+ initHttpMetaCache();
+
+ // create the global network manager
+ m_qnam.reset(new QNetworkAccessManager(this));
+
+ // init proxy settings
+ updateProxySettings();
+
+ // launch instance, if that's what should be done
+ // WARNING: disabled until further notice
+ /*
+ if (!args["launch"].isNull())
+ {
+ if (InstanceLauncher(args["launch"].toString()).launch())
+ m_status = MultiMC::Succeeded;
+ else
+ m_status = MultiMC::Failed;
+ return;
+ }
+*/
+ connect(this, SIGNAL(aboutToQuit()), SLOT(onExit()));
+ m_status = MultiMC::Initialized;
+}
+
+MultiMC::~MultiMC()
+{
+ if (m_mmc_translator)
+ {
+ removeTranslator(m_mmc_translator.get());
+ }
+ if (m_qt_translator)
+ {
+ removeTranslator(m_qt_translator.get());
+ }
+}
+
+void MultiMC::initTranslations()
+{
+ QLocale locale(m_settings->get("Language").toString());
+ QLocale::setDefault(locale);
+ QLOG_INFO() << "Your language is" << locale.bcp47Name();
+ m_qt_translator.reset(new QTranslator());
+ if (m_qt_translator->load("qt_" + locale.bcp47Name(),
+ QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ {
+ QLOG_DEBUG() << "Loading Qt Language File for"
+ << locale.bcp47Name().toLocal8Bit().constData() << "...";
+ if (!installTranslator(m_qt_translator.get()))
+ {
+ QLOG_ERROR() << "Loading Qt Language File failed.";
+ m_qt_translator.reset();
+ }
+ }
+ else
+ {
+ m_qt_translator.reset();
+ }
+
+ m_mmc_translator.reset(new QTranslator());
+ if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), MMC->root() + "/translations"))
+ {
+ QLOG_DEBUG() << "Loading MMC Language File for"
+ << locale.bcp47Name().toLocal8Bit().constData() << "...";
+ if (!installTranslator(m_mmc_translator.get()))
+ {
+ QLOG_ERROR() << "Loading MMC Language File failed.";
+ m_mmc_translator.reset();
+ }
+ }
+ else
+ {
+ m_mmc_translator.reset();
+ }
+}
+
+void moveFile(const QString &oldName, const QString &newName)
+{
+ QFile::remove(newName);
+ QFile::copy(oldName, newName);
+ QFile::remove(oldName);
+}
+void MultiMC::initLogger()
+{
+ static const QString logBase = "MultiMC-%0.log";
+
+ moveFile(logBase.arg(3), logBase.arg(4));
+ moveFile(logBase.arg(2), logBase.arg(3));
+ moveFile(logBase.arg(1), logBase.arg(2));
+ moveFile(logBase.arg(0), logBase.arg(1));
+
+ // init the logging mechanism
+ QsLogging::Logger &logger = QsLogging::Logger::instance();
+ logger.setLoggingLevel(QsLogging::TraceLevel);
+ m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination(logBase.arg(0));
+ m_debugDestination = QsLogging::DestinationFactory::MakeQDebugDestination();
+ logger.addDestination(m_fileDestination.get());
+ logger.addDestination(m_debugDestination.get());
+ // log all the things
+ logger.setLoggingLevel(QsLogging::TraceLevel);
+}
+
+void MultiMC::initGlobalSettings()
+{
+ m_settings.reset(new INISettingsObject("multimc.cfg", this));
+ // Updates
+ m_settings->registerSetting("UpdateChannel", version().channel);
+ m_settings->registerSetting("AutoUpdate", true);
+
+ // Notifications
+ m_settings->registerSetting("ShownNotifications", QString());
+
+ // FTB
+ m_settings->registerSetting("TrackFTBInstances", false);
+#ifdef Q_OS_LINUX
+ QString ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
+#elif defined(Q_OS_WIN32)
+ QString ftbDefault = PathCombine(QDir::homePath(), "AppData/Roaming/ftblauncher");
+#elif defined(Q_OS_MAC)
+ QString ftbDefault =
+ PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
+#endif
+ m_settings->registerSetting("FTBLauncherRoot", ftbDefault);
+
+ m_settings->registerSetting("FTBRoot");
+ if (m_settings->get("FTBRoot").isNull())
+ {
+ QString ftbRoot;
+ QFile f(QDir(m_settings->get("FTBLauncherRoot").toString())
+ .absoluteFilePath("ftblaunch.cfg"));
+ QLOG_INFO() << "Attempting to read" << f.fileName();
+ if (f.open(QFile::ReadOnly))
+ {
+ const QString data = QString::fromLatin1(f.readAll());
+ QRegularExpression exp("installPath=(.*)");
+ ftbRoot = QDir::cleanPath(exp.match(data).captured(1));
+#ifdef Q_OS_WIN32
+ if (!ftbRoot.isEmpty())
+ {
+ if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/')
+ {
+ ftbRoot.remove(1, 1);
+ }
+ }
+#endif
+ if (ftbRoot.isEmpty())
+ {
+ QLOG_INFO() << "Failed to get FTB root path";
+ }
+ else
+ {
+ QLOG_INFO() << "FTB is installed at" << ftbRoot;
+ m_settings->set("FTBRoot", ftbRoot);
+ }
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't open" << f.fileName() << ":" << f.errorString();
+ QLOG_WARN() << "This is perfectly normal if you don't have FTB installed";
+ }
+ }
+
+ // Folders
+ m_settings->registerSetting("InstanceDir", "instances");
+ m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
+ m_settings->registerSetting({"LWJGLDir", "LwjglDir"}, "lwjgl");
+ m_settings->registerSetting("IconsDir", "icons");
+
+ // Editors
+ m_settings->registerSetting("JsonEditor", QString());
+
+ // Language
+ m_settings->registerSetting("Language", QLocale(QLocale::system().language()).bcp47Name());
+
+ // Console
+ m_settings->registerSetting("ShowConsole", true);
+ m_settings->registerSetting("AutoCloseConsole", true);
+ m_settings->registerSetting("LogPrePostOutput", true);
+
+ // Console Colors
+ // m_settings->registerSetting("SysMessageColor", QColor(Qt::blue));
+ // m_settings->registerSetting("StdOutColor", QColor(Qt::black));
+ // m_settings->registerSetting("StdErrColor", QColor(Qt::red));
+
+ // Window Size
+ m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false);
+ m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854);
+ m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
+
+ // Proxy Settings
+ m_settings->registerSetting("ProxyType", "Default");
+ m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1");
+ m_settings->registerSetting("ProxyPort", 8080);
+ m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, "");
+ m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, "");
+
+ // Memory
+ m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
+ m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
+ m_settings->registerSetting("PermGen", 64);
+
+ // Java Settings
+ m_settings->registerSetting("JavaPath", "");
+ m_settings->registerSetting("LastHostname", "");
+ m_settings->registerSetting("JvmArgs", "");
+
+ // Custom Commands
+ m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
+ m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
+
+ // The cat
+ m_settings->registerSetting("TheCat", false);
+
+ m_settings->registerSetting("InstSortMode", "Name");
+ m_settings->registerSetting("SelectedInstance", QString());
+
+ // Window state and geometry
+ m_settings->registerSetting("MainWindowState", "");
+ m_settings->registerSetting("MainWindowGeometry", "");
+
+ m_settings->registerSetting("ConsoleWindowState", "");
+ m_settings->registerSetting("ConsoleWindowGeometry", "");
+
+ m_settings->registerSetting("SettingsGeometry", "");
+}
+
+void MultiMC::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("skins", QDir("accounts/skins").absolutePath());
+ m_metacache->addBase("root", QDir(root()).absolutePath());
+ m_metacache->Load();
+}
+
+void MultiMC::updateProxySettings()
+{
+ QString proxyTypeStr = settings()->get("ProxyType").toString();
+
+ // Get the proxy settings from the settings object.
+ QString addr = settings()->get("ProxyAddr").toString();
+ int port = settings()->get("ProxyPort").value<qint16>();
+ QString user = settings()->get("ProxyUser").toString();
+ QString pass = settings()->get("ProxyPass").toString();
+
+ // Set the application proxy settings.
+ if (proxyTypeStr == "SOCKS5")
+ {
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, pass));
+ }
+ else if (proxyTypeStr == "HTTP")
+ {
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, pass));
+ }
+ 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);
+ }
+
+ QLOG_INFO() << "Detecting proxy settings...";
+ QNetworkProxy proxy = QNetworkProxy::applicationProxy();
+ if (m_qnam.get()) m_qnam->setProxy(proxy);
+ QString proxyDesc;
+ if (proxy.type() == QNetworkProxy::NoProxy)
+ {
+ QLOG_INFO() << "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());
+ QLOG_INFO() << proxyDesc;
+}
+
+std::shared_ptr<IconList> MultiMC::icons()
+{
+ if (!m_icons)
+ {
+ m_icons.reset(new IconList);
+ }
+ return m_icons;
+}
+
+std::shared_ptr<LWJGLVersionList> MultiMC::lwjgllist()
+{
+ if (!m_lwjgllist)
+ {
+ m_lwjgllist.reset(new LWJGLVersionList());
+ }
+ return m_lwjgllist;
+}
+
+std::shared_ptr<ForgeVersionList> MultiMC::forgelist()
+{
+ if (!m_forgelist)
+ {
+ m_forgelist.reset(new ForgeVersionList());
+ }
+ return m_forgelist;
+}
+
+std::shared_ptr<MinecraftVersionList> MultiMC::minecraftlist()
+{
+ if (!m_minecraftlist)
+ {
+ m_minecraftlist.reset(new MinecraftVersionList());
+ }
+ return m_minecraftlist;
+}
+
+std::shared_ptr<JavaVersionList> MultiMC::javalist()
+{
+ if (!m_javalist)
+ {
+ m_javalist.reset(new JavaVersionList());
+ }
+ return m_javalist;
+}
+
+void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
+{
+ // if we are going to update on exit, save the params now
+ if(flags & OnExit)
+ {
+ m_updateOnExitPath = updateFilesDir;
+ m_updateOnExitFlags = flags & ~OnExit;
+ return;
+ }
+ // otherwise if there already were some params for on exit update, clear them and continue
+ else if(m_updateOnExitPath.size())
+ {
+ m_updateOnExitFlags = None;
+ m_updateOnExitPath.clear();
+ }
+ QLOG_INFO() << "Installing updates.";
+ #ifdef WINDOWS
+ QString finishCmd = MMC->applicationFilePath();
+ QString updaterBinary = PathCombine(bin(), "updater.exe");
+ #elif LINUX
+ QString finishCmd = PathCombine(root(), "MultiMC");
+ QString updaterBinary = PathCombine(bin(), "updater");
+ #elif OSX
+ QString finishCmd = MMC->applicationFilePath();
+ QString updaterBinary = PathCombine(bin(), "updater");
+ #else
+ #error Unsupported operating system.
+ #endif
+
+ QStringList args;
+ // ./updater --install-dir $INSTALL_DIR --package-dir $UPDATEFILES_DIR --script
+ // $UPDATEFILES_DIR/file_list.xml --wait $PID --mode main
+ args << "--install-dir" << root();
+ args << "--package-dir" << updateFilesDir;
+ args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
+ args << "--wait" << QString::number(MMC->applicationPid());
+ if(flags & DryRun)
+ args << "--dry-run";
+ if (flags & RestartOnFinish)
+ {
+ args << "--finish-cmd" << finishCmd;
+ args << "--finish-dir" << data();
+ }
+ QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" ");
+ QFile::setPermissions(updaterBinary, (QFileDevice::Permission)0x7755);
+
+ if (!QProcess::startDetached(updaterBinary, args/*, root()*/))
+ {
+ QLOG_ERROR() << "Failed to start the updater process!";
+ return;
+ }
+
+ // Now that we've started the updater, quit MultiMC.
+ MMC->quit();
+}
+
+void MultiMC::onExit()
+{
+ if(m_updateOnExitPath.size())
+ {
+ installUpdates(m_updateOnExitPath, m_updateOnExitFlags);
+ }
+}
+
+bool MultiMC::openJsonEditor(const QString &filename)
+{
+ const QString file = QDir::current().absoluteFilePath(filename);
+ if (m_settings->get("JsonEditor").toString().isEmpty())
+ {
+ return QDesktopServices::openUrl(QUrl::fromLocalFile(file));
+ }
+ else
+ {
+ return QProcess::startDetached(m_settings->get("JsonEditor").toString(), QStringList()
+ << file);
+ }
+}
+
+#include "MultiMC.moc"
diff --git a/MultiMC.h b/MultiMC.h
new file mode 100644
index 00000000..638a442f
--- /dev/null
+++ b/MultiMC.h
@@ -0,0 +1,214 @@
+#pragma once
+
+#include "config.h"
+#include <QApplication>
+#include "MultiMCVersion.h"
+#include <memory>
+#include "logger/QsLog.h"
+#include "logger/QsLogDest.h"
+#include <QFlag>
+
+class MinecraftVersionList;
+class LWJGLVersionList;
+class HttpMetaCache;
+class SettingsObject;
+class InstanceList;
+class MojangAccountList;
+class IconList;
+class QNetworkAccessManager;
+class ForgeVersionList;
+class JavaVersionList;
+class UpdateChecker;
+class NotificationChecker;
+class NewsChecker;
+class StatusChecker;
+
+#if defined(MMC)
+#undef MMC
+#endif
+#define MMC (static_cast<MultiMC *>(QCoreApplication::instance()))
+
+// FIXME: possibly move elsewhere
+enum InstSortMode
+{
+ // Sort alphabetically by name.
+ Sort_Name,
+ // Sort by which instance was launched most recently.
+ Sort_LastLaunch
+};
+
+enum UpdateFlag
+{
+ None = 0x0,
+ RestartOnFinish = 0x1,
+ DryRun = 0x2,
+ OnExit = 0x4
+};
+Q_DECLARE_FLAGS(UpdateFlags, UpdateFlag);
+Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateFlags);
+
+class MultiMC : public QApplication
+{
+ Q_OBJECT
+public:
+ enum Status
+ {
+ Failed,
+ Succeeded,
+ Initialized
+ };
+
+public:
+ MultiMC(int &argc, char **argv, bool root_override = false);
+ virtual ~MultiMC();
+
+ std::shared_ptr<SettingsObject> settings()
+ {
+ return m_settings;
+ }
+
+ std::shared_ptr<InstanceList> instances()
+ {
+ return m_instances;
+ }
+
+ std::shared_ptr<MojangAccountList> accounts()
+ {
+ return m_accounts;
+ }
+
+ std::shared_ptr<IconList> icons();
+
+ Status status()
+ {
+ return m_status;
+ }
+
+ MultiMCVersion version()
+ {
+ return m_version;
+ }
+
+ std::shared_ptr<QNetworkAccessManager> qnam()
+ {
+ return m_qnam;
+ }
+
+ std::shared_ptr<HttpMetaCache> metacache()
+ {
+ return m_metacache;
+ }
+
+ std::shared_ptr<UpdateChecker> updateChecker()
+ {
+ return m_updateChecker;
+ }
+
+ std::shared_ptr<NotificationChecker> notificationChecker()
+ {
+ return m_notificationChecker;
+ }
+
+ std::shared_ptr<NewsChecker> newsChecker()
+ {
+ return m_newsChecker;
+ }
+
+ std::shared_ptr<StatusChecker> statusChecker()
+ {
+ return m_statusChecker;
+ }
+
+ std::shared_ptr<LWJGLVersionList> lwjgllist();
+
+ std::shared_ptr<ForgeVersionList> forgelist();
+
+ std::shared_ptr<MinecraftVersionList> minecraftlist();
+
+ std::shared_ptr<JavaVersionList> javalist();
+
+ void installUpdates(const QString updateFilesDir, UpdateFlags flags = None);
+
+ /*!
+ * Updates the application proxy settings from the settings object.
+ */
+ void updateProxySettings();
+
+ /*!
+ * Opens a json file using either a system default editor, or, if note empty, the editor
+ * specified in the settings
+ */
+ bool openJsonEditor(const QString &filename);
+
+ /// this is the root of the 'installation'. Used for automatic updates
+ const QString &root()
+ {
+ return rootPath;
+ }
+ /// this is the where the binary files reside
+ const QString &bin()
+ {
+ return binPath;
+ }
+ /// this is the work/data path. All user data is here.
+ const QString &data()
+ {
+ return dataPath;
+ }
+ /**
+ * this is the original work path before it was changed by the adjustment mechanism
+ */
+ const QString &origcwd()
+ {
+ return origcwdPath;
+ }
+
+private slots:
+ /**
+ * Do all the things that should be done before we exit
+ */
+ void onExit();
+
+private:
+ void initLogger();
+
+ void initGlobalSettings();
+
+ void initHttpMetaCache();
+
+ void initTranslations();
+
+private:
+ friend class UpdateCheckerTest;
+ friend class DownloadUpdateTaskTest;
+
+ std::shared_ptr<QTranslator> m_qt_translator;
+ std::shared_ptr<QTranslator> m_mmc_translator;
+ std::shared_ptr<SettingsObject> m_settings;
+ std::shared_ptr<InstanceList> m_instances;
+ std::shared_ptr<UpdateChecker> m_updateChecker;
+ std::shared_ptr<NotificationChecker> m_notificationChecker;
+ std::shared_ptr<NewsChecker> m_newsChecker;
+ std::shared_ptr<StatusChecker> m_statusChecker;
+ std::shared_ptr<MojangAccountList> m_accounts;
+ std::shared_ptr<IconList> m_icons;
+ std::shared_ptr<QNetworkAccessManager> m_qnam;
+ std::shared_ptr<HttpMetaCache> m_metacache;
+ std::shared_ptr<LWJGLVersionList> m_lwjgllist;
+ std::shared_ptr<ForgeVersionList> m_forgelist;
+ std::shared_ptr<MinecraftVersionList> m_minecraftlist;
+ std::shared_ptr<JavaVersionList> m_javalist;
+ QsLogging::DestinationPtr m_fileDestination;
+ QsLogging::DestinationPtr m_debugDestination;
+
+ QString m_updateOnExitPath;
+ UpdateFlags m_updateOnExitFlags = None;
+
+ QString rootPath;
+ QString binPath;
+ QString dataPath;
+ QString origcwdPath;
+
+ Status m_status = MultiMC::Failed;
+ MultiMCVersion m_version;
+};
diff --git a/MultiMCVersion.h b/MultiMCVersion.h
new file mode 100644
index 00000000..811b9076
--- /dev/null
+++ b/MultiMCVersion.h
@@ -0,0 +1,96 @@
+/* Copyright 2013 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 <QString>
+
+/*!
+ * \brief The Version class represents a MultiMC version.
+ */
+struct MultiMCVersion
+{
+ enum Type
+ {
+ //! Version type for stable release builds.
+ Release,
+
+ //! Version type for release candidates.
+ ReleaseCandidate,
+
+ //! Version type for development builds.
+ Development,
+
+ //! Version type for custom builds. This is the default when no version type is specified.
+ Custom
+ };
+
+ /*!
+ * \brief Converts the Version to a string.
+ * \return The version number in string format (major.minor.revision.build).
+ */
+ QString toString() const
+ {
+ QString vstr = QString("%1.%2").arg(
+ QString::number(major),
+ QString::number(minor));
+
+ if (hotfix > 0) vstr += "." + QString::number(hotfix);
+
+ // If the build is a development build or release candidate, add that info to the end.
+ if (type == Development) vstr += "-dev" + QString::number(build);
+ else if (type == ReleaseCandidate) vstr += "-rc" + QString::number(build);
+
+ return vstr;
+ }
+
+ QString typeName() const
+ {
+ switch (type)
+ {
+ case Release:
+ return "Stable Release";
+ case ReleaseCandidate:
+ return "Release Candidate";
+ case Development:
+ return "Development";
+ case Custom:
+ default:
+ return "Custom";
+ }
+ }
+
+ //! The major version number.
+ int major;
+
+ //! The minor version number.
+ int minor;
+
+ //! The hotfix number.
+ int hotfix;
+
+ //! The build number.
+ int build;
+
+ //! The build type.
+ Type type;
+
+ //! The build channel.
+ QString channel;
+
+ //! A short string identifying the platform that this version is for. For example, lin64 or win32.
+ QString platform;
+};
+
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..227f0731
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+![MultiMC](http://i.imgur.com/QJXbz.png)
+
+MultiMC 5
+=========
+
+MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. It also allows you to easily install and remove mods by simply dragging and dropping.
+
+## Building
+Check [BUILD.md](BUILD.md) for build instructions.
+
+## Contributing
+The repository is currently managed by @peterix and @drayshak - we're the ones likely to review pull requests. If you'd like to contribute to the project please talk to us on IRC (Esper/#MultiMC) first! This helps us organise ideas and keep in contact with you, and we're unlikely to accept anything blindly.
+
+We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted, but there are issues with it on Windows. If you have trouble setting it up, check [.clang-format](.clang-format) manually. We don't accept pull requests with poor formatting. If you have questions, talk to us on IRC (Esper/#MultiMC) _before_ submitting a pull request.
+
+## Forking/Redistributing
+We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
+
+Part of the reason for using the Apache license is we don't want people using the "MultiMC" name when redistributing the project. This means people must take the time to go through the source code and remove all references to "MultiMC", including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).
+
+Apache covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork *without* implying that you have our blessing.
+
+
+## License
+Copyright &copy; 2013 MultiMC Contributors
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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.
diff --git a/changelog.yaml b/changelog.yaml
new file mode 100644
index 00000000..8b96b76e
--- /dev/null
+++ b/changelog.yaml
@@ -0,0 +1,33 @@
+#
+# This is MultiMC's changelog. It is formatted in YAML.
+#
+# Each key below represents a release version name. Each release key has several string entries under it, each containing information about a single change. Each of these entries may contain Markdown for formatting.
+#
+
+0.0:
+ - Initial release.
+0.1:
+ - Reworked the version numbering system to support our [new Git workflow](http://nvie.com/posts/a-successful-git-branching-model/).
+ - Added a tray icon for the console window.
+ - Fixed instances getting deselected after FTB instances are loaded (or whenever the model is reset).
+ - Implemented proxy settings.
+ - Fixed sorting of Java installations in the Java list.
+ - Jar files are now distributed separately, rather than being extracted from the binary at runtime.
+ - Added additional information to the about dialog.
+0.1.1:
+ - Hotfix - Changed the issue tracker URL to [GitHub issues](https://github.com/MultiMC/MultiMC5/issues).
+0.2:
+ - Java memory settings have MB added to the number to make the units obvious.
+ - Complete rework of the launcher part. No more sensitive information in the process arguments.
+ - Cached downloads now do not destroy files on failure.
+ - Mojang service status is now on the MultiMC status bar.
+ - Java checker is no longer needed/used on instance launch.
+ - Support for private FTB packs.
+ - Fixed instance ID issues related to copying FTB packs without changing the instance name.
+ - Forge versions are better sorted (build numbers above 999 were sorted wrong).
+ - Fixed crash related to the MultiMC update channel picker in offline mode.
+ - Started using icon themes for the application icons, fixing many OSX graphical glitches.
+ - Icon sources have been located, along with icon licenses.
+ - Update to the German translation.
+0.2.1:
+ - Hotfix - move the native library extraction into the onesix launcher part.
diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in
new file mode 100644
index 00000000..809fab00
--- /dev/null
+++ b/cmake/MacOSXBundleInfo.plist.in
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>NSHighResolutionCapable</key>
+ <string>True</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+ <key>CFBundleGetInfoString</key>
+ <string>${MACOSX_BUNDLE_INFO_STRING}</string>
+ <key>CFBundleIconFile</key>
+ <string>${MACOSX_BUNDLE_ICON_FILE}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleLongVersionString</key>
+ <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
+ <key>CFBundleName</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+ <key>LSRequiresCarbon</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+</dict>
+</plist>
diff --git a/cmake/UseJava.cmake b/cmake/UseJava.cmake
new file mode 100644
index 00000000..1a5ef107
--- /dev/null
+++ b/cmake/UseJava.cmake
@@ -0,0 +1,881 @@
+# - Use Module for Java
+# This file provides functions for Java. It is assumed that FindJava.cmake
+# has already been loaded. See FindJava.cmake for information on how to
+# load Java into your CMake project.
+#
+# add_jar(TARGET_NAME SRC1 SRC2 .. SRCN RCS1 RCS2 .. RCSN)
+#
+# This command creates a <TARGET_NAME>.jar. It compiles the given source
+# files (SRC) and adds the given resource files (RCS) to the jar file.
+# If only resource files are given then just a jar file is created.
+#
+# Additional instructions:
+# To add compile flags to the target you can set these flags with
+# the following variable:
+#
+# set(CMAKE_JAVA_COMPILE_FLAGS -nowarn)
+#
+# To add a path or a jar file to the class path you can do this
+# with the CMAKE_JAVA_INCLUDE_PATH variable.
+#
+# set(CMAKE_JAVA_INCLUDE_PATH /usr/share/java/shibboleet.jar)
+#
+# To use a different output name for the target you can set it with:
+#
+# set(CMAKE_JAVA_TARGET_OUTPUT_NAME shibboleet.jar)
+# add_jar(foobar foobar.java)
+#
+# To use a different output directory than CMAKE_CURRENT_BINARY_DIR
+# you can set it with:
+#
+# set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/bin)
+#
+# To define an entry point in your jar you can set it with:
+#
+# set(CMAKE_JAVA_JAR_ENTRY_POINT com/examples/MyProject/Main)
+#
+# To add a VERSION to the target output name you can set it using
+# CMAKE_JAVA_TARGET_VERSION. This will create a jar file with the name
+# shibboleet-1.0.0.jar and will create a symlink shibboleet.jar
+# pointing to the jar with the version information.
+#
+# set(CMAKE_JAVA_TARGET_VERSION 1.2.0)
+# add_jar(shibboleet shibbotleet.java)
+#
+# If the target is a JNI library, utilize the following commands to
+# create a JNI symbolic link:
+#
+# set(CMAKE_JNI_TARGET TRUE)
+# set(CMAKE_JAVA_TARGET_VERSION 1.2.0)
+# add_jar(shibboleet shibbotleet.java)
+# install_jar(shibboleet ${LIB_INSTALL_DIR}/shibboleet)
+# install_jni_symlink(shibboleet ${JAVA_LIB_INSTALL_DIR})
+#
+# If a single target needs to produce more than one jar from its
+# java source code, to prevent the accumulation of duplicate class
+# files in subsequent jars, set/reset CMAKE_JAR_CLASSES_PREFIX prior
+# to calling the add_jar() function:
+#
+# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/foo)
+# add_jar(foo foo.java)
+#
+# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/bar)
+# add_jar(bar bar.java)
+#
+# Target Properties:
+# The add_jar() functions sets some target properties. You can get these
+# properties with the
+# get_property(TARGET <target_name> PROPERTY <propery_name>)
+# command.
+#
+# INSTALL_FILES The files which should be installed. This is used by
+# install_jar().
+# JNI_SYMLINK The JNI symlink which should be installed.
+# This is used by install_jni_symlink().
+# JAR_FILE The location of the jar file so that you can include
+# it.
+# CLASS_DIR The directory where the class files can be found. For
+# example to use them with javah.
+#
+# find_jar(<VAR>
+# name | NAMES name1 [name2 ...]
+# [PATHS path1 [path2 ... ENV var]]
+# [VERSIONS version1 [version2]]
+# [DOC "cache documentation string"]
+# )
+#
+# This command is used to find a full path to the named jar. A cache
+# entry named by <VAR> is created to stor the result of this command. If
+# the full path to a jar is found the result is stored in the variable
+# and the search will not repeated unless the variable is cleared. If
+# nothing is found, the result will be <VAR>-NOTFOUND, and the search
+# will be attempted again next time find_jar is invoked with the same
+# variable.
+# The name of the full path to a file that is searched for is specified
+# by the names listed after NAMES argument. Additional search locations
+# can be specified after the PATHS argument. If you require special a
+# version of a jar file you can specify it with the VERSIONS argument.
+# The argument after DOC will be used for the documentation string in
+# the cache.
+#
+# install_jar(TARGET_NAME DESTINATION)
+#
+# This command installs the TARGET_NAME files to the given DESTINATION.
+# It should be called in the same scope as add_jar() or it will fail.
+#
+# install_jni_symlink(TARGET_NAME DESTINATION)
+#
+# This command installs the TARGET_NAME JNI symlinks to the given
+# DESTINATION. It should be called in the same scope as add_jar()
+# or it will fail.
+#
+# create_javadoc(<VAR>
+# PACKAGES pkg1 [pkg2 ...]
+# [SOURCEPATH <sourcepath>]
+# [CLASSPATH <classpath>]
+# [INSTALLPATH <install path>]
+# [DOCTITLE "the documentation title"]
+# [WINDOWTITLE "the title of the document"]
+# [AUTHOR TRUE|FALSE]
+# [USE TRUE|FALSE]
+# [VERSION TRUE|FALSE]
+# )
+#
+# Create java documentation based on files or packages. For more
+# details please read the javadoc manpage.
+#
+# There are two main signatures for create_javadoc. The first
+# signature works with package names on a path with source files:
+#
+# Example:
+# create_javadoc(my_example_doc
+# PACKAGES com.exmaple.foo com.example.bar
+# SOURCEPATH "${CMAKE_CURRENT_SOURCE_DIR}"
+# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH}
+# WINDOWTITLE "My example"
+# DOCTITLE "<h1>My example</h1>"
+# AUTHOR TRUE
+# USE TRUE
+# VERSION TRUE
+# )
+#
+# The second signature for create_javadoc works on a given list of
+# files.
+#
+# create_javadoc(<VAR>
+# FILES file1 [file2 ...]
+# [CLASSPATH <classpath>]
+# [INSTALLPATH <install path>]
+# [DOCTITLE "the documentation title"]
+# [WINDOWTITLE "the title of the document"]
+# [AUTHOR TRUE|FALSE]
+# [USE TRUE|FALSE]
+# [VERSION TRUE|FALSE]
+# )
+#
+# Example:
+# create_javadoc(my_example_doc
+# FILES ${example_SRCS}
+# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH}
+# WINDOWTITLE "My example"
+# DOCTITLE "<h1>My example</h1>"
+# AUTHOR TRUE
+# USE TRUE
+# VERSION TRUE
+# )
+#
+# Both signatures share most of the options. These options are the
+# same as what you can find in the javadoc manpage. Please look at
+# the manpage for CLASSPATH, DOCTITLE, WINDOWTITLE, AUTHOR, USE and
+# VERSION.
+#
+# The documentation will be by default installed to
+#
+# ${CMAKE_INSTALL_PREFIX}/share/javadoc/<VAR>
+#
+# if you don't set the INSTALLPATH.
+#
+
+#=============================================================================
+# Copyright 2010-2011 Andreas schneider <asn@redhat.com>
+# Copyright 2010 Ben Boeckel <ben.boeckel@kitware.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+function (__java_copy_file src dest comment)
+ add_custom_command(
+ OUTPUT ${dest}
+ COMMAND cmake -E copy_if_different
+ ARGS ${src}
+ ${dest}
+ DEPENDS ${src}
+ COMMENT ${comment})
+endfunction (__java_copy_file src dest comment)
+
+# define helper scripts
+set(_JAVA_CLASS_FILELIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaClassFilelist.cmake)
+set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake)
+
+function(add_jar _TARGET_NAME)
+ set(_JAVA_SOURCE_FILES ${ARGN})
+
+ if (NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR)
+ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
+ endif(NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR)
+
+ if (CMAKE_JAVA_JAR_ENTRY_POINT)
+ set(_ENTRY_POINT_OPTION e)
+ set(_ENTRY_POINT_VALUE ${CMAKE_JAVA_JAR_ENTRY_POINT})
+ endif (CMAKE_JAVA_JAR_ENTRY_POINT)
+
+ if (LIBRARY_OUTPUT_PATH)
+ set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH})
+ else (LIBRARY_OUTPUT_PATH)
+ set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR})
+ endif (LIBRARY_OUTPUT_PATH)
+
+ set(CMAKE_JAVA_INCLUDE_PATH
+ ${CMAKE_JAVA_INCLUDE_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_JAVA_OBJECT_OUTPUT_PATH}
+ ${CMAKE_JAVA_LIBRARY_OUTPUT_PATH}
+ )
+
+ if (WIN32 AND NOT CYGWIN AND NOT CMAKE_CROSSCOMPILING)
+ set(CMAKE_JAVA_INCLUDE_FLAG_SEP ";")
+ else ()
+ set(CMAKE_JAVA_INCLUDE_FLAG_SEP ":")
+ endif()
+
+ foreach (JAVA_INCLUDE_DIR ${CMAKE_JAVA_INCLUDE_PATH})
+ set(CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_PATH_FINAL}${CMAKE_JAVA_INCLUDE_FLAG_SEP}${JAVA_INCLUDE_DIR}")
+ endforeach(JAVA_INCLUDE_DIR)
+
+ set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${CMAKE_JAVA_TARGET_OUTPUT_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir")
+
+ set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}.jar")
+ if (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION)
+ set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar")
+ set(_JAVA_TARGET_OUTPUT_LINK "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar")
+ elseif (CMAKE_JAVA_TARGET_VERSION)
+ set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar")
+ set(_JAVA_TARGET_OUTPUT_LINK "${_TARGET_NAME}.jar")
+ elseif (CMAKE_JAVA_TARGET_OUTPUT_NAME)
+ set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar")
+ endif (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION)
+ # reset
+ set(CMAKE_JAVA_TARGET_OUTPUT_NAME)
+
+ set(_JAVA_CLASS_FILES)
+ set(_JAVA_COMPILE_FILES)
+ set(_JAVA_DEPENDS)
+ set(_JAVA_RESOURCE_FILES)
+ foreach(_JAVA_SOURCE_FILE ${_JAVA_SOURCE_FILES})
+ get_filename_component(_JAVA_EXT ${_JAVA_SOURCE_FILE} EXT)
+ get_filename_component(_JAVA_FILE ${_JAVA_SOURCE_FILE} NAME_WE)
+ get_filename_component(_JAVA_PATH ${_JAVA_SOURCE_FILE} PATH)
+ get_filename_component(_JAVA_FULL ${_JAVA_SOURCE_FILE} ABSOLUTE)
+
+ file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR} ${_JAVA_FULL})
+ file(RELATIVE_PATH _JAVA_REL_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${_JAVA_FULL})
+ string(LENGTH ${_JAVA_REL_BINARY_PATH} _BIN_LEN)
+ string(LENGTH ${_JAVA_REL_SOURCE_PATH} _SRC_LEN)
+ if (${_BIN_LEN} LESS ${_SRC_LEN})
+ set(_JAVA_REL_PATH ${_JAVA_REL_BINARY_PATH})
+ else (${_BIN_LEN} LESS ${_SRC_LEN})
+ set(_JAVA_REL_PATH ${_JAVA_REL_SOURCE_PATH})
+ endif (${_BIN_LEN} LESS ${_SRC_LEN})
+ get_filename_component(_JAVA_REL_PATH ${_JAVA_REL_PATH} PATH)
+
+ if (_JAVA_EXT MATCHES ".java")
+ list(APPEND _JAVA_COMPILE_FILES ${_JAVA_SOURCE_FILE})
+ set(_JAVA_CLASS_FILE "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_REL_PATH}/${_JAVA_FILE}.class")
+ set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES} ${_JAVA_CLASS_FILE})
+
+ elseif (_JAVA_EXT MATCHES ".jar"
+ OR _JAVA_EXT MATCHES ".war"
+ OR _JAVA_EXT MATCHES ".ear"
+ OR _JAVA_EXT MATCHES ".sar")
+ list(APPEND CMAKE_JAVA_INCLUDE_PATH ${_JAVA_SOURCE_FILE})
+
+ elseif (_JAVA_EXT STREQUAL "")
+ list(APPEND CMAKE_JAVA_INCLUDE_PATH ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}} ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}_CLASSPATH})
+ list(APPEND _JAVA_DEPENDS ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}})
+
+ else (_JAVA_EXT MATCHES ".java")
+ __java_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/${_JAVA_SOURCE_FILE}
+ ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_SOURCE_FILE}
+ "Copying ${_JAVA_SOURCE_FILE} to the build directory")
+ list(APPEND _JAVA_RESOURCE_FILES ${_JAVA_SOURCE_FILE})
+ endif (_JAVA_EXT MATCHES ".java")
+ endforeach(_JAVA_SOURCE_FILE)
+
+ # create an empty java_class_filelist
+ if (NOT EXISTS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist)
+ file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist "")
+ endif()
+
+ if (_JAVA_COMPILE_FILES)
+ # Compile the java files and create a list of class files
+ add_custom_command(
+ # NOTE: this command generates an artificial dependency file
+ OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
+ COMMAND ${Java_JAVAC_EXECUTABLE}
+ ${CMAKE_JAVA_COMPILE_FLAGS}
+ -classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}"
+ -d ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ ${_JAVA_COMPILE_FILES}
+ COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
+ DEPENDS ${_JAVA_COMPILE_FILES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Building Java objects for ${_TARGET_NAME}.jar"
+ )
+ add_custom_command(
+ OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
+ COMMAND ${CMAKE_COMMAND}
+ -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ -DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}"
+ -P ${_JAVA_CLASS_FILELIST_SCRIPT}
+ DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ endif (_JAVA_COMPILE_FILES)
+
+ # create the jar file
+ set(_JAVA_JAR_OUTPUT_PATH
+ ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_NAME})
+ if (CMAKE_JNI_TARGET)
+ add_custom_command(
+ OUTPUT ${_JAVA_JAR_OUTPUT_PATH}
+ COMMAND ${Java_JAR_EXECUTABLE}
+ -cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE}
+ ${_JAVA_RESOURCE_FILES} @java_class_filelist
+ COMMAND ${CMAKE_COMMAND}
+ -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
+ -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME}
+ -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
+ -P ${_JAVA_SYMLINK_SCRIPT}
+ COMMAND ${CMAKE_COMMAND}
+ -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
+ -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_JAR_OUTPUT_PATH}
+ -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
+ -P ${_JAVA_SYMLINK_SCRIPT}
+ DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
+ WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}"
+ )
+ else ()
+ add_custom_command(
+ OUTPUT ${_JAVA_JAR_OUTPUT_PATH}
+ COMMAND ${Java_JAR_EXECUTABLE}
+ -cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE}
+ ${_JAVA_RESOURCE_FILES} @java_class_filelist
+ COMMAND ${CMAKE_COMMAND}
+ -D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
+ -D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME}
+ -D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
+ -P ${_JAVA_SYMLINK_SCRIPT}
+ WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
+ COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}"
+ )
+ endif (CMAKE_JNI_TARGET)
+
+ # Add the target and make sure we have the latest resource files.
+ add_custom_target(${_TARGET_NAME} ALL DEPENDS ${_JAVA_JAR_OUTPUT_PATH})
+
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ INSTALL_FILES
+ ${_JAVA_JAR_OUTPUT_PATH}
+ )
+
+ if (_JAVA_TARGET_OUTPUT_LINK)
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ INSTALL_FILES
+ ${_JAVA_JAR_OUTPUT_PATH}
+ ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK}
+ )
+
+ if (CMAKE_JNI_TARGET)
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ JNI_SYMLINK
+ ${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK}
+ )
+ endif (CMAKE_JNI_TARGET)
+ endif (_JAVA_TARGET_OUTPUT_LINK)
+
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ JAR_FILE
+ ${_JAVA_JAR_OUTPUT_PATH}
+ )
+
+ set_property(
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ CLASSDIR
+ ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
+ )
+
+endfunction(add_jar)
+
+function(INSTALL_JAR _TARGET_NAME _DESTINATION)
+ get_property(__FILES
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ INSTALL_FILES
+ )
+
+ if (__FILES)
+ install(
+ FILES
+ ${__FILES}
+ DESTINATION
+ ${_DESTINATION}
+ )
+ else (__FILES)
+ message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.")
+ endif (__FILES)
+endfunction(INSTALL_JAR _TARGET_NAME _DESTINATION)
+
+function(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION)
+ get_property(__SYMLINK
+ TARGET
+ ${_TARGET_NAME}
+ PROPERTY
+ JNI_SYMLINK
+ )
+
+ if (__SYMLINK)
+ install(
+ FILES
+ ${__SYMLINK}
+ DESTINATION
+ ${_DESTINATION}
+ )
+ else (__SYMLINK)
+ message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.")
+ endif (__SYMLINK)
+endfunction(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION)
+
+function (find_jar VARIABLE)
+ set(_jar_names)
+ set(_jar_files)
+ set(_jar_versions)
+ set(_jar_paths
+ /usr/share/java/
+ /usr/local/share/java/
+ ${Java_JAR_PATHS})
+ set(_jar_doc "NOTSET")
+
+ set(_state "name")
+
+ foreach (arg ${ARGN})
+ if (${_state} STREQUAL "name")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "NAMES")
+ set(_jar_names ${arg})
+ if (_jar_doc STREQUAL "NOTSET")
+ set(_jar_doc "Finding ${arg} jar")
+ endif (_jar_doc STREQUAL "NOTSET")
+ endif (${arg} STREQUAL "VERSIONS")
+ elseif (${_state} STREQUAL "versions")
+ if (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "NAMES")
+ set(_jar_versions ${_jar_versions} ${arg})
+ endif (${arg} STREQUAL "NAMES")
+ elseif (${_state} STREQUAL "names")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "VERSIONS")
+ set(_jar_names ${_jar_names} ${arg})
+ if (_jar_doc STREQUAL "NOTSET")
+ set(_jar_doc "Finding ${arg} jar")
+ endif (_jar_doc STREQUAL "NOTSET")
+ endif (${arg} STREQUAL "VERSIONS")
+ elseif (${_state} STREQUAL "paths")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "DOC")
+ set(_state "doc")
+ else (${arg} STREQUAL "VERSIONS")
+ set(_jar_paths ${_jar_paths} ${arg})
+ endif (${arg} STREQUAL "VERSIONS")
+ elseif (${_state} STREQUAL "doc")
+ if (${arg} STREQUAL "VERSIONS")
+ set(_state "versions")
+ elseif (${arg} STREQUAL "NAMES")
+ set(_state "names")
+ elseif (${arg} STREQUAL "PATHS")
+ set(_state "paths")
+ else (${arg} STREQUAL "VERSIONS")
+ set(_jar_doc ${arg})
+ endif (${arg} STREQUAL "VERSIONS")
+ endif (${_state} STREQUAL "name")
+ endforeach (arg ${ARGN})
+
+ if (NOT _jar_names)
+ message(FATAL_ERROR "find_jar: No name to search for given")
+ endif (NOT _jar_names)
+
+ foreach (jar_name ${_jar_names})
+ foreach (version ${_jar_versions})
+ set(_jar_files ${_jar_files} ${jar_name}-${version}.jar)
+ endforeach (version ${_jar_versions})
+ set(_jar_files ${_jar_files} ${jar_name}.jar)
+ endforeach (jar_name ${_jar_names})
+
+ find_file(${VARIABLE}
+ NAMES ${_jar_files}
+ PATHS ${_jar_paths}
+ DOC ${_jar_doc}
+ NO_DEFAULT_PATH)
+endfunction (find_jar VARIABLE)
+
+function(create_javadoc _target)
+ set(_javadoc_packages)
+ set(_javadoc_files)
+ set(_javadoc_sourcepath)
+ set(_javadoc_classpath)
+ set(_javadoc_installpath "${CMAKE_INSTALL_PREFIX}/share/javadoc")
+ set(_javadoc_doctitle)
+ set(_javadoc_windowtitle)
+ set(_javadoc_author FALSE)
+ set(_javadoc_version FALSE)
+ set(_javadoc_use FALSE)
+
+ set(_state "package")
+
+ foreach (arg ${ARGN})
+ if (${_state} STREQUAL "package")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_packages ${arg})
+ set(_state "packages")
+ endif ()
+ elseif (${_state} STREQUAL "packages")
+ if (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_packages ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "files")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_files ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "sourcepath")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_sourcepath ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "classpath")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ list(APPEND _javadoc_classpath ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "installpath")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_installpath ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "doctitle")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_doctitle ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "windowtitle")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_windowtitle ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "author")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_author ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "use")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_use ${arg})
+ endif ()
+ elseif (${_state} STREQUAL "version")
+ if (${arg} STREQUAL "PACKAGES")
+ set(_state "packages")
+ elseif (${arg} STREQUAL "FILES")
+ set(_state "files")
+ elseif (${arg} STREQUAL "SOURCEPATH")
+ set(_state "sourcepath")
+ elseif (${arg} STREQUAL "CLASSPATH")
+ set(_state "classpath")
+ elseif (${arg} STREQUAL "INSTALLPATH")
+ set(_state "installpath")
+ elseif (${arg} STREQUAL "DOCTITLE")
+ set(_state "doctitle")
+ elseif (${arg} STREQUAL "WINDOWTITLE")
+ set(_state "windowtitle")
+ elseif (${arg} STREQUAL "AUTHOR")
+ set(_state "author")
+ elseif (${arg} STREQUAL "USE")
+ set(_state "use")
+ elseif (${arg} STREQUAL "VERSION")
+ set(_state "version")
+ else ()
+ set(_javadoc_version ${arg})
+ endif ()
+ endif (${_state} STREQUAL "package")
+ endforeach (arg ${ARGN})
+
+ set(_javadoc_builddir ${CMAKE_CURRENT_BINARY_DIR}/javadoc/${_target})
+ set(_javadoc_options -d ${_javadoc_builddir})
+
+ if (_javadoc_sourcepath)
+ set(_start TRUE)
+ foreach(_path ${_javadoc_sourcepath})
+ if (_start)
+ set(_sourcepath ${_path})
+ set(_start FALSE)
+ else (_start)
+ set(_sourcepath ${_sourcepath}:${_path})
+ endif (_start)
+ endforeach(_path ${_javadoc_sourcepath})
+ set(_javadoc_options ${_javadoc_options} -sourcepath ${_sourcepath})
+ endif (_javadoc_sourcepath)
+
+ if (_javadoc_classpath)
+ set(_start TRUE)
+ foreach(_path ${_javadoc_classpath})
+ if (_start)
+ set(_classpath ${_path})
+ set(_start FALSE)
+ else (_start)
+ set(_classpath ${_classpath}:${_path})
+ endif (_start)
+ endforeach(_path ${_javadoc_classpath})
+ set(_javadoc_options ${_javadoc_options} -classpath "${_classpath}")
+ endif (_javadoc_classpath)
+
+ if (_javadoc_doctitle)
+ set(_javadoc_options ${_javadoc_options} -doctitle '${_javadoc_doctitle}')
+ endif (_javadoc_doctitle)
+
+ if (_javadoc_windowtitle)
+ set(_javadoc_options ${_javadoc_options} -windowtitle '${_javadoc_windowtitle}')
+ endif (_javadoc_windowtitle)
+
+ if (_javadoc_author)
+ set(_javadoc_options ${_javadoc_options} -author)
+ endif (_javadoc_author)
+
+ if (_javadoc_use)
+ set(_javadoc_options ${_javadoc_options} -use)
+ endif (_javadoc_use)
+
+ if (_javadoc_version)
+ set(_javadoc_options ${_javadoc_options} -version)
+ endif (_javadoc_version)
+
+ add_custom_target(${_target}_javadoc ALL
+ COMMAND ${Java_JAVADOC_EXECUTABLE} ${_javadoc_options}
+ ${_javadoc_files}
+ ${_javadoc_packages}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+ install(
+ DIRECTORY ${_javadoc_builddir}
+ DESTINATION ${_javadoc_installpath}
+ )
+endfunction(create_javadoc)
diff --git a/cmake/UseJavaClassFilelist.cmake b/cmake/UseJavaClassFilelist.cmake
new file mode 100644
index 00000000..c842bf71
--- /dev/null
+++ b/cmake/UseJavaClassFilelist.cmake
@@ -0,0 +1,52 @@
+#
+# This script create a list of compiled Java class files to be added to a
+# jar file. This avoids including cmake files which get created in the
+# binary directory.
+#
+
+#=============================================================================
+# Copyright 2010-2011 Andreas schneider <asn@redhat.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+if (CMAKE_JAVA_CLASS_OUTPUT_PATH)
+ if (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
+
+ set(_JAVA_GLOBBED_FILES)
+ if (CMAKE_JAR_CLASSES_PREFIX)
+ foreach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX})
+ message(STATUS "JAR_CLASS_PREFIX: ${JAR_CLASS_PREFIX}")
+
+ file(GLOB_RECURSE _JAVA_GLOBBED_TMP_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${JAR_CLASS_PREFIX}/*.class")
+ if (_JAVA_GLOBBED_TMP_FILES)
+ list(APPEND _JAVA_GLOBBED_FILES ${_JAVA_GLOBBED_TMP_FILES})
+ endif (_JAVA_GLOBBED_TMP_FILES)
+ endforeach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX})
+ else()
+ file(GLOB_RECURSE _JAVA_GLOBBED_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/*.class")
+ endif (CMAKE_JAR_CLASSES_PREFIX)
+
+ set(_JAVA_CLASS_FILES)
+ # file(GLOB_RECURSE foo RELATIVE) is broken so we need this.
+ foreach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES})
+ file(RELATIVE_PATH _JAVA_CLASS_FILE ${CMAKE_JAVA_CLASS_OUTPUT_PATH} ${_JAVA_GLOBBED_FILE})
+ set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES}${_JAVA_CLASS_FILE}\n)
+ endforeach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES})
+
+ # write to file
+ file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist ${_JAVA_CLASS_FILES})
+
+ else (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
+ message(SEND_ERROR "FATAL: Java class output path doesn't exist")
+ endif (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
+else (CMAKE_JAVA_CLASS_OUTPUT_PATH)
+ message(SEND_ERROR "FATAL: Can't find CMAKE_JAVA_CLASS_OUTPUT_PATH")
+endif (CMAKE_JAVA_CLASS_OUTPUT_PATH)
diff --git a/cmake/UseJavaSymlinks.cmake b/cmake/UseJavaSymlinks.cmake
new file mode 100644
index 00000000..c66ee1ea
--- /dev/null
+++ b/cmake/UseJavaSymlinks.cmake
@@ -0,0 +1,32 @@
+#
+# Helper script for UseJava.cmake
+#
+
+#=============================================================================
+# Copyright 2010-2011 Andreas schneider <asn@redhat.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+# License text for the above reference.)
+
+if (UNIX AND _JAVA_TARGET_OUTPUT_LINK)
+ if (_JAVA_TARGET_OUTPUT_NAME)
+ find_program(LN_EXECUTABLE
+ NAMES
+ ln
+ )
+
+ execute_process(
+ COMMAND ${LN_EXECUTABLE} -sf "${_JAVA_TARGET_OUTPUT_NAME}" "${_JAVA_TARGET_OUTPUT_LINK}"
+ WORKING_DIRECTORY ${_JAVA_TARGET_DIR}
+ )
+ else (_JAVA_TARGET_OUTPUT_NAME)
+ message(SEND_ERROR "FATAL: Can't find _JAVA_TARGET_OUTPUT_NAME")
+ endif (_JAVA_TARGET_OUTPUT_NAME)
+endif (UNIX AND _JAVA_TARGET_OUTPUT_LINK)
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 00000000..16e9f54e
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,40 @@
+#pragma once
+
+// Version information
+#define VERSION_MAJOR @MultiMC_VERSION_MAJOR@
+#define VERSION_MINOR @MultiMC_VERSION_MINOR@
+#define VERSION_HOTFIX @MultiMC_VERSION_HOTFIX@
+#define VERSION_BUILD @MultiMC_VERSION_BUILD@
+#define VERSION_TYPE @MultiMC_VERSION_TYPE@
+
+// The version channel. This is used by the updater to determine what channel the current version came from.
+#define VERSION_CHANNEL "@MultiMC_VERSION_CHANNEL@"
+
+// A short string identifying this build's platform. For example, "lin64" or "win32".
+#define BUILD_PLATFORM "@MultiMC_BUILD_PLATFORM@"
+
+// URL for the updater's channel
+#define CHANLIST_URL "@MultiMC_CHANLIST_URL@"
+
+// URL for notifications
+#define NOTIFICATION_URL "@MultiMC_NOTIFICATION_URL@"
+
+// Used for matching notifications
+#define FULL_VERSION_STR "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@"
+
+// enabled for updater dry run
+#cmakedefine MultiMC_UPDATER_DRY_RUN
+
+// enabled for updater dry run
+#cmakedefine MultiMC_UPDATER_FORCE_LOCAL
+
+// The commit hash of this build
+#define GIT_COMMIT "@MultiMC_GIT_COMMIT@"
+
+// This is printed on start to standard output
+#define VERSION_STR "@MultiMC_VERSION_STRING@"
+
+// This is used to fetch the news RSS feed.
+// It defaults in CMakeLists.txt to "http://multimc.org/rss.xml"
+#define NEWS_RSS_URL "@MultiMC_NEWS_RSS_URL@"
+
diff --git a/dependencies.cmake.in b/dependencies.cmake.in
new file mode 100644
index 00000000..9d4d1f63
--- /dev/null
+++ b/dependencies.cmake.in
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 2.8.9)
+
+message(STATUS "Running install script...")
+
+SET(Qt5_DIR @Qt5_DIR@)
+
+IF(WIN32)
+SET(LIB_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
+ELSE()
+SET(LIB_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/lib)
+ENDIF()
+
+INCLUDE(GetPrerequisites)
+GET_PREREQUISITES(@BINARY_LOCATION@ MULTIMC_PREREQS 1 1 "" "")
+
+message(STATUS "Prerequisites: ${MULTIMC_PREREQS}")
+
+FOREACH(PREREQ ${MULTIMC_PREREQS})
+ GET_FILENAME_COMPONENT(PREREQ_NAME "${PREREQ}" NAME)
+ GET_FILENAME_COMPONENT(PREREQ_ACTUAL "${PREREQ}" REALPATH)
+ IF(WIN32)
+ SET(PREREQ_ACTUAL "${Qt5_DIR}/bin/${PREREQ}")
+ ENDIF()
+
+ message(STATUS "Adding install prerequisite: ${PREREQ_NAME}")
+
+ FILE(INSTALL
+ DESTINATION "${LIB_INSTALL_PREFIX}"
+ TYPE PROGRAM
+ RENAME "${PREREQ_NAME}"
+ FILES "${PREREQ_ACTUAL}"
+ )
+ENDFOREACH()
diff --git a/depends/classparser/CMakeLists.txt b/depends/classparser/CMakeLists.txt
new file mode 100644
index 00000000..5a48e002
--- /dev/null
+++ b/depends/classparser/CMakeLists.txt
@@ -0,0 +1,41 @@
+project(classparser)
+
+set(CMAKE_AUTOMOC ON)
+
+# Find Qt
+find_package(Qt5Core REQUIRED)
+
+# Include Qt headers.
+include_directories(${Qt5Base_INCLUDE_DIRS})
+
+SET(CLASSPARSER_HEADERS
+include/classparser_config.h
+
+# Public headers
+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
+)
+
+# Set the include dir path.
+SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
+
+# Include self.
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
+include_directories(${CMAKE_BINARY_DIR}/include)
+
+add_definitions(-DCLASSPARSER_LIBRARY)
+
+add_library(classparser SHARED ${CLASSPARSER_SOURCES} ${CLASSPARSER_HEADERS})
+qt5_use_modules(classparser Core)
diff --git a/depends/classparser/include/classparser_config.h b/depends/classparser/include/classparser_config.h
new file mode 100644
index 00000000..a043824a
--- /dev/null
+++ b/depends/classparser/include/classparser_config.h
@@ -0,0 +1,22 @@
+/* Copyright 2013 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 <QtCore/QtGlobal>
+
+#ifdef CLASSPARSER_LIBRARY
+#define CLASSPARSER_EXPORT Q_DECL_EXPORT
+#else
+#define CLASSPARSER_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/depends/classparser/include/javautils.h b/depends/classparser/include/javautils.h
new file mode 100644
index 00000000..90042261
--- /dev/null
+++ b/depends/classparser/include/javautils.h
@@ -0,0 +1,29 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <QString>
+#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/depends/classparser/src/annotations.cpp b/depends/classparser/src/annotations.cpp
new file mode 100644
index 00000000..d1a7c046
--- /dev/null
+++ b/depends/classparser/src/annotations.cpp
@@ -0,0 +1,85 @@
+#include "classfile.h"
+#include "annotations.h"
+#include <sstream>
+
+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<uint16_t, element_value *> &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<element_value *> 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/depends/classparser/src/annotations.h b/depends/classparser/src/annotations.h
new file mode 100644
index 00000000..aa25d241
--- /dev/null
+++ b/depends/classparser/src/annotations.h
@@ -0,0 +1,277 @@
+#pragma once
+#include "classfile.h"
+#include <map>
+#include <vector>
+
+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<std::pair<uint16_t, element_value *>> 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 *> 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<element_value *> elem_vec;
+
+protected:
+ elem_vec values;
+
+public:
+ element_value_array(element_value_type type, std::vector<element_value *> &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/depends/classparser/src/classfile.h b/depends/classparser/src/classfile.h
new file mode 100644
index 00000000..a5e7ee50
--- /dev/null
+++ b/depends/classparser/src/classfile.h
@@ -0,0 +1,156 @@
+#pragma once
+#include "membuffer.h"
+#include "constants.h"
+#include "annotations.h"
+#include <map>
+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<uint16_t> interfaces;
+ // FIXME: doesn't free up memory on delete
+ java::annotation_table visible_class_annotations;
+};
+} \ No newline at end of file
diff --git a/depends/classparser/src/constants.h b/depends/classparser/src/constants.h
new file mode 100644
index 00000000..242b943e
--- /dev/null
+++ b/depends/classparser/src/constants.h
@@ -0,0 +1,220 @@
+#pragma once
+#include "errors.h"
+#include <sstream>
+
+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<java::constant> 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/depends/classparser/src/errors.h b/depends/classparser/src/errors.h
new file mode 100644
index 00000000..ddbbd828
--- /dev/null
+++ b/depends/classparser/src/errors.h
@@ -0,0 +1,8 @@
+#pragma once
+#include <exception>
+namespace java
+{
+class classfile_exception : public std::exception
+{
+};
+}
diff --git a/depends/classparser/src/javaendian.h b/depends/classparser/src/javaendian.h
new file mode 100644
index 00000000..d488b382
--- /dev/null
+++ b/depends/classparser/src/javaendian.h
@@ -0,0 +1,76 @@
+#pragma once
+#include <stdint.h>
+
+/**
+ * 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/depends/classparser/src/javautils.cpp b/depends/classparser/src/javautils.cpp
new file mode 100644
index 00000000..9f9ac263
--- /dev/null
+++ b/depends/classparser/src/javautils.cpp
@@ -0,0 +1,83 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <QFile>
+#include <quazipfile.h>
+
+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/depends/classparser/src/membuffer.h b/depends/classparser/src/membuffer.h
new file mode 100644
index 00000000..ab83412a
--- /dev/null
+++ b/depends/classparser/src/membuffer.h
@@ -0,0 +1,63 @@
+#pragma once
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include <exception>
+#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 <class T> 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 <class T> 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/depends/groupview/.clang-format b/depends/groupview/.clang-format
new file mode 100644
index 00000000..167a8fa7
--- /dev/null
+++ b/depends/groupview/.clang-format
@@ -0,0 +1,24 @@
+UseTab: true
+IndentWidth: 4
+TabWidth: 4
+ConstructorInitializerIndentWidth: 4
+AccessModifierOffset: -4
+IndentCaseLabels: false
+IndentFunctionDeclarationAfterType: false
+NamespaceIndentation: None
+
+BreakBeforeBraces: Allman
+AllowShortIfStatementsOnASingleLine: false
+ColumnLimit: 96
+MaxEmptyLinesToKeep: 1
+
+Standard: Cpp11
+Cpp11BracedListStyle: true
+
+SpacesInParentheses: false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpaceAfterControlStatementKeyword: true
+
+AlignTrailingComments: true
+SpacesBeforeTrailingComments: 1
diff --git a/depends/groupview/.gitignore b/depends/groupview/.gitignore
new file mode 100644
index 00000000..a5d18fa3
--- /dev/null
+++ b/depends/groupview/.gitignore
@@ -0,0 +1,2 @@
+build/
+*.user*
diff --git a/depends/groupview/CMakeLists.txt b/depends/groupview/CMakeLists.txt
new file mode 100644
index 00000000..e2a85950
--- /dev/null
+++ b/depends/groupview/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 2.8.9)
+
+project(GroupView)
+
+set(CMAKE_AUTOMOC ON)
+
+IF(APPLE)
+ message(STATUS "Using APPLE CMAKE_CXX_FLAGS")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
+ELSEIF(UNIX)
+ # assume GCC, add C++0x/C++11 stuff
+ MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
+ELSEIF(MINGW)
+ MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall")
+ENDIF()
+
+find_package(Qt5Core REQUIRED)
+find_package(Qt5Gui REQUIRED)
+find_package(Qt5Widgets REQUIRED)
+
+include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS})
+
+set(SOURCES
+ main.cpp
+ main.h
+
+ GroupView.h
+ GroupView.cpp
+ Group.h
+ Group.cpp
+ GroupedProxyModel.h
+ GroupedProxyModel.cpp
+ InstanceDelegate.h
+ InstanceDelegate.cpp
+)
+
+add_executable(GroupView ${SOURCES})
+qt5_use_modules(GroupView Core Gui Widgets)
diff --git a/Group.cpp b/depends/groupview/Group.cpp
index f216cc6e..f216cc6e 100644
--- a/Group.cpp
+++ b/depends/groupview/Group.cpp
diff --git a/Group.h b/depends/groupview/Group.h
index 455ee1a8..455ee1a8 100644
--- a/Group.h
+++ b/depends/groupview/Group.h
diff --git a/GroupView.cpp b/depends/groupview/GroupView.cpp
index 89e3e223..89e3e223 100644
--- a/GroupView.cpp
+++ b/depends/groupview/GroupView.cpp
diff --git a/GroupView.h b/depends/groupview/GroupView.h
index 329a3503..329a3503 100644
--- a/GroupView.h
+++ b/depends/groupview/GroupView.h
diff --git a/GroupedProxyModel.cpp b/depends/groupview/GroupedProxyModel.cpp
index 57d7ff5c..57d7ff5c 100644
--- a/GroupedProxyModel.cpp
+++ b/depends/groupview/GroupedProxyModel.cpp
diff --git a/GroupedProxyModel.h b/depends/groupview/GroupedProxyModel.h
index cae87ecd..cae87ecd 100644
--- a/GroupedProxyModel.h
+++ b/depends/groupview/GroupedProxyModel.h
diff --git a/InstanceDelegate.cpp b/depends/groupview/InstanceDelegate.cpp
index 8527e2bc..8527e2bc 100644
--- a/InstanceDelegate.cpp
+++ b/depends/groupview/InstanceDelegate.cpp
diff --git a/InstanceDelegate.h b/depends/groupview/InstanceDelegate.h
index de2f429b..de2f429b 100644
--- a/InstanceDelegate.h
+++ b/depends/groupview/InstanceDelegate.h
diff --git a/depends/groupview/main.cpp b/depends/groupview/main.cpp
new file mode 100644
index 00000000..df14e3bd
--- /dev/null
+++ b/depends/groupview/main.cpp
@@ -0,0 +1,98 @@
+#include "main.h"
+
+#include <QApplication>
+#include <QStandardItemModel>
+#include <QPainter>
+#include <QTime>
+
+#include "GroupView.h"
+#include "GroupedProxyModel.h"
+#include "InstanceDelegate.h"
+
+Progresser *progresser;
+
+QPixmap icon(const Qt::GlobalColor color)
+{
+ QPixmap p = QPixmap(32, 32);
+ p.fill(QColor(color));
+ return p;
+}
+QPixmap icon(const int number)
+{
+ QPixmap p = icon(Qt::white);
+ QPainter painter(&p);
+ QFont font = painter.font();
+ font.setBold(true);
+ font.setPixelSize(28);
+ painter.setFont(font);
+ painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter,
+ QString::number(number));
+ painter.end();
+ return p;
+}
+QStandardItem *createItem(const Qt::GlobalColor color, const QString &text,
+ const QString &category)
+{
+ QStandardItem *item = new QStandardItem;
+ item->setText(text);
+ item->setData(icon(color), Qt::DecorationRole);
+ item->setData(category, GroupViewRoles::GroupRole);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+ // progresser->addTrackedIndex(item);
+ return item;
+}
+QStandardItem *createItem(const int index, const QString &category)
+{
+ QStandardItem *item = new QStandardItem;
+ item->setText(QString("Item #%1").arg(index));
+ item->setData(icon(index), Qt::DecorationRole);
+ item->setData(category, GroupViewRoles::GroupRole);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+ // progresser->addTrackedIndex(item);
+ return item;
+}
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ qsrand(QTime::currentTime().msec());
+
+ progresser = new Progresser();
+
+ QStandardItemModel model;
+ model.setRowCount(10);
+ model.setColumnCount(1);
+
+ model.setItem(
+ 0, createItem(Qt::red,
+ "Red is a color. Some more text. I'm out of ideas. 42. What's your name?",
+ "Colorful"));
+ model.setItem(1, createItem(Qt::blue, "Blue", "Colorful"));
+ model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful"));
+
+ model.setItem(3, createItem(Qt::black, "Black", "Not Colorful"));
+ model.setItem(4, createItem(Qt::darkGray, "Dark Gray", "Not Colorful"));
+ model.setItem(5, createItem(Qt::gray, "Gray", "Not Colorful"));
+ model.setItem(6, createItem(Qt::lightGray, "Light Gray", "Not Colorful"));
+ model.setItem(7, createItem(Qt::white, "White", "Not Colorful"));
+
+ model.setItem(8, createItem(Qt::darkGreen, "Dark Green", ""));
+ model.setItem(9, progresser->addTrackedIndex(createItem(Qt::green, "Green", "")));
+
+ for (int i = 0; i < 20; ++i)
+ {
+ model.setItem(i + 10, createItem(i + 1, "Items 1-20"));
+ }
+
+ GroupedProxyModel pModel;
+ pModel.setSourceModel(&model);
+
+ GroupView w;
+ w.setItemDelegate(new ListViewDelegate);
+ w.setModel(&pModel);
+ w.resize(640, 480);
+ w.show();
+
+ return a.exec();
+}
diff --git a/main.h b/depends/groupview/main.h
index 2a358329..2a358329 100644
--- a/main.h
+++ b/depends/groupview/main.h
diff --git a/depends/javacheck/.gitignore b/depends/javacheck/.gitignore
new file mode 100644
index 00000000..cc1c52bf
--- /dev/null
+++ b/depends/javacheck/.gitignore
@@ -0,0 +1,6 @@
+.idea
+*.iml
+out
+.classpath
+.idea
+.project
diff --git a/depends/javacheck/CMakeLists.txt b/depends/javacheck/CMakeLists.txt
new file mode 100644
index 00000000..10b9a716
--- /dev/null
+++ b/depends/javacheck/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.6)
+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})
+
+INSTALL_JAR(JavaCheck "${BINARY_DEST_DIR}/jars")
diff --git a/depends/javacheck/JavaCheck.java b/depends/javacheck/JavaCheck.java
new file mode 100644
index 00000000..11420b86
--- /dev/null
+++ b/depends/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/depends/launcher/.gitignore b/depends/launcher/.gitignore
new file mode 100644
index 00000000..cc1c52bf
--- /dev/null
+++ b/depends/launcher/.gitignore
@@ -0,0 +1,6 @@
+.idea
+*.iml
+out
+.classpath
+.idea
+.project
diff --git a/depends/launcher/CMakeLists.txt b/depends/launcher/CMakeLists.txt
new file mode 100644
index 00000000..6af5f738
--- /dev/null
+++ b/depends/launcher/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 2.8.6)
+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/legacy/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})
+
+INSTALL_JAR(NewLaunch "${BINARY_DEST_DIR}/jars")
diff --git a/depends/launcher/net/minecraft/Launcher.java b/depends/launcher/net/minecraft/Launcher.java
new file mode 100644
index 00000000..c9b137e1
--- /dev/null
+++ b/depends/launcher/net/minecraft/Launcher.java
@@ -0,0 +1,154 @@
+/*
+ * 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;
+
+public class Launcher extends Applet implements AppletStub
+{
+ private Applet wrappedApplet;
+ private URL documentBase;
+ private boolean active = false;
+ private final Map<String, String> params;
+
+ public Launcher(Applet applet, URL documentBase)
+ {
+ params = new TreeMap<String, String>();
+
+ 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() {
+ return wrappedApplet.getCodeBase();
+ }
+
+ @Override
+ public URL getDocumentBase()
+ {
+ return documentBase;
+ }
+
+ @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/depends/launcher/org/multimc/EntryPoint.java b/depends/launcher/org/multimc/EntryPoint.java
new file mode 100644
index 00000000..e2721ffa
--- /dev/null
+++ b/depends/launcher/org/multimc/EntryPoint.java
@@ -0,0 +1,147 @@
+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
+ }
+
+ 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 != 2)
+ throw new ParseException();
+
+ String command = pair[0];
+ String param = pair[1];
+
+ if(command.equals("launch"))
+ {
+ if(param.equals("legacy"))
+ {
+ m_launcher = new LegacyLauncher();
+ Utils.log("Using legacy launcher.");
+ Utils.log();
+ return Action.Launch;
+ }
+ if(param.equals("onesix"))
+ {
+ m_launcher = new OneSixLauncher();
+ Utils.log("Using onesix launcher.");
+ Utils.log();
+ return Action.Launch;
+ }
+ 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;
+ // Main loop
+ while (isListening)
+ {
+ String inData;
+ try
+ {
+ // Read from the pipe one line at a time
+ inData = buffer.readLine();
+ if (inData != null)
+ {
+ if(parseLine(inData) == Action.Launch)
+ {
+ isListening = false;
+ }
+ }
+ }
+ 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(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/depends/launcher/org/multimc/IconLoader.java b/depends/launcher/org/multimc/IconLoader.java
new file mode 100644
index 00000000..f1638f3a
--- /dev/null
+++ b/depends/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/depends/launcher/org/multimc/Launcher.java b/depends/launcher/org/multimc/Launcher.java
new file mode 100644
index 00000000..1aa2b21f
--- /dev/null
+++ b/depends/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/depends/launcher/org/multimc/NotFoundException.java b/depends/launcher/org/multimc/NotFoundException.java
new file mode 100644
index 00000000..fe154a2f
--- /dev/null
+++ b/depends/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/depends/launcher/org/multimc/ParamBucket.java b/depends/launcher/org/multimc/ParamBucket.java
new file mode 100644
index 00000000..2e197d9f
--- /dev/null
+++ b/depends/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<String> coll = null;
+ if(!m_params.containsKey(key))
+ {
+ coll = new ArrayList<String>();
+ m_params.put(key, coll);
+ }
+ else
+ {
+ coll = m_params.get(key);
+ }
+ coll.add(value);
+ }
+
+ public List<String> all(String key) throws NotFoundException
+ {
+ if(!m_params.containsKey(key))
+ throw new NotFoundException();
+ return m_params.get(key);
+ }
+
+ public List<String> allSafe(String key, List<String> def)
+ {
+ if(!m_params.containsKey(key) || m_params.get(key).size() < 1)
+ {
+ return def;
+ }
+ return m_params.get(key);
+ }
+
+ public List<String> allSafe(String key)
+ {
+ return allSafe(key, new ArrayList<String>());
+ }
+
+ public String first(String key) throws NotFoundException
+ {
+ List<String> 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<String, List<String>> m_params = new HashMap<String, List<String>>();
+}
diff --git a/depends/launcher/org/multimc/ParseException.java b/depends/launcher/org/multimc/ParseException.java
new file mode 100644
index 00000000..d9e8e53e
--- /dev/null
+++ b/depends/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/depends/launcher/org/multimc/Utils.java b/depends/launcher/org/multimc/Utils.java
new file mode 100644
index 00000000..f3bac91d
--- /dev/null
+++ b/depends/launcher/org/multimc/Utils.java
@@ -0,0 +1,242 @@
+/*
+ * 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.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+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<String> 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<String> 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 MinecraftProcess::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();
+ }
+ }
+
+ /**
+ * Unzip zip file 'source' into the folder 'targetFolder'
+ * @param source
+ * @param targetFolder
+ * @throws IOException
+ */
+ public static void unzip(File source, File targetFolder) throws IOException
+ {
+ ZipFile zip = new ZipFile(source);
+ try
+ {
+ Enumeration entries = zip.entries();
+
+ while (entries.hasMoreElements())
+ {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+
+ File targetFile = new File(targetFolder, entry.getName());
+ 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/depends/launcher/org/multimc/legacy/LegacyFrame.java b/depends/launcher/org/multimc/legacy/LegacyFrame.java
new file mode 100644
index 00000000..c3c0cafc
--- /dev/null
+++ b/depends/launcher/org/multimc/legacy/LegacyFrame.java
@@ -0,0 +1,112 @@
+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 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/depends/launcher/org/multimc/legacy/LegacyLauncher.java b/depends/launcher/org/multimc/legacy/LegacyLauncher.java
new file mode 100644
index 00000000..1ca37c4a
--- /dev/null
+++ b/depends/launcher/org/multimc/legacy/LegacyLauncher.java
@@ -0,0 +1,178 @@
+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.Launcher;
+import org.multimc.NotFoundException;
+import org.multimc.ParamBucket;
+import org.multimc.Utils;
+
+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/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java
new file mode 100644
index 00000000..28f8e6ee
--- /dev/null
+++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java
@@ -0,0 +1,223 @@
+/* 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.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class OneSixLauncher implements Launcher
+{
+ @Override
+ public int launch(ParamBucket params)
+ {
+ // get and process the launch script params
+ List<String> libraries;
+ List<String> extlibs;
+ List<String> mcparams;
+ List<String> mods;
+ String mainClass;
+ String natives;
+ final String windowTitle;
+ String windowParams;
+ try
+ {
+ libraries = params.all("cp");
+ extlibs = params.all("ext");
+ mcparams = params.all("param");
+ mainClass = params.first("mainClass");
+ mods = params.allSafe("mods", new ArrayList<String>());
+ natives = params.first("natives");
+
+ windowTitle = params.first("windowTitle");
+ // windowParams = params.first("windowParams");
+ } catch (NotFoundException e)
+ {
+ System.err.println("Not enough arguments.");
+ e.printStackTrace(System.err);
+ return -1;
+ }
+
+ List<String> allJars = new ArrayList<String>();
+ allJars.addAll(mods);
+ allJars.addAll(libraries);
+
+ if(!Utils.addToClassPath(allJars))
+ {
+ System.err.println("Halting launch due to previous errors.");
+ return -1;
+ }
+
+ // print the pretty things
+ {
+ Utils.log("Main Class:");
+ Utils.log(" " + mainClass);
+ Utils.log();
+
+ Utils.log("Native path:");
+ Utils.log(" " + natives);
+ Utils.log();
+
+ Utils.log("Libraries:");
+ for (String s : libraries)
+ {
+ Utils.log(" " + s);
+ }
+ Utils.log();
+
+ if(mods.size() > 0)
+ {
+ Utils.log("Class Path Mods:");
+ for (String s : mods)
+ {
+ Utils.log(" " + s);
+ }
+ Utils.log();
+ }
+
+ Utils.log("Params:");
+ Utils.log(" " + mcparams.toString());
+ Utils.log();
+ }
+
+ // set up the natives path(s).
+ Utils.log("Preparing native libraries...");
+ String property = System.getProperty("os.arch");
+ boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
+ for(String extlib: extlibs)
+ {
+ try
+ {
+ String cleanlib = extlib.replace("${arch}", is_64 ? "64" : "32");
+ File cleanlibf = new File(cleanlib);
+ Utils.log("Extracting " + cleanlibf.getName());
+ Utils.unzip(cleanlibf, new File(natives));
+ } catch (IOException e)
+ {
+ System.err.println("Failed to extract native library:");
+ e.printStackTrace(System.err);
+ return -1;
+ }
+ }
+ Utils.log();
+
+ System.setProperty("java.library.path", natives);
+ Field fieldSysPath;
+ try
+ {
+ 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);
+ return -1;
+ }
+
+ // Get the Minecraft Class.
+ final ClassLoader cl = ClassLoader.getSystemClassLoader();
+ 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;
+ }
+
+ // FIXME: works only on linux, we need a better solution
+/*
+ final java.nio.ByteBuffer[] icons = IconLoader.load("icon.png");
+ new Thread() {
+ public void run() {
+ ClassLoader cl = ClassLoader.getSystemClassLoader();
+ try
+ {
+ Class<?> Display;
+ Method isCreated;
+ Method setTitle;
+ Method setIcon;
+
+ Display = cl.loadClass("org.lwjgl.opengl.Display");
+ isCreated = Display.getMethod("isCreated");
+ setTitle = Display.getMethod("setTitle", String.class);
+ setIcon = Display.getMethod("setIcon", java.nio.ByteBuffer[].class);
+
+ // set the window title? Maybe?
+ while(!(Boolean) isCreated.invoke(null))
+ {
+ try
+ {
+ Thread.sleep(150);
+ } catch (InterruptedException ignored) {}
+ }
+ // Give it a bit more time ;)
+ Thread.sleep(150);
+ // set the title
+ setTitle.invoke(null,windowTitle);
+ // only set icon when there's actually something to set...
+ if(icons.length > 0)
+ {
+ setIcon.invoke(null,(Object)icons);
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("Couldn't set window icon or title.");
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+ .start();
+*/
+ // start Minecraft
+ String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); // init params accordingly
+ try
+ {
+ meth.invoke(null, (Object) paramsArray); // static method doesn't have an instance
+ } catch (Exception e)
+ {
+ System.err.println("Failed to start Minecraft:");
+ e.printStackTrace(System.err);
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/depends/launcher/org/simplericity/macify/eawt/Application.java b/depends/launcher/org/simplericity/macify/eawt/Application.java
new file mode 100644
index 00000000..153bb9ee
--- /dev/null
+++ b/depends/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
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/index.html">
+ * Apple Java Extensions API
+ * </a>.
+ * 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
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#addAboutMenuItem()">
+ * Apple's API
+ * </a>.
+ */
+ void addAboutMenuItem();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#addApplicationListener(com.apple.eawt.ApplicationListener)">
+ * Apple's API
+ * </a>.
+ */
+ void addApplicationListener(ApplicationListener applicationListener);
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#addPreferencesMenuItem()">
+ * Apple's API
+ * </a>.
+ */
+ void addPreferencesMenuItem();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledAboutMenu()">
+ * Apple's API
+ * </a>.
+ */
+ boolean getEnabledAboutMenu();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledPreferencesMenu()">
+ * Apple's API
+ * </a>.
+ */
+ boolean getEnabledPreferencesMenu();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#isAboutMenuItemPresent()">
+ * Apple's API
+ * </a>.
+ */
+ boolean isAboutMenuItemPresent();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#isPreferencesMenuItemPresent()">
+ * Apple's API
+ * </a>.
+ */
+ boolean isPreferencesMenuItemPresent();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#removeAboutMenuItem()">
+ * Apple's API
+ * </a>.
+ */
+ void removeAboutMenuItem();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#removeApplicationListener(com.apple.eawt.ApplicationListener)">
+ * Apple's API
+ * </a>.
+ */
+ void removeApplicationListener(ApplicationListener applicationListener);
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#removePreferencesMenuItem()">
+ * Apple's API
+ * </a>.
+ */
+ void removePreferencesMenuItem();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledAboutMenu()">
+ * Apple's API
+ * </a>.
+ */
+ void setEnabledAboutMenu(boolean enabled);
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getEnabledPreferencesMenu()">
+ * Apple's API
+ * </a>.
+ */
+ void setEnabledPreferencesMenu(boolean enabled);
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/Application.html#getMouseLocationOnScreen()">
+ * Apple's API
+ * </a>.
+ */
+ Point getMouseLocationOnScreen();
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/index.html#//apple_ref/doc/uid/TP40004004">
+ * Apple's NSApplication Class Reference
+ * </a>.
+ * @param type on of {@link #REQUEST_USER_ATTENTION_TYPE_CRITICAL} or {@link #REQUEST_USER_ATTENTION_TYPE_INFORMATIONAL}.
+ */
+ int requestUserAttention(int type);
+
+ /**
+ * See
+ * <a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/index.html#//apple_ref/doc/uid/TP40004004">
+ * Apple's NSApplication Class Reference
+ * </a>
+ */
+ 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/depends/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java b/depends/launcher/org/simplericity/macify/eawt/ApplicationAdapter.java
new file mode 100644
index 00000000..e9c3db7d
--- /dev/null
+++ b/depends/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/depends/launcher/org/simplericity/macify/eawt/ApplicationEvent.java b/depends/launcher/org/simplericity/macify/eawt/ApplicationEvent.java
new file mode 100644
index 00000000..78420355
--- /dev/null
+++ b/depends/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/depends/launcher/org/simplericity/macify/eawt/ApplicationListener.java b/depends/launcher/org/simplericity/macify/eawt/ApplicationListener.java
new file mode 100644
index 00000000..a291bee4
--- /dev/null
+++ b/depends/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/depends/launcher/org/simplericity/macify/eawt/DefaultApplication.java b/depends/launcher/org/simplericity/macify/eawt/DefaultApplication.java
new file mode 100644
index 00000000..5752a350
--- /dev/null
+++ b/depends/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<Object, Object>());
+ 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/depends/pack200/CMakeLists.txt b/depends/pack200/CMakeLists.txt
new file mode 100644
index 00000000..386c8bb8
--- /dev/null
+++ b/depends/pack200/CMakeLists.txt
@@ -0,0 +1,59 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+IF(WIN32)
+ # In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
+ cmake_policy(SET CMP0020 OLD)
+ENDIF()
+
+project(unpack200)
+
+# Find ZLIB for quazip
+# Use system zlib on unix and Qt ZLIB on Windows
+IF(UNIX)
+ find_package(ZLIB REQUIRED)
+ELSE(UNIX)
+ get_filename_component (ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
+ SET(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
+ SET(ZLIB_LIBRARIES "")
+ IF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
+ MESSAGE("Please specify a valid zlib include dir")
+ ENDIF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
+ENDIF(UNIX)
+
+SET(PACK200_SRC
+include/unpack200.h
+src/bands.cpp
+src/bands.h
+src/bytes.cpp
+src/bytes.h
+src/coding.cpp
+src/coding.h
+src/constants.h
+src/defines.h
+src/unpack200.cpp
+src/unpack.cpp
+src/unpack.h
+src/utils.cpp
+src/utils.h
+src/zip.cpp
+src/zip.h
+)
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+SET(PACK200_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
+include_directories(
+ include
+ ${ZLIB_INCLUDE_DIRS}
+)
+add_library(unpack200 STATIC ${PACK200_SRC})
+
+IF(UNIX)
+ target_link_libraries(unpack200 ${ZLIB_LIBRARIES})
+ELSE()
+ # zlib is part of Qt on windows. use it.
+ QT5_USE_MODULES(unpack200 Core)
+ENDIF()
+
+add_executable(anti200 anti200.cpp)
+target_link_libraries(anti200 unpack200)
diff --git a/depends/pack200/LICENSE b/depends/pack200/LICENSE
new file mode 100644
index 00000000..b40a0f45
--- /dev/null
+++ b/depends/pack200/LICENSE
@@ -0,0 +1,347 @@
+The GNU General Public License (GPL)
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to
+most of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must
+make sure that they, too, receive or can get the source code. And you must
+show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program proprietary.
+To prevent this, we have made it clear that any patent must be licensed for
+everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included
+without limitation in the term "modification".) Each licensee is addressed as
+"you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may
+at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such modifications
+or work under the terms of Section 1 above, provided that you also meet all of
+these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole or
+ in part contains or is derived from the Program or any part thereof, to be
+ licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+ c) If the modified program normally reads commands interactively when run,
+ you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms
+of this License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on
+the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and
+2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above
+ on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only
+ for noncommercial distribution and only if you received the program in
+ object code or executable form with such an offer, in accord with
+ Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all
+the source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code
+distributed need not include anything that is normally distributed (in either
+source or binary form) with the major components (compiler, kernel, and so on)
+of the operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not
+accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to
+copy, distribute or modify the Program subject to these terms and conditions.
+You may not impose any further restrictions on the recipients' exercise of the
+rights granted herein. You are not responsible for enforcing compliance by
+third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In
+such case, this License incorporates the limitation as if written in the body
+of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems
+or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any later
+version", you have the option of following the terms and conditions either of
+that version or of any later version published by the Free Software Foundation.
+If the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
+ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
+PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+ One line to give the program's name and a brief idea of what it does.
+
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+ with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
+ software, and you are welcome to redistribute it under certain conditions;
+ type 'show c' for details.
+
+The hypothetical commands 'show w' and 'show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than 'show w' and 'show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary. Here
+is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ 'Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General Public
+License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL
+
+Certain source files distributed by Oracle America and/or its affiliates are
+subject to the following clarification and special exception to the GPL, but
+only where Oracle has expressly included in the particular source file's header
+the words "Oracle designates this particular file as subject to the "Classpath"
+exception as provided by Oracle in the LICENSE file that accompanied this code."
+
+ Linking this library statically or dynamically with other modules is making
+ a combined work based on this library. Thus, the terms and conditions of
+ the GNU General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent modules,
+ and to copy and distribute the resulting executable under terms of your
+ choice, provided that you also meet, for each linked independent module,
+ the terms and conditions of the license of that module. An independent
+ module is a module which is not derived from or based on this library. If
+ you modify this library, you may extend this exception to your version of
+ the library, but you are not obligated to do so. If you do not wish to do
+ so, delete this exception statement from your version.
diff --git a/depends/pack200/anti200.cpp b/depends/pack200/anti200.cpp
new file mode 100644
index 00000000..1e1ec0c8
--- /dev/null
+++ b/depends/pack200/anti200.cpp
@@ -0,0 +1,43 @@
+/*
+ * This is trivial. Do what thou wilt with it. Public domain.
+ */
+
+#include <stdexcept>
+#include <iostream>
+#include "unpack200.h"
+
+int main(int argc, char **argv)
+{
+ if (argc != 3)
+ {
+ std::cerr << "Simple pack200 unpacker!" << std::endl << "Run like this:" << std::endl
+ << " " << argv[0] << " input.jar.lzma output.jar" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ FILE *input = fopen(argv[1], "rb");
+ FILE *output = fopen(argv[2], "wb");
+ if (!input)
+ {
+ std::cerr << "Can't open input file";
+ return EXIT_FAILURE;
+ }
+ if (!output)
+ {
+ fclose(output);
+ std::cerr << "Can't open output file";
+ return EXIT_FAILURE;
+ }
+ try
+ {
+ unpack_200(input, output);
+ }
+ catch (std::runtime_error &e)
+ {
+ std::cerr << "Bad things happened: " << e.what() << std::endl;
+ fclose(input);
+ fclose(output);
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/depends/pack200/include/unpack200.h b/depends/pack200/include/unpack200.h
new file mode 100644
index 00000000..f9239488
--- /dev/null
+++ b/depends/pack200/include/unpack200.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#pragma once
+#include <string>
+
+/**
+ * @brief Unpack a PACK200 file
+ *
+ * @param input_path Path to the input file in PACK200 format. System native string encoding.
+ * @param output_path Path to the output file in PACK200 format. System native string encoding.
+ * @return void
+ * @throw std::runtime_error for any error encountered
+ */
+void unpack_200(FILE * input, FILE * output);
diff --git a/depends/pack200/src/bands.cpp b/depends/pack200/src/bands.cpp
new file mode 100644
index 00000000..1608d838
--- /dev/null
+++ b/depends/pack200/src/bands.cpp
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// -*- C++ -*-
+// Small program for unpacking specially compressed Java packages.
+// John R. Rose
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <stdint.h>
+
+#include "defines.h"
+#include "bytes.h"
+#include "utils.h"
+#include "coding.h"
+#include "bands.h"
+
+#include "constants.h"
+#include "unpack.h"
+
+void band::readData(int expectedLength)
+{
+ assert(expectedLength >= 0);
+ assert(vs[0].cmk == cmk_ERROR);
+ if (expectedLength != 0)
+ {
+ assert(length == 0);
+ length = expectedLength;
+ }
+ if (length == 0)
+ {
+ assert((rplimit = cm.vs0.rp = u->rp) != nullptr);
+ return;
+ }
+ assert(length > 0);
+
+ bool is_BYTE1 = (defc->spec == BYTE1_spec);
+
+ if (is_BYTE1)
+ {
+ // No possibility of coding change. Sizing is exact.
+ u->ensure_input(length);
+ }
+ else
+ {
+ // Make a conservatively generous estimate of band size in bytes.
+ // Assume B == 5 everywhere.
+ // Assume awkward pop with all {U} values (2*5 per value)
+ int64_t generous = (int64_t)length * (B_MAX * 3 + 1) + C_SLOP;
+ u->ensure_input(generous);
+ }
+
+ // Read one value to see what it might be.
+ int XB = _meta_default;
+ if (!is_BYTE1)
+ {
+ // must be a variable-length coding
+ assert(defc->B() > 1 && defc->L() > 0);
+
+ value_stream xvs;
+ coding *valc = defc;
+ if (valc->D() != 0)
+ {
+ valc = coding::findBySpec(defc->B(), defc->H(), defc->S());
+ assert(!valc->isMalloc);
+ }
+ xvs.init(u->rp, u->rplimit, valc);
+ int X = xvs.getInt();
+ if (valc->S() != 0)
+ {
+ assert(valc->min <= -256);
+ XB = -1 - X;
+ }
+ else
+ {
+ int L = valc->L();
+ assert(valc->max >= L + 255);
+ XB = X - L;
+ }
+ if (0 <= XB && XB < 256)
+ {
+ // Skip over the escape value.
+ u->rp = xvs.rp;
+ }
+ else
+ {
+ // No, it's still default.
+ XB = _meta_default;
+ }
+ }
+
+ if (XB <= _meta_canon_max)
+ {
+ byte XB_byte = (byte)XB;
+ byte *XB_ptr = &XB_byte;
+ cm.init(u->rp, u->rplimit, XB_ptr, 0, defc, length, nullptr);
+ }
+ else
+ {
+ assert(u->meta_rp != nullptr);
+ // Scribble the initial byte onto the band.
+ byte *save_meta_rp = --u->meta_rp;
+ byte save_meta_xb = (*save_meta_rp);
+ (*save_meta_rp) = (byte)XB;
+ cm.init(u->rp, u->rplimit, u->meta_rp, 0, defc, length, nullptr);
+ (*save_meta_rp) = save_meta_xb; // put it back, just to be tidy
+ }
+ rplimit = u->rp;
+
+ rewind();
+}
+
+void band::setIndex(cpindex *ix_)
+{
+ assert(ix_ == nullptr || ixTag == ix_->ixTag);
+ ix = ix_;
+}
+void band::setIndexByTag(byte tag)
+{
+ setIndex(u->cp.getIndex(tag));
+}
+
+entry *band::getRefCommon(cpindex *ix_, bool nullOKwithCaller)
+{
+ assert(ix_->ixTag == ixTag ||
+ (ixTag == CONSTANT_Literal && ix_->ixTag >= CONSTANT_Integer &&
+ ix_->ixTag <= CONSTANT_String));
+ int n = vs[0].getInt() - nullOK;
+ // Note: band-local nullOK means nullptr encodes as 0.
+ // But nullOKwithCaller means caller is willing to tolerate a nullptr.
+ entry *ref = ix_->get(n);
+ if (ref == nullptr && !(nullOKwithCaller && n == -1))
+ unpack_abort(n == -1 ? "nullptr ref" : "bad ref");
+ return ref;
+}
+
+int64_t band::getLong(band &lo_band, bool have_hi)
+{
+ band &hi_band = (*this);
+ assert(lo_band.bn == hi_band.bn + 1);
+ uint32_t lo = lo_band.getInt();
+ if (!have_hi)
+ {
+ assert(hi_band.length == 0);
+ return makeLong(0, lo);
+ }
+ uint32_t hi = hi_band.getInt();
+ return makeLong(hi, lo);
+}
+
+int band::getIntTotal()
+{
+ if (length == 0)
+ return 0;
+ if (total_memo > 0)
+ return total_memo - 1;
+ int total = getInt();
+ // overflow checks require that none of the addends are <0,
+ // and that the partial sums never overflow (wrap negative)
+ if (total < 0)
+ {
+ unpack_abort("overflow detected");
+ }
+ for (int k = length - 1; k > 0; k--)
+ {
+ int prev_total = total;
+ total += vs[0].getInt();
+ if (total < prev_total)
+ {
+ unpack_abort("overflow detected");
+ }
+ }
+ rewind();
+ total_memo = total + 1;
+ return total;
+}
+
+int band::getIntCount(int tag)
+{
+ if (length == 0)
+ return 0;
+ if (tag >= HIST0_MIN && tag <= HIST0_MAX)
+ {
+ if (hist0 == nullptr)
+ {
+ // Lazily calculate an approximate histogram.
+ hist0 = U_NEW(int, (HIST0_MAX - HIST0_MIN) + 1);
+ for (int k = length; k > 0; k--)
+ {
+ int x = vs[0].getInt();
+ if (x >= HIST0_MIN && x <= HIST0_MAX)
+ hist0[x - HIST0_MIN] += 1;
+ }
+ rewind();
+ }
+ return hist0[tag - HIST0_MIN];
+ }
+ int total = 0;
+ for (int k = length; k > 0; k--)
+ {
+ total += (vs[0].getInt() == tag) ? 1 : 0;
+ }
+ rewind();
+ return total;
+}
+
+#define INDEX_INIT(tag, nullOK, subindex) ((tag) + (subindex) * SUBINDEX_BIT + (nullOK) * 256)
+
+#define INDEX(tag) INDEX_INIT(tag, 0, 0)
+#define NULL_OR_INDEX(tag) INDEX_INIT(tag, 1, 0)
+#define SUB_INDEX(tag) INDEX_INIT(tag, 0, 1)
+#define NO_INDEX 0
+
+struct band_init
+{
+ int defc;
+ int index;
+};
+
+#define BAND_INIT(name, cspec, ix) \
+ { \
+ cspec, ix \
+ }
+
+const band_init all_band_inits[] =
+ {
+ // BAND_INIT(archive_magic, BYTE1_spec, 0),
+ // BAND_INIT(archive_header, UNSIGNED5_spec, 0),
+ // BAND_INIT(band_headers, BYTE1_spec, 0),
+ BAND_INIT(cp_Utf8_prefix, DELTA5_spec, 0), BAND_INIT(cp_Utf8_suffix, UNSIGNED5_spec, 0),
+ BAND_INIT(cp_Utf8_chars, CHAR3_spec, 0), BAND_INIT(cp_Utf8_big_suffix, DELTA5_spec, 0),
+ BAND_INIT(cp_Utf8_big_chars, DELTA5_spec, 0), BAND_INIT(cp_Int, UDELTA5_spec, 0),
+ BAND_INIT(cp_Float, UDELTA5_spec, 0), BAND_INIT(cp_Long_hi, UDELTA5_spec, 0),
+ BAND_INIT(cp_Long_lo, DELTA5_spec, 0), BAND_INIT(cp_Double_hi, UDELTA5_spec, 0),
+ BAND_INIT(cp_Double_lo, DELTA5_spec, 0),
+ BAND_INIT(cp_String, UDELTA5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(cp_Class, UDELTA5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(cp_Signature_form, DELTA5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(cp_Signature_classes, UDELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(cp_Descr_name, DELTA5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(cp_Descr_type, UDELTA5_spec, INDEX(CONSTANT_Signature)),
+ BAND_INIT(cp_Field_class, DELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(cp_Field_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)),
+ BAND_INIT(cp_Method_class, DELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(cp_Method_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)),
+ BAND_INIT(cp_Imethod_class, DELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(cp_Imethod_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)),
+ BAND_INIT(attr_definition_headers, BYTE1_spec, 0),
+ BAND_INIT(attr_definition_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(attr_definition_layout, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(ic_this_class, UDELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(ic_flags, UNSIGNED5_spec, 0),
+ BAND_INIT(ic_outer_class, DELTA5_spec, NULL_OR_INDEX(CONSTANT_Class)),
+ BAND_INIT(ic_name, DELTA5_spec, NULL_OR_INDEX(CONSTANT_Utf8)),
+ BAND_INIT(class_this, DELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(class_super, DELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(class_interface_count, DELTA5_spec, 0),
+ BAND_INIT(class_interface, DELTA5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(class_field_count, DELTA5_spec, 0),
+ BAND_INIT(class_method_count, DELTA5_spec, 0),
+ BAND_INIT(field_descr, DELTA5_spec, INDEX(CONSTANT_NameandType)),
+ BAND_INIT(field_flags_hi, UNSIGNED5_spec, 0),
+ BAND_INIT(field_flags_lo, UNSIGNED5_spec, 0),
+ BAND_INIT(field_attr_count, UNSIGNED5_spec, 0),
+ BAND_INIT(field_attr_indexes, UNSIGNED5_spec, 0),
+ BAND_INIT(field_attr_calls, UNSIGNED5_spec, 0),
+ BAND_INIT(field_ConstantValue_KQ, UNSIGNED5_spec, INDEX(CONSTANT_Literal)),
+ BAND_INIT(field_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)),
+ BAND_INIT(field_metadata_bands, -1, -1), BAND_INIT(field_attr_bands, -1, -1),
+ BAND_INIT(method_descr, MDELTA5_spec, INDEX(CONSTANT_NameandType)),
+ BAND_INIT(method_flags_hi, UNSIGNED5_spec, 0),
+ BAND_INIT(method_flags_lo, UNSIGNED5_spec, 0),
+ BAND_INIT(method_attr_count, UNSIGNED5_spec, 0),
+ BAND_INIT(method_attr_indexes, UNSIGNED5_spec, 0),
+ BAND_INIT(method_attr_calls, UNSIGNED5_spec, 0),
+ BAND_INIT(method_Exceptions_N, UNSIGNED5_spec, 0),
+ BAND_INIT(method_Exceptions_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(method_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)),
+ BAND_INIT(method_metadata_bands, -1, -1), BAND_INIT(method_attr_bands, -1, -1),
+ BAND_INIT(class_flags_hi, UNSIGNED5_spec, 0),
+ BAND_INIT(class_flags_lo, UNSIGNED5_spec, 0),
+ BAND_INIT(class_attr_count, UNSIGNED5_spec, 0),
+ BAND_INIT(class_attr_indexes, UNSIGNED5_spec, 0),
+ BAND_INIT(class_attr_calls, UNSIGNED5_spec, 0),
+ BAND_INIT(class_SourceFile_RUN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Utf8)),
+ BAND_INIT(class_EnclosingMethod_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(class_EnclosingMethod_RDN, UNSIGNED5_spec,
+ NULL_OR_INDEX(CONSTANT_NameandType)),
+ BAND_INIT(class_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)),
+ BAND_INIT(class_metadata_bands, -1, -1),
+ BAND_INIT(class_InnerClasses_N, UNSIGNED5_spec, 0),
+ BAND_INIT(class_InnerClasses_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(class_InnerClasses_F, UNSIGNED5_spec, 0),
+ BAND_INIT(class_InnerClasses_outer_RCN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)),
+ BAND_INIT(class_InnerClasses_name_RUN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Utf8)),
+ BAND_INIT(class_ClassFile_version_minor_H, UNSIGNED5_spec, 0),
+ BAND_INIT(class_ClassFile_version_major_H, UNSIGNED5_spec, 0),
+ BAND_INIT(class_attr_bands, -1, -1), BAND_INIT(code_headers, BYTE1_spec, 0),
+ BAND_INIT(code_max_stack, UNSIGNED5_spec, 0),
+ BAND_INIT(code_max_na_locals, UNSIGNED5_spec, 0),
+ BAND_INIT(code_handler_count, UNSIGNED5_spec, 0),
+ BAND_INIT(code_handler_start_P, BCI5_spec, 0),
+ BAND_INIT(code_handler_end_PO, BRANCH5_spec, 0),
+ BAND_INIT(code_handler_catch_PO, BRANCH5_spec, 0),
+ BAND_INIT(code_handler_class_RCN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)),
+ BAND_INIT(code_flags_hi, UNSIGNED5_spec, 0),
+ BAND_INIT(code_flags_lo, UNSIGNED5_spec, 0),
+ BAND_INIT(code_attr_count, UNSIGNED5_spec, 0),
+ BAND_INIT(code_attr_indexes, UNSIGNED5_spec, 0),
+ BAND_INIT(code_attr_calls, UNSIGNED5_spec, 0),
+ BAND_INIT(code_StackMapTable_N, UNSIGNED5_spec, 0),
+ BAND_INIT(code_StackMapTable_frame_T, BYTE1_spec, 0),
+ BAND_INIT(code_StackMapTable_local_N, UNSIGNED5_spec, 0),
+ BAND_INIT(code_StackMapTable_stack_N, UNSIGNED5_spec, 0),
+ BAND_INIT(code_StackMapTable_offset, UNSIGNED5_spec, 0),
+ BAND_INIT(code_StackMapTable_T, BYTE1_spec, 0),
+ BAND_INIT(code_StackMapTable_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)),
+ BAND_INIT(code_StackMapTable_P, BCI5_spec, 0),
+ BAND_INIT(code_LineNumberTable_N, UNSIGNED5_spec, 0),
+ BAND_INIT(code_LineNumberTable_bci_P, BCI5_spec, 0),
+ BAND_INIT(code_LineNumberTable_line, UNSIGNED5_spec, 0),
+ BAND_INIT(code_LocalVariableTable_N, UNSIGNED5_spec, 0),
+ BAND_INIT(code_LocalVariableTable_bci_P, BCI5_spec, 0),
+ BAND_INIT(code_LocalVariableTable_span_O, BRANCH5_spec, 0),
+ BAND_INIT(code_LocalVariableTable_name_RU, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(code_LocalVariableTable_type_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)),
+ BAND_INIT(code_LocalVariableTable_slot, UNSIGNED5_spec, 0),
+ BAND_INIT(code_LocalVariableTypeTable_N, UNSIGNED5_spec, 0),
+ BAND_INIT(code_LocalVariableTypeTable_bci_P, BCI5_spec, 0),
+ BAND_INIT(code_LocalVariableTypeTable_span_O, BRANCH5_spec, 0),
+ BAND_INIT(code_LocalVariableTypeTable_name_RU, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(code_LocalVariableTypeTable_type_RS, UNSIGNED5_spec,
+ INDEX(CONSTANT_Signature)),
+ BAND_INIT(code_LocalVariableTypeTable_slot, UNSIGNED5_spec, 0),
+ BAND_INIT(code_attr_bands, -1, -1), BAND_INIT(bc_codes, BYTE1_spec, 0),
+ BAND_INIT(bc_case_count, UNSIGNED5_spec, 0), BAND_INIT(bc_case_value, DELTA5_spec, 0),
+ BAND_INIT(bc_byte, BYTE1_spec, 0), BAND_INIT(bc_short, DELTA5_spec, 0),
+ BAND_INIT(bc_local, UNSIGNED5_spec, 0), BAND_INIT(bc_label, BRANCH5_spec, 0),
+ BAND_INIT(bc_intref, DELTA5_spec, INDEX(CONSTANT_Integer)),
+ BAND_INIT(bc_floatref, DELTA5_spec, INDEX(CONSTANT_Float)),
+ BAND_INIT(bc_longref, DELTA5_spec, INDEX(CONSTANT_Long)),
+ BAND_INIT(bc_doubleref, DELTA5_spec, INDEX(CONSTANT_Double)),
+ BAND_INIT(bc_stringref, DELTA5_spec, INDEX(CONSTANT_String)),
+ BAND_INIT(bc_classref, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)),
+ BAND_INIT(bc_fieldref, DELTA5_spec, INDEX(CONSTANT_Fieldref)),
+ BAND_INIT(bc_methodref, UNSIGNED5_spec, INDEX(CONSTANT_Methodref)),
+ BAND_INIT(bc_imethodref, DELTA5_spec, INDEX(CONSTANT_InterfaceMethodref)),
+ BAND_INIT(bc_thisfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)),
+ BAND_INIT(bc_superfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)),
+ BAND_INIT(bc_thismethod, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)),
+ BAND_INIT(bc_supermethod, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)),
+ BAND_INIT(bc_initref, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)),
+ BAND_INIT(bc_escref, UNSIGNED5_spec, INDEX(CONSTANT_All)),
+ BAND_INIT(bc_escrefsize, UNSIGNED5_spec, 0), BAND_INIT(bc_escsize, UNSIGNED5_spec, 0),
+ BAND_INIT(bc_escbyte, BYTE1_spec, 0),
+ BAND_INIT(file_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)),
+ BAND_INIT(file_size_hi, UNSIGNED5_spec, 0), BAND_INIT(file_size_lo, UNSIGNED5_spec, 0),
+ BAND_INIT(file_modtime, DELTA5_spec, 0), BAND_INIT(file_options, UNSIGNED5_spec, 0),
+ // BAND_INIT(file_bits, BYTE1_spec, 0),
+ {0, 0}};
+
+band *band::makeBands(unpacker *u)
+{
+ band *tmp_all_bands = U_NEW(band, BAND_LIMIT);
+ for (int i = 0; i < BAND_LIMIT; i++)
+ {
+ assert((byte *)&all_band_inits[i + 1] <
+ (byte *)all_band_inits + sizeof(all_band_inits));
+ const band_init &bi = all_band_inits[i];
+ band &b = tmp_all_bands[i];
+ coding *defc = coding::findBySpec(bi.defc);
+ assert((defc == nullptr) == (bi.defc == -1)); // no garbage, please
+ assert(defc == nullptr || !defc->isMalloc);
+ b.init(u, i, defc);
+ if (bi.index > 0)
+ {
+ b.nullOK = ((bi.index >> 8) & 1);
+ b.ixTag = (bi.index & 0xFF);
+ }
+ }
+ return tmp_all_bands;
+}
+
+void band::initIndexes(unpacker *u)
+{
+ band *tmp_all_bands = u->all_bands;
+ for (int i = 0; i < BAND_LIMIT; i++)
+ {
+ band *scan = &tmp_all_bands[i];
+ uint32_t tag = scan->ixTag; // Cf. #define INDEX(tag) above
+ if (tag != 0 && tag != CONSTANT_Literal && (tag & SUBINDEX_BIT) == 0)
+ {
+ scan->setIndex(u->cp.getIndex(tag));
+ }
+ }
+}
diff --git a/depends/pack200/src/bands.h b/depends/pack200/src/bands.h
new file mode 100644
index 00000000..a56cd7d5
--- /dev/null
+++ b/depends/pack200/src/bands.h
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// -*- C++ -*-
+struct entry;
+struct cpindex;
+struct unpacker;
+
+struct band
+{
+ int bn; // band_number of this band
+ coding *defc; // default coding method
+ cpindex *ix; // CP entry mapping, if CPRefBand
+ byte ixTag; // 0 or 1; nullptr is coded as (nullOK?0:-1)
+ byte nullOK; // 0 or 1; nullptr is coded as (nullOK?0:-1)
+ int length; // expected # values
+ unpacker *u; // back pointer
+
+ value_stream vs[2]; // source of values
+ coding_method cm; // method used for initial state of vs[0]
+ byte *rplimit; // end of band (encoded, transmitted)
+
+ int total_memo; // cached value of getIntTotal, or -1
+ int *hist0; // approximate. histogram
+ enum
+ {
+ HIST0_MIN = 0,
+ HIST0_MAX = 255
+ }; // catches the usual cases
+
+ // properties for attribute layout elements:
+ byte le_kind; // EK_XXX
+ byte le_bci; // 0,EK_BCI,EK_BCD,EK_BCO
+ byte le_back; // ==EF_BACK
+ byte le_len; // 0,1,2,4 (size in classfile), or call addr
+ band **le_body; // body of repl, union, call (nullptr-terminated)
+// Note: EK_CASE elements use hist0 to record union tags.
+#define le_casetags hist0
+
+ band &nextBand()
+ {
+ return this[1];
+ }
+ band &prevBand()
+ {
+ return this[-1];
+ }
+
+ void init(unpacker *u_, int bn_, coding *defc_)
+ {
+ u = u_;
+ cm.u = u_;
+ bn = bn_;
+ defc = defc_;
+ }
+ void init(unpacker *u_, int bn_, int defcSpec)
+ {
+ init(u_, bn_, coding::findBySpec(defcSpec));
+ }
+ void initRef(int ixTag_ = 0, bool nullOK_ = false)
+ {
+ ixTag = ixTag_;
+ nullOK = nullOK_;
+ setIndexByTag(ixTag);
+ }
+
+ void expectMoreLength(int l)
+ {
+ assert(length >= 0); // able to accept a length
+ assert((int)l >= 0); // no overflow
+ assert(rplimit == nullptr); // readData not yet called
+ length += l;
+ assert(length >= l); // no overflow
+ }
+
+ void setIndex(cpindex *ix_);
+ void setIndexByTag(byte tag);
+
+ // Parse the band and its meta-coding header.
+ void readData(int expectedLength = 0);
+
+ // Reset the band for another pass (Cf. Java Band.resetForSecondPass.)
+ void rewind()
+ {
+ cm.reset(&vs[0]);
+ }
+
+ byte *&curRP()
+ {
+ return vs[0].rp;
+ }
+ byte *minRP()
+ {
+ return cm.vs0.rp;
+ }
+ byte *maxRP()
+ {
+ return rplimit;
+ }
+ size_t size()
+ {
+ return maxRP() - minRP();
+ }
+
+ int getByte()
+ {
+ assert(ix == nullptr);
+ return vs[0].getByte();
+ }
+ int getInt()
+ {
+ assert(ix == nullptr);
+ return vs[0].getInt();
+ }
+ entry *getRefN()
+ {
+ assert(ix != nullptr);
+ return getRefCommon(ix, true);
+ }
+ entry *getRef()
+ {
+ assert(ix != nullptr);
+ return getRefCommon(ix, false);
+ }
+ entry *getRefUsing(cpindex *ix2)
+ {
+ assert(ix == nullptr);
+ return getRefCommon(ix2, true);
+ }
+ entry *getRefCommon(cpindex *ix, bool nullOK);
+ int64_t getLong(band &lo_band, bool have_hi);
+
+ static int64_t makeLong(uint32_t hi, uint32_t lo)
+ {
+ return ((uint64_t)hi << 32) + (((uint64_t)lo << 32) >> 32);
+ }
+
+ int getIntTotal();
+ int getIntCount(int tag);
+
+ static band *makeBands(unpacker *u);
+ static void initIndexes(unpacker *u);
+};
+
+extern band all_bands[];
+
+#define BAND_LOCAL /* \
+ band* band_temp = all_bands; \
+ band* all_bands = band_temp */
+
+// Band schema:
+enum band_number
+{
+ // e_archive_magic,
+ // e_archive_header,
+ // e_band_headers,
+
+ // constant pool contents
+ e_cp_Utf8_prefix,
+ e_cp_Utf8_suffix,
+ e_cp_Utf8_chars,
+ e_cp_Utf8_big_suffix,
+ e_cp_Utf8_big_chars,
+ e_cp_Int,
+ e_cp_Float,
+ e_cp_Long_hi,
+ e_cp_Long_lo,
+ e_cp_Double_hi,
+ e_cp_Double_lo,
+ e_cp_String,
+ e_cp_Class,
+ e_cp_Signature_form,
+ e_cp_Signature_classes,
+ e_cp_Descr_name,
+ e_cp_Descr_type,
+ e_cp_Field_class,
+ e_cp_Field_desc,
+ e_cp_Method_class,
+ e_cp_Method_desc,
+ e_cp_Imethod_class,
+ e_cp_Imethod_desc,
+
+ // bands which define transmission of attributes
+ e_attr_definition_headers,
+ e_attr_definition_name,
+ e_attr_definition_layout,
+
+ // band for hardwired InnerClasses attribute (shared across the package)
+ e_ic_this_class,
+ e_ic_flags,
+ // These bands contain data only where flags sets ACC_IC_LONG_FORM:
+ e_ic_outer_class,
+ e_ic_name,
+
+ // bands for carrying class schema information:
+ e_class_this,
+ e_class_super,
+ e_class_interface_count,
+ e_class_interface,
+
+ // bands for class members
+ e_class_field_count,
+ e_class_method_count,
+ e_field_descr,
+ e_field_flags_hi,
+ e_field_flags_lo,
+ e_field_attr_count,
+ e_field_attr_indexes,
+ e_field_attr_calls,
+ e_field_ConstantValue_KQ,
+ e_field_Signature_RS,
+ e_field_metadata_bands,
+ e_field_attr_bands,
+ e_method_descr,
+ e_method_flags_hi,
+ e_method_flags_lo,
+ e_method_attr_count,
+ e_method_attr_indexes,
+ e_method_attr_calls,
+ e_method_Exceptions_N,
+ e_method_Exceptions_RC,
+ e_method_Signature_RS,
+ e_method_metadata_bands,
+ e_method_attr_bands,
+ e_class_flags_hi,
+ e_class_flags_lo,
+ e_class_attr_count,
+ e_class_attr_indexes,
+ e_class_attr_calls,
+ e_class_SourceFile_RUN,
+ e_class_EnclosingMethod_RC,
+ e_class_EnclosingMethod_RDN,
+ e_class_Signature_RS,
+ e_class_metadata_bands,
+ e_class_InnerClasses_N,
+ e_class_InnerClasses_RC,
+ e_class_InnerClasses_F,
+ e_class_InnerClasses_outer_RCN,
+ e_class_InnerClasses_name_RUN,
+ e_class_ClassFile_version_minor_H,
+ e_class_ClassFile_version_major_H,
+ e_class_attr_bands,
+ e_code_headers,
+ e_code_max_stack,
+ e_code_max_na_locals,
+ e_code_handler_count,
+ e_code_handler_start_P,
+ e_code_handler_end_PO,
+ e_code_handler_catch_PO,
+ e_code_handler_class_RCN,
+
+ // code attributes
+ e_code_flags_hi,
+ e_code_flags_lo,
+ e_code_attr_count,
+ e_code_attr_indexes,
+ e_code_attr_calls,
+ e_code_StackMapTable_N,
+ e_code_StackMapTable_frame_T,
+ e_code_StackMapTable_local_N,
+ e_code_StackMapTable_stack_N,
+ e_code_StackMapTable_offset,
+ e_code_StackMapTable_T,
+ e_code_StackMapTable_RC,
+ e_code_StackMapTable_P,
+ e_code_LineNumberTable_N,
+ e_code_LineNumberTable_bci_P,
+ e_code_LineNumberTable_line,
+ e_code_LocalVariableTable_N,
+ e_code_LocalVariableTable_bci_P,
+ e_code_LocalVariableTable_span_O,
+ e_code_LocalVariableTable_name_RU,
+ e_code_LocalVariableTable_type_RS,
+ e_code_LocalVariableTable_slot,
+ e_code_LocalVariableTypeTable_N,
+ e_code_LocalVariableTypeTable_bci_P,
+ e_code_LocalVariableTypeTable_span_O,
+ e_code_LocalVariableTypeTable_name_RU,
+ e_code_LocalVariableTypeTable_type_RS,
+ e_code_LocalVariableTypeTable_slot,
+ e_code_attr_bands,
+
+ // bands for bytecodes
+ e_bc_codes,
+ // remaining bands provide typed opcode fields required by the bc_codes
+ e_bc_case_count,
+ e_bc_case_value,
+ e_bc_byte,
+ e_bc_short,
+ e_bc_local,
+ e_bc_label,
+
+ // ldc* operands:
+ e_bc_intref,
+ e_bc_floatref,
+ e_bc_longref,
+ e_bc_doubleref,
+ e_bc_stringref,
+ e_bc_classref,
+ e_bc_fieldref,
+ e_bc_methodref,
+ e_bc_imethodref,
+
+ // _self_linker_op family
+ e_bc_thisfield,
+ e_bc_superfield,
+ e_bc_thismethod,
+ e_bc_supermethod,
+
+ // bc_invokeinit family:
+ e_bc_initref,
+
+ // bytecode escape sequences
+ e_bc_escref,
+ e_bc_escrefsize,
+ e_bc_escsize,
+ e_bc_escbyte,
+
+ // file attributes and contents
+ e_file_name,
+ e_file_size_hi,
+ e_file_size_lo,
+ e_file_modtime,
+ e_file_options,
+ // e_file_bits, // handled specially as an appendix
+ BAND_LIMIT
+};
+
+// Symbolic names for bands, as if in a giant global struct:
+//#define archive_magic all_bands[e_archive_magic]
+//#define archive_header all_bands[e_archive_header]
+//#define band_headers all_bands[e_band_headers]
+#define cp_Utf8_prefix all_bands[e_cp_Utf8_prefix]
+#define cp_Utf8_suffix all_bands[e_cp_Utf8_suffix]
+#define cp_Utf8_chars all_bands[e_cp_Utf8_chars]
+#define cp_Utf8_big_suffix all_bands[e_cp_Utf8_big_suffix]
+#define cp_Utf8_big_chars all_bands[e_cp_Utf8_big_chars]
+#define cp_Int all_bands[e_cp_Int]
+#define cp_Float all_bands[e_cp_Float]
+#define cp_Long_hi all_bands[e_cp_Long_hi]
+#define cp_Long_lo all_bands[e_cp_Long_lo]
+#define cp_Double_hi all_bands[e_cp_Double_hi]
+#define cp_Double_lo all_bands[e_cp_Double_lo]
+#define cp_String all_bands[e_cp_String]
+#define cp_Class all_bands[e_cp_Class]
+#define cp_Signature_form all_bands[e_cp_Signature_form]
+#define cp_Signature_classes all_bands[e_cp_Signature_classes]
+#define cp_Descr_name all_bands[e_cp_Descr_name]
+#define cp_Descr_type all_bands[e_cp_Descr_type]
+#define cp_Field_class all_bands[e_cp_Field_class]
+#define cp_Field_desc all_bands[e_cp_Field_desc]
+#define cp_Method_class all_bands[e_cp_Method_class]
+#define cp_Method_desc all_bands[e_cp_Method_desc]
+#define cp_Imethod_class all_bands[e_cp_Imethod_class]
+#define cp_Imethod_desc all_bands[e_cp_Imethod_desc]
+#define attr_definition_headers all_bands[e_attr_definition_headers]
+#define attr_definition_name all_bands[e_attr_definition_name]
+#define attr_definition_layout all_bands[e_attr_definition_layout]
+#define ic_this_class all_bands[e_ic_this_class]
+#define ic_flags all_bands[e_ic_flags]
+#define ic_outer_class all_bands[e_ic_outer_class]
+#define ic_name all_bands[e_ic_name]
+#define class_this all_bands[e_class_this]
+#define class_super all_bands[e_class_super]
+#define class_interface_count all_bands[e_class_interface_count]
+#define class_interface all_bands[e_class_interface]
+#define class_field_count all_bands[e_class_field_count]
+#define class_method_count all_bands[e_class_method_count]
+#define field_descr all_bands[e_field_descr]
+#define field_flags_hi all_bands[e_field_flags_hi]
+#define field_flags_lo all_bands[e_field_flags_lo]
+#define field_attr_count all_bands[e_field_attr_count]
+#define field_attr_indexes all_bands[e_field_attr_indexes]
+#define field_ConstantValue_KQ all_bands[e_field_ConstantValue_KQ]
+#define field_Signature_RS all_bands[e_field_Signature_RS]
+#define field_attr_bands all_bands[e_field_attr_bands]
+#define method_descr all_bands[e_method_descr]
+#define method_flags_hi all_bands[e_method_flags_hi]
+#define method_flags_lo all_bands[e_method_flags_lo]
+#define method_attr_count all_bands[e_method_attr_count]
+#define method_attr_indexes all_bands[e_method_attr_indexes]
+#define method_Exceptions_N all_bands[e_method_Exceptions_N]
+#define method_Exceptions_RC all_bands[e_method_Exceptions_RC]
+#define method_Signature_RS all_bands[e_method_Signature_RS]
+#define method_attr_bands all_bands[e_method_attr_bands]
+#define class_flags_hi all_bands[e_class_flags_hi]
+#define class_flags_lo all_bands[e_class_flags_lo]
+#define class_attr_count all_bands[e_class_attr_count]
+#define class_attr_indexes all_bands[e_class_attr_indexes]
+#define class_SourceFile_RUN all_bands[e_class_SourceFile_RUN]
+#define class_EnclosingMethod_RC all_bands[e_class_EnclosingMethod_RC]
+#define class_EnclosingMethod_RDN all_bands[e_class_EnclosingMethod_RDN]
+#define class_Signature_RS all_bands[e_class_Signature_RS]
+#define class_InnerClasses_N all_bands[e_class_InnerClasses_N]
+#define class_InnerClasses_RC all_bands[e_class_InnerClasses_RC]
+#define class_InnerClasses_F all_bands[e_class_InnerClasses_F]
+#define class_InnerClasses_outer_RCN all_bands[e_class_InnerClasses_outer_RCN]
+#define class_InnerClasses_name_RUN all_bands[e_class_InnerClasses_name_RUN]
+#define class_ClassFile_version_minor_H all_bands[e_class_ClassFile_version_minor_H]
+#define class_ClassFile_version_major_H all_bands[e_class_ClassFile_version_major_H]
+#define class_attr_bands all_bands[e_class_attr_bands]
+#define code_headers all_bands[e_code_headers]
+#define code_max_stack all_bands[e_code_max_stack]
+#define code_max_na_locals all_bands[e_code_max_na_locals]
+#define code_handler_count all_bands[e_code_handler_count]
+#define code_handler_start_P all_bands[e_code_handler_start_P]
+#define code_handler_end_PO all_bands[e_code_handler_end_PO]
+#define code_handler_catch_PO all_bands[e_code_handler_catch_PO]
+#define code_handler_class_RCN all_bands[e_code_handler_class_RCN]
+#define code_flags_hi all_bands[e_code_flags_hi]
+#define code_flags_lo all_bands[e_code_flags_lo]
+#define code_attr_count all_bands[e_code_attr_count]
+#define code_attr_indexes all_bands[e_code_attr_indexes]
+#define code_StackMapTable_N all_bands[e_code_StackMapTable_N]
+#define code_StackMapTable_frame_T all_bands[e_code_StackMapTable_frame_T]
+#define code_StackMapTable_local_N all_bands[e_code_StackMapTable_local_N]
+#define code_StackMapTable_stack_N all_bands[e_code_StackMapTable_stack_N]
+#define code_StackMapTable_offset all_bands[e_code_StackMapTable_offset]
+#define code_StackMapTable_T all_bands[e_code_StackMapTable_T]
+#define code_StackMapTable_RC all_bands[e_code_StackMapTable_RC]
+#define code_StackMapTable_P all_bands[e_code_StackMapTable_P]
+#define code_LineNumberTable_N all_bands[e_code_LineNumberTable_N]
+#define code_LineNumberTable_bci_P all_bands[e_code_LineNumberTable_bci_P]
+#define code_LineNumberTable_line all_bands[e_code_LineNumberTable_line]
+#define code_LocalVariableTable_N all_bands[e_code_LocalVariableTable_N]
+#define code_LocalVariableTable_bci_P all_bands[e_code_LocalVariableTable_bci_P]
+#define code_LocalVariableTable_span_O all_bands[e_code_LocalVariableTable_span_O]
+#define code_LocalVariableTable_name_RU all_bands[e_code_LocalVariableTable_name_RU]
+#define code_LocalVariableTable_type_RS all_bands[e_code_LocalVariableTable_type_RS]
+#define code_LocalVariableTable_slot all_bands[e_code_LocalVariableTable_slot]
+#define code_LocalVariableTypeTable_N all_bands[e_code_LocalVariableTypeTable_N]
+#define code_LocalVariableTypeTable_bci_P all_bands[e_code_LocalVariableTypeTable_bci_P]
+#define code_LocalVariableTypeTable_span_O all_bands[e_code_LocalVariableTypeTable_span_O]
+#define code_LocalVariableTypeTable_name_RU all_bands[e_code_LocalVariableTypeTable_name_RU]
+#define code_LocalVariableTypeTable_type_RS all_bands[e_code_LocalVariableTypeTable_type_RS]
+#define code_LocalVariableTypeTable_slot all_bands[e_code_LocalVariableTypeTable_slot]
+#define code_attr_bands all_bands[e_code_attr_bands]
+#define bc_codes all_bands[e_bc_codes]
+#define bc_case_count all_bands[e_bc_case_count]
+#define bc_case_value all_bands[e_bc_case_value]
+#define bc_byte all_bands[e_bc_byte]
+#define bc_short all_bands[e_bc_short]
+#define bc_local all_bands[e_bc_local]
+#define bc_label all_bands[e_bc_label]
+#define bc_intref all_bands[e_bc_intref]
+#define bc_floatref all_bands[e_bc_floatref]
+#define bc_longref all_bands[e_bc_longref]
+#define bc_doubleref all_bands[e_bc_doubleref]
+#define bc_stringref all_bands[e_bc_stringref]
+#define bc_classref all_bands[e_bc_classref]
+#define bc_fieldref all_bands[e_bc_fieldref]
+#define bc_methodref all_bands[e_bc_methodref]
+#define bc_imethodref all_bands[e_bc_imethodref]
+#define bc_thisfield all_bands[e_bc_thisfield]
+#define bc_superfield all_bands[e_bc_superfield]
+#define bc_thismethod all_bands[e_bc_thismethod]
+#define bc_supermethod all_bands[e_bc_supermethod]
+#define bc_initref all_bands[e_bc_initref]
+#define bc_escref all_bands[e_bc_escref]
+#define bc_escrefsize all_bands[e_bc_escrefsize]
+#define bc_escsize all_bands[e_bc_escsize]
+#define bc_escbyte all_bands[e_bc_escbyte]
+#define file_name all_bands[e_file_name]
+#define file_size_hi all_bands[e_file_size_hi]
+#define file_size_lo all_bands[e_file_size_lo]
+#define file_modtime all_bands[e_file_modtime]
+#define file_options all_bands[e_file_options]
diff --git a/depends/pack200/src/bytes.cpp b/depends/pack200/src/bytes.cpp
new file mode 100644
index 00000000..d3808afa
--- /dev/null
+++ b/depends/pack200/src/bytes.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdint.h>
+#include "defines.h"
+#include "bytes.h"
+#include "utils.h"
+
+static byte dummy[1 << 10];
+
+bool bytes::inBounds(const void *p)
+{
+ return p >= ptr && p < limit();
+}
+
+void bytes::malloc(size_t len_)
+{
+ len = len_;
+ ptr = NEW(byte, add_size(len_, 1)); // add trailing zero byte always
+ if (ptr == nullptr)
+ {
+ // set ptr to some victim memory, to ease escape
+ set(dummy, sizeof(dummy) - 1);
+ unpack_abort(ERROR_ENOMEM);
+ }
+}
+
+void bytes::realloc(size_t len_)
+{
+ if (len == len_)
+ return; // nothing to do
+ if (ptr == dummy)
+ return; // escaping from an error
+ if (ptr == nullptr)
+ {
+ malloc(len_);
+ return;
+ }
+ byte *oldptr = ptr;
+ ptr = (len_ >= PSIZE_MAX) ? nullptr : (byte *)::realloc(ptr, add_size(len_, 1));
+ if (ptr != nullptr)
+ {
+ if (len < len_)
+ memset(ptr + len, 0, len_ - len);
+ ptr[len_] = 0;
+ len = len_;
+ }
+ else
+ {
+ ptr = oldptr; // ease our escape
+ unpack_abort(ERROR_ENOMEM);
+ }
+}
+
+void bytes::free()
+{
+ if (ptr == dummy)
+ return; // escaping from an error
+ if (ptr != nullptr)
+ {
+ ::free(ptr);
+ }
+ len = 0;
+ ptr = 0;
+}
+
+int bytes::indexOf(byte c)
+{
+ byte *p = (byte *)memchr(ptr, c, len);
+ return (p == 0) ? -1 : (int)(p - ptr);
+}
+
+byte *bytes::writeTo(byte *bp)
+{
+ memcpy(bp, ptr, len);
+ return bp + len;
+}
+
+int bytes::compareTo(bytes &other)
+{
+ size_t l1 = len;
+ size_t l2 = other.len;
+ int cmp = memcmp(ptr, other.ptr, (l1 < l2) ? l1 : l2);
+ if (cmp != 0)
+ return cmp;
+ return (l1 < l2) ? -1 : (l1 > l2) ? 1 : 0;
+}
+
+void bytes::saveFrom(const void *ptr_, size_t len_)
+{
+ malloc(len_);
+ // Save as much as possible.
+ if (len_ > len)
+ {
+ assert(ptr == dummy); // error recovery
+ len_ = len;
+ }
+ copyFrom(ptr_, len_);
+}
+
+//#TODO: Need to fix for exception handling
+void bytes::copyFrom(const void *ptr_, size_t len_, size_t offset)
+{
+ assert(len_ == 0 || inBounds(ptr + offset));
+ assert(len_ == 0 || inBounds(ptr + offset + len_ - 1));
+ memcpy(ptr + offset, ptr_, len_);
+}
+
+// Make sure there are 'o' bytes beyond the fill pointer,
+// advance the fill pointer, and return the old fill pointer.
+byte *fillbytes::grow(size_t s)
+{
+ size_t nlen = add_size(b.len, s);
+ if (nlen <= allocated)
+ {
+ b.len = nlen;
+ return limit() - s;
+ }
+ size_t maxlen = nlen;
+ if (maxlen < 128)
+ maxlen = 128;
+ if (maxlen < allocated * 2)
+ maxlen = allocated * 2;
+ if (allocated == 0)
+ {
+ // Initial buffer was not malloced. Do not reallocate it.
+ bytes old = b;
+ b.malloc(maxlen);
+ if (b.len == maxlen)
+ old.writeTo(b.ptr);
+ }
+ else
+ {
+ b.realloc(maxlen);
+ }
+ allocated = b.len;
+ if (allocated != maxlen)
+ {
+ b.len = nlen - s; // back up
+ return dummy; // scribble during error recov.
+ }
+ // after realloc, recompute pointers
+ b.len = nlen;
+ assert(b.len <= allocated);
+ return limit() - s;
+}
+
+void fillbytes::ensureSize(size_t s)
+{
+ if (allocated >= s)
+ return;
+ size_t len0 = b.len;
+ grow(s - size());
+ b.len = len0; // put it back
+}
+
+int ptrlist::indexOf(const void *x)
+{
+ int len = length();
+ for (int i = 0; i < len; i++)
+ {
+ if (get(i) == x)
+ return i;
+ }
+ return -1;
+}
+
+void ptrlist::freeAll()
+{
+ int len = length();
+ for (int i = 0; i < len; i++)
+ {
+ void *p = (void *)get(i);
+ if (p != nullptr)
+ {
+ ::free(p);
+ }
+ }
+ free();
+}
+
+int intlist::indexOf(int x)
+{
+ int len = length();
+ for (int i = 0; i < len; i++)
+ {
+ if (get(i) == x)
+ return i;
+ }
+ return -1;
+}
diff --git a/depends/pack200/src/bytes.h b/depends/pack200/src/bytes.h
new file mode 100644
index 00000000..6ed0b729
--- /dev/null
+++ b/depends/pack200/src/bytes.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+struct bytes
+{
+ int8_t *ptr;
+ size_t len;
+ int8_t *limit()
+ {
+ return ptr + len;
+ }
+
+ void set(int8_t *ptr_, size_t len_)
+ {
+ ptr = ptr_;
+ len = len_;
+ }
+ void set(const char *str)
+ {
+ ptr = (int8_t *)str;
+ len = strlen(str);
+ }
+ bool inBounds(const void *p); // p in [ptr, limit)
+ void malloc(size_t len_);
+ void realloc(size_t len_);
+ void free();
+ void copyFrom(const void *ptr_, size_t len_, size_t offset = 0);
+ void saveFrom(const void *ptr_, size_t len_);
+ void saveFrom(const char *str)
+ {
+ saveFrom(str, strlen(str));
+ }
+ void copyFrom(bytes &other, size_t offset = 0)
+ {
+ copyFrom(other.ptr, other.len, offset);
+ }
+ void saveFrom(bytes &other)
+ {
+ saveFrom(other.ptr, other.len);
+ }
+ void clear(int fill_byte = 0)
+ {
+ memset(ptr, fill_byte, len);
+ }
+ int8_t *writeTo(int8_t *bp);
+ bool equals(bytes &other)
+ {
+ return 0 == compareTo(other);
+ }
+ int compareTo(bytes &other);
+ bool contains(int8_t c)
+ {
+ return indexOf(c) >= 0;
+ }
+ int indexOf(int8_t c);
+ // substrings:
+ static bytes of(int8_t *ptr, size_t len)
+ {
+ bytes res;
+ res.set(ptr, len);
+ return res;
+ }
+ bytes slice(size_t beg, size_t end)
+ {
+ bytes res;
+ res.ptr = ptr + beg;
+ res.len = end - beg;
+ assert(res.len == 0 ||(inBounds(res.ptr) && inBounds(res.limit() - 1)));
+ return res;
+ }
+ // building C strings inside byte buffers:
+ bytes &strcat(const char *str)
+ {
+ ::strcat((char *)ptr, str);
+ return *this;
+ }
+ bytes &strcat(bytes &other)
+ {
+ ::strncat((char *)ptr, (char *)other.ptr, other.len);
+ return *this;
+ }
+ char *strval()
+ {
+ assert(strlen((char *)ptr) == len);
+ return (char *)ptr;
+ }
+};
+#define BYTES_OF(var) (bytes::of((int8_t *)&(var), sizeof(var)))
+
+struct fillbytes
+{
+ bytes b;
+ size_t allocated;
+
+ int8_t *base()
+ {
+ return b.ptr;
+ }
+ size_t size()
+ {
+ return b.len;
+ }
+ int8_t *limit()
+ {
+ return b.limit();
+ } // logical limit
+ void setLimit(int8_t *lp)
+ {
+ assert(isAllocated(lp));
+ b.len = lp - b.ptr;
+ }
+ int8_t *end()
+ {
+ return b.ptr + allocated;
+ } // physical limit
+ int8_t *loc(size_t o)
+ {
+ assert(o < b.len);
+ return b.ptr + o;
+ }
+ void init()
+ {
+ allocated = 0;
+ b.set(nullptr, 0);
+ }
+ void init(size_t s)
+ {
+ init();
+ ensureSize(s);
+ }
+ void free()
+ {
+ if (allocated != 0)
+ b.free();
+ allocated = 0;
+ }
+ void empty()
+ {
+ b.len = 0;
+ }
+ int8_t *grow(size_t s); // grow so that limit() += s
+ int getByte(uint32_t i)
+ {
+ return *loc(i) & 0xFF;
+ }
+ void addByte(int8_t x)
+ {
+ *grow(1) = x;
+ }
+ void ensureSize(size_t s); // make sure allocated >= s
+ void trimToSize()
+ {
+ if (allocated > size())
+ b.realloc(allocated = size());
+ }
+ bool canAppend(size_t s)
+ {
+ return allocated > b.len + s;
+ }
+ bool isAllocated(int8_t *p)
+ {
+ return p >= base() && p <= end();
+ } // asserts
+ void set(bytes &src)
+ {
+ set(src.ptr, src.len);
+ }
+
+ void set(int8_t *ptr, size_t len)
+ {
+ b.set(ptr, len);
+ allocated = 0; // mark as not reallocatable
+ }
+
+ // block operations on resizing byte buffer:
+ fillbytes &append(const void *ptr_, size_t len_)
+ {
+ memcpy(grow(len_), ptr_, len_);
+ return (*this);
+ }
+ fillbytes &append(bytes &other)
+ {
+ return append(other.ptr, other.len);
+ }
+ fillbytes &append(const char *str)
+ {
+ return append(str, strlen(str));
+ }
+};
+
+struct ptrlist : fillbytes
+{
+ typedef const void *cvptr;
+ int length()
+ {
+ return (int)(size() / sizeof(cvptr));
+ }
+ cvptr *base()
+ {
+ return (cvptr *)fillbytes::base();
+ }
+ cvptr &get(int i)
+ {
+ return *(cvptr *)loc(i * sizeof(cvptr));
+ }
+ cvptr *limit()
+ {
+ return (cvptr *)fillbytes::limit();
+ }
+ void add(cvptr x)
+ {
+ *(cvptr *)grow(sizeof(x)) = x;
+ }
+ void popTo(int l)
+ {
+ assert(l <= length());
+ b.len = l * sizeof(cvptr);
+ }
+ int indexOf(cvptr x);
+ bool contains(cvptr x)
+ {
+ return indexOf(x) >= 0;
+ }
+ void freeAll(); // frees every ptr on the list, plus the list itself
+};
+// Use a macro rather than mess with subtle mismatches
+// between member and non-member function pointers.
+#define PTRLIST_QSORT(ptrls, fn) ::qsort((ptrls).base(), (ptrls).length(), sizeof(void *), fn)
+
+struct intlist : fillbytes
+{
+ int length()
+ {
+ return (int)(size() / sizeof(int));
+ }
+ int *base()
+ {
+ return (int *)fillbytes::base();
+ }
+ int &get(int i)
+ {
+ return *(int *)loc(i * sizeof(int));
+ }
+ int *limit()
+ {
+ return (int *)fillbytes::limit();
+ }
+ void add(int x)
+ {
+ *(int *)grow(sizeof(x)) = x;
+ }
+ void popTo(int l)
+ {
+ assert(l <= length());
+ b.len = l * sizeof(int);
+ }
+ int indexOf(int x);
+ bool contains(int x)
+ {
+ return indexOf(x) >= 0;
+ }
+};
diff --git a/depends/pack200/src/coding.cpp b/depends/pack200/src/coding.cpp
new file mode 100644
index 00000000..3e311131
--- /dev/null
+++ b/depends/pack200/src/coding.cpp
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// -*- C++ -*-
+// Small program for unpacking specially compressed Java packages.
+// John R. Rose
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <stdint.h>
+
+#include "defines.h"
+#include "bytes.h"
+#include "utils.h"
+#include "coding.h"
+
+#include "constants.h"
+#include "unpack.h"
+
+extern coding basic_codings[];
+
+#define CODING_PRIVATE(spec) \
+ int spec_ = spec; \
+ int B = CODING_B(spec_); \
+ int H = CODING_H(spec_); \
+ int L = 256 - H; \
+ int S = CODING_S(spec_); \
+ int D = CODING_D(spec_)
+
+#define IS_NEG_CODE(S, codeVal) ((((int)(codeVal) + 1) & ((1 << S) - 1)) == 0)
+
+#define DECODE_SIGN_S1(ux) (((uint32_t)(ux) >> 1) ^ -((int)(ux) & 1))
+
+static int decode_sign(int S, uint32_t ux)
+{ // == Coding.decodeSign32
+ assert(S > 0);
+ uint32_t sigbits = (ux >> S);
+ if (IS_NEG_CODE(S, ux))
+ return (int)(~sigbits);
+ else
+ return (int)(ux - sigbits);
+ // Note that (int)(ux-sigbits) can be negative, if ux is large enough.
+}
+
+coding *coding::init()
+{
+ if (umax > 0)
+ return this; // already done
+ assert(spec != 0); // sanity
+
+ // fill in derived fields
+ CODING_PRIVATE(spec);
+
+ // Return nullptr if 'arb(BHSD)' parameter constraints are not met:
+ if (B < 1 || B > B_MAX)
+ return nullptr;
+ if (H < 1 || H > 256)
+ return nullptr;
+ if (S < 0 || S > 2)
+ return nullptr;
+ if (D < 0 || D > 1)
+ return nullptr;
+ if (B == 1 && H != 256)
+ return nullptr; // 1-byte coding must be fixed-size
+ if (B >= 5 && H == 256)
+ return nullptr; // no 5-byte fixed-size coding
+
+ // first compute the range of the coding, in 64 bits
+ int64_t range = 0;
+ {
+ int64_t H_i = 1;
+ for (int i = 0; i < B; i++)
+ {
+ range += H_i;
+ H_i *= H;
+ }
+ range *= L;
+ range += H_i;
+ }
+ assert(range > 0); // no useless codings, please
+
+ int this_umax;
+
+ // now, compute min and max
+ if (range >= ((int64_t)1 << 32))
+ {
+ this_umax = INT_MAX_VALUE;
+ this->umin = INT_MIN_VALUE;
+ this->max = INT_MAX_VALUE;
+ this->min = INT_MIN_VALUE;
+ }
+ else
+ {
+ this_umax = (range > INT_MAX_VALUE) ? INT_MAX_VALUE : (int)range - 1;
+ this->max = this_umax;
+ this->min = this->umin = 0;
+ if (S != 0 && range != 0)
+ {
+ int64_t maxPosCode = range - 1;
+ int64_t maxNegCode = range - 1;
+ while (IS_NEG_CODE(S, maxPosCode))
+ --maxPosCode;
+ while (!IS_NEG_CODE(S, maxNegCode))
+ --maxNegCode;
+ int maxPos = decode_sign(S, (uint32_t)maxPosCode);
+ if (maxPos < 0)
+ this->max = INT_MAX_VALUE; // 32-bit wraparound
+ else
+ this->max = maxPos;
+ if (maxNegCode < 0)
+ this->min = 0; // No negative codings at all.
+ else
+ this->min = decode_sign(S, (uint32_t)maxNegCode);
+ }
+ }
+
+ assert(!(isFullRange | isSigned | isSubrange)); // init
+ if (min < 0)
+ this->isSigned = true;
+ if (max < INT_MAX_VALUE && range <= INT_MAX_VALUE)
+ this->isSubrange = true;
+ if (max == INT_MAX_VALUE && min == INT_MIN_VALUE)
+ this->isFullRange = true;
+
+ // do this last, to reduce MT exposure (should have a membar too)
+ this->umax = this_umax;
+
+ return this;
+}
+
+coding *coding::findBySpec(int spec)
+{
+ for (coding *scan = &basic_codings[0];; scan++)
+ {
+ if (scan->spec == spec)
+ return scan->init();
+ if (scan->spec == 0)
+ break;
+ }
+ coding *ptr = NEW(coding, 1);
+ if (!ptr)
+ return nullptr;
+ coding *c = ptr->initFrom(spec);
+ if (c == nullptr)
+ {
+ ::free(ptr);
+ }
+ else
+ // else caller should free it...
+ c->isMalloc = true;
+ return c;
+}
+
+coding *coding::findBySpec(int B, int H, int S, int D)
+{
+ if (B < 1 || B > B_MAX)
+ return nullptr;
+ if (H < 1 || H > 256)
+ return nullptr;
+ if (S < 0 || S > 2)
+ return nullptr;
+ if (D < 0 || D > 1)
+ return nullptr;
+ return findBySpec(CODING_SPEC(B, H, S, D));
+}
+
+void coding::free()
+{
+ if (isMalloc)
+ {
+ ::free(this);
+ }
+}
+
+void coding_method::reset(value_stream *state)
+{
+ assert(state->rp == state->rplimit); // not in mid-stream, please
+ // assert(this == vs0.cm);
+ state[0] = vs0;
+ if (uValues != nullptr)
+ {
+ uValues->reset(state->helper());
+ }
+}
+
+uint32_t coding::parse(byte *&rp, int B, int H)
+{
+ int L = 256 - H;
+ byte *ptr = rp;
+ // hand peel the i==0 part of the loop:
+ uint32_t b_i = *ptr++ & 0xFF;
+ if (B == 1 || b_i < (uint32_t)L)
+ {
+ rp = ptr;
+ return b_i;
+ }
+ uint32_t sum = b_i;
+ uint32_t H_i = H;
+ assert(B <= B_MAX);
+ for (int i = 2; i <= B_MAX; i++)
+ { // easy for compilers to unroll if desired
+ b_i = *ptr++ & 0xFF;
+ sum += b_i * H_i;
+ if (i == B || b_i < (uint32_t)L)
+ {
+ rp = ptr;
+ return sum;
+ }
+ H_i *= H;
+ }
+ assert(false);
+ return 0;
+}
+
+uint32_t coding::parse_lgH(byte *&rp, int B, int H, int lgH)
+{
+ assert(H == (1 << lgH));
+ int L = 256 - (1 << lgH);
+ byte *ptr = rp;
+ // hand peel the i==0 part of the loop:
+ uint32_t b_i = *ptr++ & 0xFF;
+ if (B == 1 || b_i < (uint32_t)L)
+ {
+ rp = ptr;
+ return b_i;
+ }
+ uint32_t sum = b_i;
+ uint32_t lg_H_i = lgH;
+ assert(B <= B_MAX);
+ for (int i = 2; i <= B_MAX; i++)
+ { // easy for compilers to unroll if desired
+ b_i = *ptr++ & 0xFF;
+ sum += b_i << lg_H_i;
+ if (i == B || b_i < (uint32_t)L)
+ {
+ rp = ptr;
+ return sum;
+ }
+ lg_H_i += lgH;
+ }
+ assert(false);
+ return 0;
+}
+
+static const char ERB[] = "EOF reading band";
+
+void coding::parseMultiple(byte *&rp, int N, byte *limit, int B, int H)
+{
+ if (N < 0)
+ {
+ unpack_abort("bad value count");
+ return;
+ }
+ byte *ptr = rp;
+ if (B == 1 || H == 256)
+ {
+ size_t len = (size_t)N * B;
+ if (len / B != (size_t)N || ptr + len > limit)
+ {
+ unpack_abort(ERB);
+ return;
+ }
+ rp = ptr + len;
+ return;
+ }
+ // Note: We assume rp has enough zero-padding.
+ int L = 256 - H;
+ int n = B;
+ while (N > 0)
+ {
+ ptr += 1;
+ if (--n == 0)
+ {
+ // end of encoding at B bytes, regardless of byte value
+ }
+ else
+ {
+ int b = (ptr[-1] & 0xFF);
+ if (b >= L)
+ {
+ // keep going, unless we find a byte < L
+ continue;
+ }
+ }
+ // found the last byte
+ N -= 1;
+ n = B; // reset length counter
+ // do an error check here
+ if (ptr > limit)
+ {
+ unpack_abort(ERB);
+ return;
+ }
+ }
+ rp = ptr;
+ return;
+}
+
+bool value_stream::hasHelper()
+{
+ // If my coding method is a pop-style method,
+ // then I need a second value stream to transmit
+ // unfavored values.
+ // This can be determined by examining fValues.
+ return cm->fValues != nullptr;
+}
+
+void value_stream::init(byte *rp_, byte *rplimit_, coding *defc)
+{
+ rp = rp_;
+ rplimit = rplimit_;
+ sum = 0;
+ cm = nullptr; // no need in the simple case
+ setCoding(defc);
+}
+
+void value_stream::setCoding(coding *defc)
+{
+ if (defc == nullptr)
+ {
+ unpack_abort("bad coding");
+ defc = coding::findByIndex(_meta_canon_min); // random pick for recovery
+ }
+
+ c = (*defc);
+
+ // choose cmk
+ cmk = cmk_ERROR;
+ switch (c.spec)
+ {
+ case BYTE1_spec:
+ cmk = cmk_BYTE1;
+ break;
+ case CHAR3_spec:
+ cmk = cmk_CHAR3;
+ break;
+ case UNSIGNED5_spec:
+ cmk = cmk_UNSIGNED5;
+ break;
+ case DELTA5_spec:
+ cmk = cmk_DELTA5;
+ break;
+ case BCI5_spec:
+ cmk = cmk_BCI5;
+ break;
+ case BRANCH5_spec:
+ cmk = cmk_BRANCH5;
+ break;
+ default:
+ if (c.D() == 0)
+ {
+ switch (c.S())
+ {
+ case 0:
+ cmk = cmk_BHS0;
+ break;
+ case 1:
+ cmk = cmk_BHS1;
+ break;
+ default:
+ cmk = cmk_BHS;
+ break;
+ }
+ }
+ else
+ {
+ if (c.S() == 1)
+ {
+ if (c.isFullRange)
+ cmk = cmk_BHS1D1full;
+ if (c.isSubrange)
+ cmk = cmk_BHS1D1sub;
+ }
+ if (cmk == cmk_ERROR)
+ cmk = cmk_BHSD1;
+ }
+ }
+}
+
+static int getPopValue(value_stream *self, uint32_t uval)
+{
+ if (uval > 0)
+ {
+ // note that the initial parse performed a range check
+ assert(uval <= (uint32_t)self->cm->fVlength);
+ return self->cm->fValues[uval - 1];
+ }
+ else
+ {
+ // take an unfavored value
+ return self->helper()->getInt();
+ }
+}
+
+int coding::sumInUnsignedRange(int x, int y)
+{
+ assert(isSubrange);
+ int range = (int)(umax + 1);
+ assert(range > 0);
+ x += y;
+ if (x != (int)((int64_t)(x - y) + (int64_t)y))
+ {
+ // 32-bit overflow interferes with range reduction.
+ // Back off from the overflow by adding a multiple of range:
+ if (x < 0)
+ {
+ x -= range;
+ assert(x >= 0);
+ }
+ else
+ {
+ x += range;
+ assert(x < 0);
+ }
+ }
+ if (x < 0)
+ {
+ x += range;
+ if (x >= 0)
+ return x;
+ }
+ else if (x >= range)
+ {
+ x -= range;
+ if (x < range)
+ return x;
+ }
+ else
+ {
+ // in range
+ return x;
+ }
+ // do it the hard way
+ x %= range;
+ if (x < 0)
+ x += range;
+ return x;
+}
+
+static int getDeltaValue(value_stream *self, uint32_t uval, bool isSubrange)
+{
+ assert((uint32_t)(self->c.isSubrange) == (uint32_t)isSubrange);
+ assert(self->c.isSubrange | self->c.isFullRange);
+ if (isSubrange)
+ return self->sum = self->c.sumInUnsignedRange(self->sum, (int)uval);
+ else
+ return self->sum += (int)uval;
+}
+
+bool value_stream::hasValue()
+{
+ if (rp < rplimit)
+ return true;
+ if (cm == nullptr)
+ return false;
+ if (cm->next == nullptr)
+ return false;
+ cm->next->reset(this);
+ return hasValue();
+}
+
+int value_stream::getInt()
+{
+ if (rp >= rplimit)
+ {
+ // Advance to next coding segment.
+ if (rp > rplimit || cm == nullptr || cm->next == nullptr)
+ {
+ // Must perform this check and throw an exception on bad input.
+ unpack_abort(ERB);
+ return 0;
+ }
+ cm->next->reset(this);
+ return getInt();
+ }
+
+ CODING_PRIVATE(c.spec);
+ uint32_t uval;
+ enum
+ {
+ B5 = 5,
+ B3 = 3,
+ H128 = 128,
+ H64 = 64,
+ H4 = 4
+ };
+ switch (cmk)
+ {
+ case cmk_BHS:
+ assert(D == 0);
+ uval = coding::parse(rp, B, H);
+ if (S == 0)
+ return (int)uval;
+ return decode_sign(S, uval);
+
+ case cmk_BHS0:
+ assert(S == 0 && D == 0);
+ uval = coding::parse(rp, B, H);
+ return (int)uval;
+
+ case cmk_BHS1:
+ assert(S == 1 && D == 0);
+ uval = coding::parse(rp, B, H);
+ return DECODE_SIGN_S1(uval);
+
+ case cmk_BYTE1:
+ assert(c.spec == BYTE1_spec);
+ assert(B == 1 && H == 256 && S == 0 && D == 0);
+ return *rp++ & 0xFF;
+
+ case cmk_CHAR3:
+ assert(c.spec == CHAR3_spec);
+ assert(B == B3 && H == H128 && S == 0 && D == 0);
+ return coding::parse_lgH(rp, B3, H128, 7);
+
+ case cmk_UNSIGNED5:
+ assert(c.spec == UNSIGNED5_spec);
+ assert(B == B5 && H == H64 && S == 0 && D == 0);
+ return coding::parse_lgH(rp, B5, H64, 6);
+
+ case cmk_BHSD1:
+ assert(D == 1);
+ uval = coding::parse(rp, B, H);
+ if (S != 0)
+ uval = (uint32_t)decode_sign(S, uval);
+ return getDeltaValue(this, uval, (bool)c.isSubrange);
+
+ case cmk_BHS1D1full:
+ assert(S == 1 && D == 1 && c.isFullRange);
+ uval = coding::parse(rp, B, H);
+ uval = (uint32_t)DECODE_SIGN_S1(uval);
+ return getDeltaValue(this, uval, false);
+
+ case cmk_BHS1D1sub:
+ assert(S == 1 && D == 1 && c.isSubrange);
+ uval = coding::parse(rp, B, H);
+ uval = (uint32_t)DECODE_SIGN_S1(uval);
+ return getDeltaValue(this, uval, true);
+
+ case cmk_DELTA5:
+ assert(c.spec == DELTA5_spec);
+ assert(B == B5 && H == H64 && S == 1 && D == 1 && c.isFullRange);
+ uval = coding::parse_lgH(rp, B5, H64, 6);
+ sum += DECODE_SIGN_S1(uval);
+ return sum;
+
+ case cmk_BCI5:
+ assert(c.spec == BCI5_spec);
+ assert(B == B5 && H == H4 && S == 0 && D == 0);
+ return coding::parse_lgH(rp, B5, H4, 2);
+
+ case cmk_BRANCH5:
+ assert(c.spec == BRANCH5_spec);
+ assert(B == B5 && H == H4 && S == 2 && D == 0);
+ uval = coding::parse_lgH(rp, B5, H4, 2);
+ return decode_sign(S, uval);
+
+ case cmk_pop:
+ uval = coding::parse(rp, B, H);
+ if (S != 0)
+ {
+ uval = (uint32_t)decode_sign(S, uval);
+ }
+ if (D != 0)
+ {
+ assert(c.isSubrange | c.isFullRange);
+ if (c.isSubrange)
+ sum = c.sumInUnsignedRange(sum, (int)uval);
+ else
+ sum += (int)uval;
+ uval = (uint32_t)sum;
+ }
+ return getPopValue(this, uval);
+
+ case cmk_pop_BHS0:
+ assert(S == 0 && D == 0);
+ uval = coding::parse(rp, B, H);
+ return getPopValue(this, uval);
+
+ case cmk_pop_BYTE1:
+ assert(c.spec == BYTE1_spec);
+ assert(B == 1 && H == 256 && S == 0 && D == 0);
+ return getPopValue(this, *rp++ & 0xFF);
+
+ default:
+ break;
+ }
+ assert(false);
+ return 0;
+}
+
+static int moreCentral(int x, int y)
+{ // used to find end of Pop.{F}
+ // Suggested implementation from the Pack200 specification:
+ uint32_t kx = (x >> 31) ^ (x << 1);
+ uint32_t ky = (y >> 31) ^ (y << 1);
+ return (kx < ky ? x : y);
+}
+// static maybe_inline
+// int moreCentral2(int x, int y, int min) {
+// // Strict implementation of buggy 150.7 specification.
+// // The bug is that the spec. says absolute-value ties are broken
+// // in favor of positive numbers, but the suggested implementation
+// // (also mentioned in the spec.) breaks ties in favor of negative numbers.
+// if ((x + y) != 0)
+// return min;
+// else
+// // return the other value, which breaks a tie in the positive direction
+// return (x > y)? x: y;
+//}
+
+static const byte *no_meta[] = {nullptr};
+#define NO_META (*(byte **)no_meta)
+enum
+{
+ POP_FAVORED_N = -2
+};
+
+// mode bits
+#define DISABLE_RUN 1 // used immediately inside ACodee
+#define DISABLE_POP 2 // used recursively in all pop sub-bands
+
+// This function knows all about meta-coding.
+void coding_method::init(byte *&band_rp, byte *band_limit, byte *&meta_rp, int mode,
+ coding *defc, int N, intlist *valueSink)
+{
+ assert(N != 0);
+
+ assert(u != nullptr); // must be pre-initialized
+ // if (u == nullptr) u = unpacker::current(); // expensive
+
+ int op = (meta_rp == nullptr) ? _meta_default : (*meta_rp++ & 0xFF);
+ coding *foundc = nullptr;
+ coding *to_free = nullptr;
+
+ if (op == _meta_default)
+ {
+ foundc = defc;
+ // and fall through
+ }
+ else if (op >= _meta_canon_min && op <= _meta_canon_max)
+ {
+ foundc = coding::findByIndex(op);
+ // and fall through
+ }
+ else if (op == _meta_arb)
+ {
+ int args = (*meta_rp++ & 0xFF);
+ // args = (D:[0..1] + 2*S[0..2] + 8*(B:[1..5]-1))
+ int D = ((args >> 0) & 1);
+ int S = ((args >> 1) & 3);
+ int B = ((args >> 3) & -1) + 1;
+ // & (H[1..256]-1)
+ int H = (*meta_rp++ & 0xFF) + 1;
+ foundc = coding::findBySpec(B, H, S, D);
+ to_free = foundc; // findBySpec may dynamically allocate
+ if (foundc == nullptr)
+ {
+ unpack_abort("illegal arbitrary coding");
+ return;
+ }
+ // and fall through
+ }
+ else if (op >= _meta_run && op < _meta_pop)
+ {
+ int args = (op - _meta_run);
+ // args: KX:[0..3] + 4*(KBFlag:[0..1]) + 8*(ABDef:[0..2])
+ int KX = ((args >> 0) & 3);
+ int KBFlag = ((args >> 2) & 1);
+ int ABDef = ((args >> 3) & -1);
+ assert(ABDef <= 2);
+ // & KB: one of [0..255] if KBFlag=1
+ int KB = (!KBFlag ? 3 : (*meta_rp++ & 0xFF));
+ int K = (KB + 1) << (KX * 4);
+ int N2 = (N >= 0) ? N - K : N;
+ if (N == 0 || (N2 <= 0 && N2 != N))
+ {
+ unpack_abort("illegal run encoding");
+ }
+ if ((mode & DISABLE_RUN) != 0)
+ {
+ unpack_abort("illegal nested run encoding");
+ }
+
+ // & Enc{ ACode } if ADef=0 (ABDef != 1)
+ // No direct nesting of 'run' in ACode, but in BCode it's OK.
+ int disRun = mode | DISABLE_RUN;
+ if (ABDef == 1)
+ {
+ this->init(band_rp, band_limit, NO_META, disRun, defc, K, valueSink);
+ }
+ else
+ {
+ this->init(band_rp, band_limit, meta_rp, disRun, defc, K, valueSink);
+ }
+
+ // & Enc{ BCode } if BDef=0 (ABDef != 2)
+ coding_method *tail = U_NEW(coding_method, 1);
+ if (!tail)
+ return;
+ tail->u = u;
+
+ // The 'run' codings may be nested indirectly via 'pop' codings.
+ // This means that this->next may already be filled in, if
+ // ACode was of type 'pop' with a 'run' token coding.
+ // No problem: Just chain the upcoming BCode onto the end.
+ for (coding_method *self = this;; self = self->next)
+ {
+ if (self->next == nullptr)
+ {
+ self->next = tail;
+ break;
+ }
+ }
+
+ if (ABDef == 2)
+ {
+ tail->init(band_rp, band_limit, NO_META, mode, defc, N2, valueSink);
+ }
+ else
+ {
+ tail->init(band_rp, band_limit, meta_rp, mode, defc, N2, valueSink);
+ }
+ // Note: The preceding calls to init should be tail-recursive.
+
+ return; // done; no falling through
+ }
+ else if (op >= _meta_pop && op < _meta_limit)
+ {
+ int args = (op - _meta_pop);
+ // args: (FDef:[0..1]) + 2*UDef:[0..1] + 4*(TDefL:[0..11])
+ int FDef = ((args >> 0) & 1);
+ int UDef = ((args >> 1) & 1);
+ int TDefL = ((args >> 2) & -1);
+ assert(TDefL <= 11);
+ int TDef = (TDefL > 0);
+ int TL = (TDefL <= 6) ? (2 << TDefL) : (256 - (4 << (11 - TDefL)));
+ int TH = (256 - TL);
+ if (N <= 0)
+ {
+ unpack_abort("illegal pop encoding");
+ }
+ if ((mode & DISABLE_POP) != 0)
+ {
+ unpack_abort("illegal nested pop encoding");
+ }
+
+ // No indirect nesting of 'pop', but 'run' is OK.
+ int disPop = DISABLE_POP;
+
+ // & Enc{ FCode } if FDef=0
+ int FN = POP_FAVORED_N;
+ assert(valueSink == nullptr);
+ intlist fValueSink;
+ fValueSink.init();
+ coding_method fval;
+ BYTES_OF(fval).clear();
+ fval.u = u;
+ if (FDef != 0)
+ {
+ fval.init(band_rp, band_limit, NO_META, disPop, defc, FN, &fValueSink);
+ }
+ else
+ {
+ fval.init(band_rp, band_limit, meta_rp, disPop, defc, FN, &fValueSink);
+ }
+ bytes fvbuf;
+ fValues = (u->saveTo(fvbuf, fValueSink.b), (int *)fvbuf.ptr);
+ fVlength = fValueSink.length(); // i.e., the parameter K
+ fValueSink.free();
+
+ // Skip the first {F} run in all subsequent passes.
+ // The next call to this->init(...) will set vs0.rp to point after the {F}.
+
+ // & Enc{ TCode } if TDef=0 (TDefL==0)
+ if (TDef != 0)
+ {
+ coding *tcode = coding::findBySpec(1, 256); // BYTE1
+ // find the most narrowly sufficient code:
+ for (int B = 2; B <= B_MAX; B++)
+ {
+ if (fVlength <= tcode->umax)
+ break; // found it
+ tcode->free();
+ tcode = coding::findBySpec(B, TH);
+ if (!tcode)
+ return;
+ }
+ if (!(fVlength <= tcode->umax))
+ {
+ unpack_abort("pop.L value too small");
+ }
+ this->init(band_rp, band_limit, NO_META, disPop, tcode, N, nullptr);
+ tcode->free();
+ }
+ else
+ {
+ this->init(band_rp, band_limit, meta_rp, disPop, defc, N, nullptr);
+ }
+
+ // Count the number of zero tokens right now.
+ // Also verify that they are in bounds.
+ int UN = 0; // one {U} for each zero in {T}
+ value_stream vs = vs0;
+ for (int i = 0; i < N; i++)
+ {
+ uint32_t val = vs.getInt();
+ if (val == 0)
+ UN += 1;
+ if (!(val <= (uint32_t)fVlength))
+ {
+ unpack_abort("pop token out of range");
+ }
+ }
+ vs.done();
+
+ // & Enc{ UCode } if UDef=0
+ if (UN != 0)
+ {
+ uValues = U_NEW(coding_method, 1);
+ if (uValues == nullptr)
+ return;
+ uValues->u = u;
+ if (UDef != 0)
+ {
+ uValues->init(band_rp, band_limit, NO_META, disPop, defc, UN, nullptr);
+ }
+ else
+ {
+ uValues->init(band_rp, band_limit, meta_rp, disPop, defc, UN, nullptr);
+ }
+ }
+ else
+ {
+ if (UDef == 0)
+ {
+ int uop = (*meta_rp++ & 0xFF);
+ if (uop > _meta_canon_max)
+ // %%% Spec. requires the more strict (uop != _meta_default).
+ unpack_abort("bad meta-coding for empty pop/U");
+ }
+ }
+
+ // Bug fix for 6259542
+ // Last of all, adjust vs0.cmk to the 'pop' flavor
+ for (coding_method *self = this; self != nullptr; self = self->next)
+ {
+ coding_method_kind cmk2 = cmk_pop;
+ switch (self->vs0.cmk)
+ {
+ case cmk_BHS0:
+ cmk2 = cmk_pop_BHS0;
+ break;
+ case cmk_BYTE1:
+ cmk2 = cmk_pop_BYTE1;
+ break;
+ default:
+ break;
+ }
+ self->vs0.cmk = cmk2;
+ if (self != this)
+ {
+ assert(self->fValues == nullptr); // no double init
+ self->fValues = this->fValues;
+ self->fVlength = this->fVlength;
+ assert(self->uValues == nullptr); // must stay nullptr
+ }
+ }
+
+ return; // done; no falling through
+ }
+ else
+ {
+ unpack_abort("bad meta-coding");
+ }
+
+ // Common code here skips a series of values with one coding.
+ assert(foundc != nullptr);
+
+ assert(vs0.cmk == cmk_ERROR); // no garbage, please
+ assert(vs0.rp == nullptr); // no garbage, please
+ assert(vs0.rplimit == nullptr); // no garbage, please
+ assert(vs0.sum == 0); // no garbage, please
+
+ vs0.init(band_rp, band_limit, foundc);
+
+ // Done with foundc. Free if necessary.
+ if (to_free != nullptr)
+ {
+ to_free->free();
+ to_free = nullptr;
+ }
+ foundc = nullptr;
+
+ coding &c = vs0.c;
+ CODING_PRIVATE(c.spec);
+ // assert sane N
+ assert((uint32_t)N < INT_MAX_VALUE || N == POP_FAVORED_N);
+
+ // Look at the values, or at least skip over them quickly.
+ if (valueSink == nullptr)
+ {
+ // Skip and ignore values in the first pass.
+ c.parseMultiple(band_rp, N, band_limit, B, H);
+ }
+ else if (N >= 0)
+ {
+ // Pop coding, {F} sequence, initial run of values...
+ assert((mode & DISABLE_POP) != 0);
+ value_stream vs = vs0;
+ for (int n = 0; n < N; n++)
+ {
+ int val = vs.getInt();
+ valueSink->add(val);
+ }
+ band_rp = vs.rp;
+ }
+ else
+ {
+ // Pop coding, {F} sequence, final run of values...
+ assert((mode & DISABLE_POP) != 0);
+ assert(N == POP_FAVORED_N);
+ int min = INT_MIN_VALUE; // farthest from the center
+ // min2 is based on the buggy specification of centrality in version 150.7
+ // no known implementations transmit this value, but just in case...
+ // int min2 = INT_MIN_VALUE;
+ int last = 0;
+ // if there were initial runs, find the potential sentinels in them:
+ for (int i = 0; i < valueSink->length(); i++)
+ {
+ last = valueSink->get(i);
+ min = moreCentral(min, last);
+ // min2 = moreCentral2(min2, last, min);
+ }
+ value_stream vs = vs0;
+ for (;;)
+ {
+ int val = vs.getInt();
+ if (valueSink->length() > 0 && (val == last || val == min)) //|| val == min2
+ break;
+ valueSink->add(val);
+ last = val;
+ min = moreCentral(min, last);
+ // min2 = moreCentral2(min2, last, min);
+ }
+ band_rp = vs.rp;
+ }
+
+ // Get an accurate upper limit now.
+ vs0.rplimit = band_rp;
+ vs0.cm = this;
+
+ return; // success
+}
+
+coding basic_codings[] = {
+ // This one is not a usable irregular coding, but is used by cp_Utf8_chars.
+ CODING_INIT(3, 128, 0, 0),
+
+ // Fixed-length codings:
+ CODING_INIT(1, 256, 0, 0), CODING_INIT(1, 256, 1, 0), CODING_INIT(1, 256, 0, 1),
+ CODING_INIT(1, 256, 1, 1), CODING_INIT(2, 256, 0, 0), CODING_INIT(2, 256, 1, 0),
+ CODING_INIT(2, 256, 0, 1), CODING_INIT(2, 256, 1, 1), CODING_INIT(3, 256, 0, 0),
+ CODING_INIT(3, 256, 1, 0), CODING_INIT(3, 256, 0, 1), CODING_INIT(3, 256, 1, 1),
+ CODING_INIT(4, 256, 0, 0), CODING_INIT(4, 256, 1, 0), CODING_INIT(4, 256, 0, 1),
+ CODING_INIT(4, 256, 1, 1),
+
+ // Full-range variable-length codings:
+ CODING_INIT(5, 4, 0, 0), CODING_INIT(5, 4, 1, 0), CODING_INIT(5, 4, 2, 0),
+ CODING_INIT(5, 16, 0, 0), CODING_INIT(5, 16, 1, 0), CODING_INIT(5, 16, 2, 0),
+ CODING_INIT(5, 32, 0, 0), CODING_INIT(5, 32, 1, 0), CODING_INIT(5, 32, 2, 0),
+ CODING_INIT(5, 64, 0, 0), CODING_INIT(5, 64, 1, 0), CODING_INIT(5, 64, 2, 0),
+ CODING_INIT(5, 128, 0, 0), CODING_INIT(5, 128, 1, 0), CODING_INIT(5, 128, 2, 0),
+ CODING_INIT(5, 4, 0, 1), CODING_INIT(5, 4, 1, 1), CODING_INIT(5, 4, 2, 1),
+ CODING_INIT(5, 16, 0, 1), CODING_INIT(5, 16, 1, 1), CODING_INIT(5, 16, 2, 1),
+ CODING_INIT(5, 32, 0, 1), CODING_INIT(5, 32, 1, 1), CODING_INIT(5, 32, 2, 1),
+ CODING_INIT(5, 64, 0, 1), CODING_INIT(5, 64, 1, 1), CODING_INIT(5, 64, 2, 1),
+ CODING_INIT(5, 128, 0, 1), CODING_INIT(5, 128, 1, 1), CODING_INIT(5, 128, 2, 1),
+
+ // Variable length subrange codings:
+ CODING_INIT(2, 192, 0, 0), CODING_INIT(2, 224, 0, 0), CODING_INIT(2, 240, 0, 0),
+ CODING_INIT(2, 248, 0, 0), CODING_INIT(2, 252, 0, 0), CODING_INIT(2, 8, 0, 1),
+ CODING_INIT(2, 8, 1, 1), CODING_INIT(2, 16, 0, 1), CODING_INIT(2, 16, 1, 1),
+ CODING_INIT(2, 32, 0, 1), CODING_INIT(2, 32, 1, 1), CODING_INIT(2, 64, 0, 1),
+ CODING_INIT(2, 64, 1, 1), CODING_INIT(2, 128, 0, 1), CODING_INIT(2, 128, 1, 1),
+ CODING_INIT(2, 192, 0, 1), CODING_INIT(2, 192, 1, 1), CODING_INIT(2, 224, 0, 1),
+ CODING_INIT(2, 224, 1, 1), CODING_INIT(2, 240, 0, 1), CODING_INIT(2, 240, 1, 1),
+ CODING_INIT(2, 248, 0, 1), CODING_INIT(2, 248, 1, 1), CODING_INIT(3, 192, 0, 0),
+ CODING_INIT(3, 224, 0, 0), CODING_INIT(3, 240, 0, 0), CODING_INIT(3, 248, 0, 0),
+ CODING_INIT(3, 252, 0, 0), CODING_INIT(3, 8, 0, 1), CODING_INIT(3, 8, 1, 1),
+ CODING_INIT(3, 16, 0, 1), CODING_INIT(3, 16, 1, 1), CODING_INIT(3, 32, 0, 1),
+ CODING_INIT(3, 32, 1, 1), CODING_INIT(3, 64, 0, 1), CODING_INIT(3, 64, 1, 1),
+ CODING_INIT(3, 128, 0, 1), CODING_INIT(3, 128, 1, 1), CODING_INIT(3, 192, 0, 1),
+ CODING_INIT(3, 192, 1, 1), CODING_INIT(3, 224, 0, 1), CODING_INIT(3, 224, 1, 1),
+ CODING_INIT(3, 240, 0, 1), CODING_INIT(3, 240, 1, 1), CODING_INIT(3, 248, 0, 1),
+ CODING_INIT(3, 248, 1, 1), CODING_INIT(4, 192, 0, 0), CODING_INIT(4, 224, 0, 0),
+ CODING_INIT(4, 240, 0, 0), CODING_INIT(4, 248, 0, 0), CODING_INIT(4, 252, 0, 0),
+ CODING_INIT(4, 8, 0, 1), CODING_INIT(4, 8, 1, 1), CODING_INIT(4, 16, 0, 1),
+ CODING_INIT(4, 16, 1, 1), CODING_INIT(4, 32, 0, 1), CODING_INIT(4, 32, 1, 1),
+ CODING_INIT(4, 64, 0, 1), CODING_INIT(4, 64, 1, 1), CODING_INIT(4, 128, 0, 1),
+ CODING_INIT(4, 128, 1, 1), CODING_INIT(4, 192, 0, 1), CODING_INIT(4, 192, 1, 1),
+ CODING_INIT(4, 224, 0, 1), CODING_INIT(4, 224, 1, 1), CODING_INIT(4, 240, 0, 1),
+ CODING_INIT(4, 240, 1, 1), CODING_INIT(4, 248, 0, 1), CODING_INIT(4, 248, 1, 1),
+ CODING_INIT(0, 0, 0, 0)};
+#define BASIC_INDEX_LIMIT (int)(sizeof(basic_codings) / sizeof(basic_codings[0]) - 1)
+
+coding *coding::findByIndex(int idx)
+{
+ int index_limit = BASIC_INDEX_LIMIT;
+ assert(_meta_canon_min == 1 && _meta_canon_max + 1 == index_limit);
+
+ if (idx >= _meta_canon_min && idx <= _meta_canon_max)
+ return basic_codings[idx].init();
+ else
+ return nullptr;
+}
diff --git a/depends/pack200/src/coding.h b/depends/pack200/src/coding.h
new file mode 100644
index 00000000..f9bd6ca2
--- /dev/null
+++ b/depends/pack200/src/coding.h
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+struct unpacker;
+
+#define INT_MAX_VALUE ((int)0x7FFFFFFF)
+#define INT_MIN_VALUE ((int)0x80000000)
+
+#define CODING_SPEC(B, H, S, D) ((B) << 20 | (H) << 8 | (S) << 4 | (D) << 0)
+#define CODING_B(x) ((x) >> 20 & 0xF)
+#define CODING_H(x) ((x) >> 8 & 0xFFF)
+#define CODING_S(x) ((x) >> 4 & 0xF)
+#define CODING_D(x) ((x) >> 0 & 0xF)
+
+#define CODING_INIT(B, H, S, D) \
+ { \
+ CODING_SPEC(B, H, S, D), 0, 0, 0, 0, 0, 0, 0, 0 \
+ }
+
+// For debugging purposes, some compilers do not like this and will complain.
+// #define long do_not_use_C_long_types_use_jlong_or_int
+// Use of the type "long" is problematic, do not use it.
+
+struct coding
+{
+ int spec; // B,H,S,D
+
+ // Handy values derived from the spec:
+ int B()
+ {
+ return CODING_B(spec);
+ }
+ int H()
+ {
+ return CODING_H(spec);
+ }
+ int S()
+ {
+ return CODING_S(spec);
+ }
+ int D()
+ {
+ return CODING_D(spec);
+ }
+ int L()
+ {
+ return 256 - CODING_H(spec);
+ }
+ int min, max;
+ int umin, umax;
+ char isSigned, isSubrange, isFullRange, isMalloc;
+
+ coding *init(); // returns self or nullptr if error
+ coding *initFrom(int spec_)
+ {
+ assert(this->spec == 0);
+ this->spec = spec_;
+ return init();
+ }
+
+ static coding *findBySpec(int spec);
+ static coding *findBySpec(int B, int H, int S = 0, int D = 0);
+ static coding *findByIndex(int irregularCodingIndex);
+
+ static uint32_t parse(byte *&rp, int B, int H);
+ static uint32_t parse_lgH(byte *&rp, int B, int H, int lgH);
+ static void parseMultiple(byte *&rp, int N, byte *limit, int B, int H);
+
+ uint32_t parse(byte *&rp)
+ {
+ return parse(rp, CODING_B(spec), CODING_H(spec));
+ }
+ void parseMultiple(byte *&rp, int N, byte *limit)
+ {
+ parseMultiple(rp, N, limit, CODING_B(spec), CODING_H(spec));
+ }
+
+ bool canRepresent(int x)
+ {
+ return (x >= min && x <= max);
+ }
+ bool canRepresentUnsigned(int x)
+ {
+ return (x >= umin && x <= umax);
+ }
+
+ int sumInUnsignedRange(int x, int y);
+
+ int readFrom(byte *&rpVar, int *dbase);
+ void readArrayFrom(byte *&rpVar, int *dbase, int length, int *values);
+ void skipArrayFrom(byte *&rpVar, int length)
+ {
+ readArrayFrom(rpVar, (int *)NULL, length, (int *)NULL);
+ }
+
+ void free(); // free self if isMalloc
+};
+
+enum coding_method_kind
+{
+ cmk_ERROR,
+ cmk_BHS,
+ cmk_BHS0,
+ cmk_BHS1,
+ cmk_BHSD1,
+ cmk_BHS1D1full, // isFullRange
+ cmk_BHS1D1sub, // isSubRange
+
+ // special cases hand-optimized (~50% of all decoded values)
+ cmk_BYTE1, //(1,256) 6%
+ cmk_CHAR3, //(3,128) 7%
+ cmk_UNSIGNED5, //(5,64) 13%
+ cmk_DELTA5, //(5,64,1,1) 5%
+ cmk_BCI5, //(5,4) 18%
+ cmk_BRANCH5, //(5,4,2) 4%
+ // cmk_UNSIGNED5H16, //(5,16) 5%
+ // cmk_UNSIGNED2H4, //(2,4) 6%
+ // cmk_DELTA4H8, //(4,8,1,1) 10%
+ // cmk_DELTA3H16, //(3,16,1,1) 9%
+ cmk_BHS_LIMIT,
+ cmk_pop,
+ cmk_pop_BHS0,
+ cmk_pop_BYTE1,
+ cmk_pop_LIMIT,
+ cmk_LIMIT
+};
+
+enum
+{
+ BYTE1_spec = CODING_SPEC(1, 256, 0, 0),
+ CHAR3_spec = CODING_SPEC(3, 128, 0, 0),
+ UNSIGNED4_spec = CODING_SPEC(4, 256, 0, 0),
+ UNSIGNED5_spec = CODING_SPEC(5, 64, 0, 0),
+ SIGNED5_spec = CODING_SPEC(5, 64, 1, 0),
+ DELTA5_spec = CODING_SPEC(5, 64, 1, 1),
+ UDELTA5_spec = CODING_SPEC(5, 64, 0, 1),
+ MDELTA5_spec = CODING_SPEC(5, 64, 2, 1),
+ BCI5_spec = CODING_SPEC(5, 4, 0, 0),
+ BRANCH5_spec = CODING_SPEC(5, 4, 2, 0)
+};
+
+enum
+{
+ B_MAX = 5,
+ C_SLOP = B_MAX * 10
+};
+
+struct coding_method;
+
+// iterator under the control of a meta-coding
+struct value_stream
+{
+ // current coding of values or values
+ coding c; // B,H,S,D,etc.
+ coding_method_kind cmk; // type of decoding needed
+ byte *rp; // read pointer
+ byte *rplimit; // final value of read pointer
+ int sum; // partial sum of all values so far (D=1 only)
+ coding_method *cm; // coding method that defines this stream
+
+ void init(byte *band_rp, byte *band_limit, coding *defc);
+ void init(byte *band_rp, byte *band_limit, int spec)
+ {
+ init(band_rp, band_limit, coding::findBySpec(spec));
+ }
+
+ void setCoding(coding *c);
+ void setCoding(int spec)
+ {
+ setCoding(coding::findBySpec(spec));
+ }
+
+ // Parse and decode a single value.
+ int getInt();
+
+ // Parse and decode a single byte, with no error checks.
+ int getByte()
+ {
+ assert(cmk == cmk_BYTE1);
+ assert(rp < rplimit);
+ return *rp++ & 0xFF;
+ }
+
+ // Used only for asserts.
+ bool hasValue();
+
+ void done()
+ {
+ assert(!hasValue());
+ }
+
+ // Sometimes a value stream has an auxiliary (but there are never two).
+ value_stream *helper()
+ {
+ assert(hasHelper());
+ return this + 1;
+ }
+ bool hasHelper();
+};
+
+struct coding_method
+{
+ value_stream vs0; // initial state snapshot (vs.meta==this)
+
+ coding_method *next; // what to do when we run out of bytes
+
+ // these fields are used for pop codes only:
+ int *fValues; // favored value array
+ int fVlength; // maximum favored value token
+ coding_method *uValues; // unfavored value stream
+
+ // pointer to outer unpacker, for error checks etc.
+ unpacker *u;
+
+ // Initialize a value stream.
+ void reset(value_stream *state);
+
+ // Parse a band header, size a band, and initialize for further action.
+ // band_rp advances (but not past band_limit), and meta_rp advances.
+ // The mode gives context, such as "inside a pop".
+ // The defc and N are the incoming parameters to a meta-coding.
+ // The value sink is used to collect output values, when desired.
+ void init(byte *&band_rp, byte *band_limit, byte *&meta_rp, int mode, coding *defc, int N,
+ intlist *valueSink);
+};
diff --git a/depends/pack200/src/constants.h b/depends/pack200/src/constants.h
new file mode 100644
index 00000000..2cc14b7d
--- /dev/null
+++ b/depends/pack200/src/constants.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ Java Class Version numbers history
+ 1.0 to 1.3.X 45,3
+ 1.4 to 1.4.X 46,0
+ 1.5 to 1.5.X 49,0
+ 1.6 to 1.5.x 50,0 NOTE Assumed for now
+*/
+
+// classfile constants
+#define JAVA_MAGIC 0xCAFEBABE
+#define JAVA_MIN_MAJOR_VERSION 45
+#define JAVA_MIN_MINOR_VERSION 3
+#define JAVA5_MAX_MAJOR_VERSION 49
+#define JAVA5_MAX_MINOR_VERSION 0
+// NOTE: Assume for now
+#define JAVA6_MAX_MAJOR_VERSION 50
+#define JAVA6_MAX_MINOR_VERSION 0
+
+// package file constants
+#define JAVA_PACKAGE_MAGIC 0xCAFED00D
+#define JAVA5_PACKAGE_MAJOR_VERSION 150
+#define JAVA5_PACKAGE_MINOR_VERSION 7
+
+#define JAVA6_PACKAGE_MAJOR_VERSION 160
+#define JAVA6_PACKAGE_MINOR_VERSION 1
+
+// magic number for gzip streams (for processing pack200-gzip data)
+#define GZIP_MAGIC 0x1F8B0800
+#define GZIP_MAGIC_MASK 0xFFFFFF00 // last \bchar\b is variable "flg" field
+
+enum
+{
+ CONSTANT_None,
+ CONSTANT_Utf8,
+ CONSTANT_unused2, /* unused, was Unicode */
+ CONSTANT_Integer,
+ CONSTANT_Float,
+ CONSTANT_Long,
+ CONSTANT_Double,
+ CONSTANT_Class,
+ CONSTANT_String,
+ CONSTANT_Fieldref,
+ CONSTANT_Methodref,
+ CONSTANT_InterfaceMethodref,
+ CONSTANT_NameandType,
+ CONSTANT_Signature = 13,
+ CONSTANT_All = 14,
+ CONSTANT_Limit = 15,
+ CONSTANT_NONE = 0,
+ CONSTANT_Literal = 20, // pseudo-tag for debugging
+ CONSTANT_Member = 21, // pseudo-tag for debugging
+ SUBINDEX_BIT = 64, // combined with CONSTANT_xxx for ixTag
+ ACC_STATIC = 0x0008,
+ ACC_IC_LONG_FORM = (1 << 16), // for ic_flags
+ CLASS_ATTR_SourceFile = 17,
+ CLASS_ATTR_EnclosingMethod = 18,
+ CLASS_ATTR_InnerClasses = 23,
+ CLASS_ATTR_ClassFile_version = 24,
+ FIELD_ATTR_ConstantValue = 17,
+ METHOD_ATTR_Code = 17,
+ METHOD_ATTR_Exceptions = 18,
+ METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23,
+ METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24,
+ METHOD_ATTR_AnnotationDefault = 25,
+ CODE_ATTR_StackMapTable = 0,
+ CODE_ATTR_LineNumberTable = 1,
+ CODE_ATTR_LocalVariableTable = 2,
+ CODE_ATTR_LocalVariableTypeTable = 3,
+ // X_ATTR_Synthetic = 12, // ACC_SYNTHETIC; not predefined
+ X_ATTR_Signature = 19,
+ X_ATTR_Deprecated = 20,
+ X_ATTR_RuntimeVisibleAnnotations = 21,
+ X_ATTR_RuntimeInvisibleAnnotations = 22,
+ X_ATTR_OVERFLOW = 16,
+ X_ATTR_LIMIT_NO_FLAGS_HI = 32,
+ X_ATTR_LIMIT_FLAGS_HI = 63,
+
+#define O_ATTR_DO(F) \
+ F(X_ATTR_OVERFLOW, 01) \
+ /*(end)*/
+#define X_ATTR_DO(F) \
+ O_ATTR_DO(F) F(X_ATTR_Signature, Signature) F(X_ATTR_Deprecated, Deprecated) \
+ F(X_ATTR_RuntimeVisibleAnnotations, RuntimeVisibleAnnotations) \
+ F(X_ATTR_RuntimeInvisibleAnnotations, RuntimeInvisibleAnnotations) \
+ /*F(X_ATTR_Synthetic,Synthetic)*/ \
+ /*(end)*/
+#define CLASS_ATTR_DO(F) \
+ F(CLASS_ATTR_SourceFile, SourceFile) F(CLASS_ATTR_InnerClasses, InnerClasses) \
+ F(CLASS_ATTR_EnclosingMethod, EnclosingMethod) F(CLASS_ATTR_ClassFile_version, 02) \
+ /*(end)*/
+#define FIELD_ATTR_DO(F) \
+ F(FIELD_ATTR_ConstantValue, ConstantValue) \
+ /*(end)*/
+#define METHOD_ATTR_DO(F) \
+ F(METHOD_ATTR_Code, Code) F(METHOD_ATTR_Exceptions, Exceptions) \
+ F(METHOD_ATTR_RuntimeVisibleParameterAnnotations, RuntimeVisibleParameterAnnotations) \
+ F(METHOD_ATTR_RuntimeInvisibleParameterAnnotations, \
+ RuntimeInvisibleParameterAnnotations) \
+ F(METHOD_ATTR_AnnotationDefault, AnnotationDefault) \
+ /*(end)*/
+#define CODE_ATTR_DO(F) \
+ F(CODE_ATTR_StackMapTable, StackMapTable) F(CODE_ATTR_LineNumberTable, LineNumberTable) \
+ F(CODE_ATTR_LocalVariableTable, LocalVariableTable) \
+ F(CODE_ATTR_LocalVariableTypeTable, LocalVariableTypeTable) \
+ /*(end)*/
+#define ALL_ATTR_DO(F) \
+ X_ATTR_DO(F) CLASS_ATTR_DO(F) FIELD_ATTR_DO(F) METHOD_ATTR_DO(F) CODE_ATTR_DO(F) \
+ /*(end)*/
+
+ // attribute "context types"
+ ATTR_CONTEXT_CLASS = 0,
+ ATTR_CONTEXT_FIELD = 1,
+ ATTR_CONTEXT_METHOD = 2,
+ ATTR_CONTEXT_CODE = 3,
+ ATTR_CONTEXT_LIMIT = 4,
+
+ // constants for parsed layouts (stored in band::le_kind)
+ EK_NONE = 0, // not a layout element
+ EK_INT = 'I', // B H I SH etc., also FH etc.
+ EK_BCI = 'P', // PH etc.
+ EK_BCID = 'Q', // POH etc.
+ EK_BCO = 'O', // OH etc.
+ EK_REPL = 'N', // NH[...] etc.
+ EK_REF = 'R', // RUH, RUNH, KQH, etc.
+ EK_UN = 'T', // TB(...)[...] etc.
+ EK_CASE = 'K', // (...)[...] etc.
+ EK_CALL = '(', // (0), (1), etc.
+ EK_CBLE = '[', // [...][...] etc.
+ NO_BAND_INDEX = -1,
+
+ // File option bits, from LSB in ascending bit position.
+ FO_DEFLATE_HINT = 1 << 0,
+ FO_IS_CLASS_STUB = 1 << 1,
+
+ // Archive option bits, from LSB in ascending bit position:
+ AO_HAVE_SPECIAL_FORMATS = 1 << 0,
+ AO_HAVE_CP_NUMBERS = 1 << 1,
+ AO_HAVE_ALL_CODE_FLAGS = 1 << 2,
+ AO_3_UNUSED_MBZ = 1 << 3,
+ AO_HAVE_FILE_HEADERS = 1 << 4,
+ AO_DEFLATE_HINT = 1 << 5,
+ AO_HAVE_FILE_MODTIME = 1 << 6,
+ AO_HAVE_FILE_OPTIONS = 1 << 7,
+ AO_HAVE_FILE_SIZE_HI = 1 << 8,
+ AO_HAVE_CLASS_FLAGS_HI = 1 << 9,
+ AO_HAVE_FIELD_FLAGS_HI = 1 << 10,
+ AO_HAVE_METHOD_FLAGS_HI = 1 << 11,
+ AO_HAVE_CODE_FLAGS_HI = 1 << 12,
+#define ARCHIVE_BIT_DO(F) \
+ F(AO_HAVE_SPECIAL_FORMATS) F(AO_HAVE_CP_NUMBERS) F(AO_HAVE_ALL_CODE_FLAGS) \
+ /*F(AO_3_UNUSED_MBZ)*/ \
+ F(AO_HAVE_FILE_HEADERS) F(AO_DEFLATE_HINT) F(AO_HAVE_FILE_MODTIME) \
+ F(AO_HAVE_FILE_OPTIONS) F(AO_HAVE_FILE_SIZE_HI) F(AO_HAVE_CLASS_FLAGS_HI) \
+ F(AO_HAVE_FIELD_FLAGS_HI) F(AO_HAVE_METHOD_FLAGS_HI) F(AO_HAVE_CODE_FLAGS_HI) \
+ /*(end)*/
+
+ // Constants for decoding attribute definition header bytes.
+ ADH_CONTEXT_MASK = 0x3, // (hdr & ADH_CONTEXT_MASK)
+ ADH_BIT_SHIFT = 0x2, // (hdr >> ADH_BIT_SHIFT)
+ ADH_BIT_IS_LSB = 1, // (hdr >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB
+#define ADH_BYTE(context, index) ((((index) + ADH_BIT_IS_LSB) << ADH_BIT_SHIFT) + (context))
+#define ADH_BYTE_CONTEXT(adhb) ((adhb) & ADH_CONTEXT_MASK)
+#define ADH_BYTE_INDEX(adhb) (((adhb) >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB)
+ NO_MODTIME = 0, // nullptr modtime value
+
+ // meta-coding
+ _meta_default = 0,
+ _meta_canon_min = 1,
+ _meta_canon_max = 115,
+ _meta_arb = 116,
+ _meta_run = 117,
+ _meta_pop = 141,
+ _meta_limit = 189,
+ _meta_error = 255,
+ _xxx_1_end
+};
+
+// Bytecodes.
+
+enum
+{
+ bc_nop = 0, // 0x00
+ bc_aconst_null = 1, // 0x01
+ bc_iconst_m1 = 2, // 0x02
+ bc_iconst_0 = 3, // 0x03
+ bc_iconst_1 = 4, // 0x04
+ bc_iconst_2 = 5, // 0x05
+ bc_iconst_3 = 6, // 0x06
+ bc_iconst_4 = 7, // 0x07
+ bc_iconst_5 = 8, // 0x08
+ bc_lconst_0 = 9, // 0x09
+ bc_lconst_1 = 10, // 0x0a
+ bc_fconst_0 = 11, // 0x0b
+ bc_fconst_1 = 12, // 0x0c
+ bc_fconst_2 = 13, // 0x0d
+ bc_dconst_0 = 14, // 0x0e
+ bc_dconst_1 = 15, // 0x0f
+ bc_bipush = 16, // 0x10
+ bc_sipush = 17, // 0x11
+ bc_ldc = 18, // 0x12
+ bc_ldc_w = 19, // 0x13
+ bc_ldc2_w = 20, // 0x14
+ bc_iload = 21, // 0x15
+ bc_lload = 22, // 0x16
+ bc_fload = 23, // 0x17
+ bc_dload = 24, // 0x18
+ bc_aload = 25, // 0x19
+ bc_iload_0 = 26, // 0x1a
+ bc_iload_1 = 27, // 0x1b
+ bc_iload_2 = 28, // 0x1c
+ bc_iload_3 = 29, // 0x1d
+ bc_lload_0 = 30, // 0x1e
+ bc_lload_1 = 31, // 0x1f
+ bc_lload_2 = 32, // 0x20
+ bc_lload_3 = 33, // 0x21
+ bc_fload_0 = 34, // 0x22
+ bc_fload_1 = 35, // 0x23
+ bc_fload_2 = 36, // 0x24
+ bc_fload_3 = 37, // 0x25
+ bc_dload_0 = 38, // 0x26
+ bc_dload_1 = 39, // 0x27
+ bc_dload_2 = 40, // 0x28
+ bc_dload_3 = 41, // 0x29
+ bc_aload_0 = 42, // 0x2a
+ bc_aload_1 = 43, // 0x2b
+ bc_aload_2 = 44, // 0x2c
+ bc_aload_3 = 45, // 0x2d
+ bc_iaload = 46, // 0x2e
+ bc_laload = 47, // 0x2f
+ bc_faload = 48, // 0x30
+ bc_daload = 49, // 0x31
+ bc_aaload = 50, // 0x32
+ bc_baload = 51, // 0x33
+ bc_caload = 52, // 0x34
+ bc_saload = 53, // 0x35
+ bc_istore = 54, // 0x36
+ bc_lstore = 55, // 0x37
+ bc_fstore = 56, // 0x38
+ bc_dstore = 57, // 0x39
+ bc_astore = 58, // 0x3a
+ bc_istore_0 = 59, // 0x3b
+ bc_istore_1 = 60, // 0x3c
+ bc_istore_2 = 61, // 0x3d
+ bc_istore_3 = 62, // 0x3e
+ bc_lstore_0 = 63, // 0x3f
+ bc_lstore_1 = 64, // 0x40
+ bc_lstore_2 = 65, // 0x41
+ bc_lstore_3 = 66, // 0x42
+ bc_fstore_0 = 67, // 0x43
+ bc_fstore_1 = 68, // 0x44
+ bc_fstore_2 = 69, // 0x45
+ bc_fstore_3 = 70, // 0x46
+ bc_dstore_0 = 71, // 0x47
+ bc_dstore_1 = 72, // 0x48
+ bc_dstore_2 = 73, // 0x49
+ bc_dstore_3 = 74, // 0x4a
+ bc_astore_0 = 75, // 0x4b
+ bc_astore_1 = 76, // 0x4c
+ bc_astore_2 = 77, // 0x4d
+ bc_astore_3 = 78, // 0x4e
+ bc_iastore = 79, // 0x4f
+ bc_lastore = 80, // 0x50
+ bc_fastore = 81, // 0x51
+ bc_dastore = 82, // 0x52
+ bc_aastore = 83, // 0x53
+ bc_bastore = 84, // 0x54
+ bc_castore = 85, // 0x55
+ bc_sastore = 86, // 0x56
+ bc_pop = 87, // 0x57
+ bc_pop2 = 88, // 0x58
+ bc_dup = 89, // 0x59
+ bc_dup_x1 = 90, // 0x5a
+ bc_dup_x2 = 91, // 0x5b
+ bc_dup2 = 92, // 0x5c
+ bc_dup2_x1 = 93, // 0x5d
+ bc_dup2_x2 = 94, // 0x5e
+ bc_swap = 95, // 0x5f
+ bc_iadd = 96, // 0x60
+ bc_ladd = 97, // 0x61
+ bc_fadd = 98, // 0x62
+ bc_dadd = 99, // 0x63
+ bc_isub = 100, // 0x64
+ bc_lsub = 101, // 0x65
+ bc_fsub = 102, // 0x66
+ bc_dsub = 103, // 0x67
+ bc_imul = 104, // 0x68
+ bc_lmul = 105, // 0x69
+ bc_fmul = 106, // 0x6a
+ bc_dmul = 107, // 0x6b
+ bc_idiv = 108, // 0x6c
+ bc_ldiv = 109, // 0x6d
+ bc_fdiv = 110, // 0x6e
+ bc_ddiv = 111, // 0x6f
+ bc_irem = 112, // 0x70
+ bc_lrem = 113, // 0x71
+ bc_frem = 114, // 0x72
+ bc_drem = 115, // 0x73
+ bc_ineg = 116, // 0x74
+ bc_lneg = 117, // 0x75
+ bc_fneg = 118, // 0x76
+ bc_dneg = 119, // 0x77
+ bc_ishl = 120, // 0x78
+ bc_lshl = 121, // 0x79
+ bc_ishr = 122, // 0x7a
+ bc_lshr = 123, // 0x7b
+ bc_iushr = 124, // 0x7c
+ bc_lushr = 125, // 0x7d
+ bc_iand = 126, // 0x7e
+ bc_land = 127, // 0x7f
+ bc_ior = 128, // 0x80
+ bc_lor = 129, // 0x81
+ bc_ixor = 130, // 0x82
+ bc_lxor = 131, // 0x83
+ bc_iinc = 132, // 0x84
+ bc_i2l = 133, // 0x85
+ bc_i2f = 134, // 0x86
+ bc_i2d = 135, // 0x87
+ bc_l2i = 136, // 0x88
+ bc_l2f = 137, // 0x89
+ bc_l2d = 138, // 0x8a
+ bc_f2i = 139, // 0x8b
+ bc_f2l = 140, // 0x8c
+ bc_f2d = 141, // 0x8d
+ bc_d2i = 142, // 0x8e
+ bc_d2l = 143, // 0x8f
+ bc_d2f = 144, // 0x90
+ bc_i2b = 145, // 0x91
+ bc_i2c = 146, // 0x92
+ bc_i2s = 147, // 0x93
+ bc_lcmp = 148, // 0x94
+ bc_fcmpl = 149, // 0x95
+ bc_fcmpg = 150, // 0x96
+ bc_dcmpl = 151, // 0x97
+ bc_dcmpg = 152, // 0x98
+ bc_ifeq = 153, // 0x99
+ bc_ifne = 154, // 0x9a
+ bc_iflt = 155, // 0x9b
+ bc_ifge = 156, // 0x9c
+ bc_ifgt = 157, // 0x9d
+ bc_ifle = 158, // 0x9e
+ bc_if_icmpeq = 159, // 0x9f
+ bc_if_icmpne = 160, // 0xa0
+ bc_if_icmplt = 161, // 0xa1
+ bc_if_icmpge = 162, // 0xa2
+ bc_if_icmpgt = 163, // 0xa3
+ bc_if_icmple = 164, // 0xa4
+ bc_if_acmpeq = 165, // 0xa5
+ bc_if_acmpne = 166, // 0xa6
+ bc_goto = 167, // 0xa7
+ bc_jsr = 168, // 0xa8
+ bc_ret = 169, // 0xa9
+ bc_tableswitch = 170, // 0xaa
+ bc_lookupswitch = 171, // 0xab
+ bc_ireturn = 172, // 0xac
+ bc_lreturn = 173, // 0xad
+ bc_freturn = 174, // 0xae
+ bc_dreturn = 175, // 0xaf
+ bc_areturn = 176, // 0xb0
+ bc_return = 177, // 0xb1
+ bc_getstatic = 178, // 0xb2
+ bc_putstatic = 179, // 0xb3
+ bc_getfield = 180, // 0xb4
+ bc_putfield = 181, // 0xb5
+ bc_invokevirtual = 182, // 0xb6
+ bc_invokespecial = 183, // 0xb7
+ bc_invokestatic = 184, // 0xb8
+ bc_invokeinterface = 185, // 0xb9
+ bc_xxxunusedxxx = 186, // 0xba
+ bc_new = 187, // 0xbb
+ bc_newarray = 188, // 0xbc
+ bc_anewarray = 189, // 0xbd
+ bc_arraylength = 190, // 0xbe
+ bc_athrow = 191, // 0xbf
+ bc_checkcast = 192, // 0xc0
+ bc_instanceof = 193, // 0xc1
+ bc_monitorenter = 194, // 0xc2
+ bc_monitorexit = 195, // 0xc3
+ bc_wide = 196, // 0xc4
+ bc_multianewarray = 197, // 0xc5
+ bc_ifnull = 198, // 0xc6
+ bc_ifnonnull = 199, // 0xc7
+ bc_goto_w = 200, // 0xc8
+ bc_jsr_w = 201, // 0xc9
+ bc_bytecode_limit = 202 // 0xca
+};
+
+enum
+{
+ bc_end_marker = 255,
+ bc_byte_escape = 254,
+ bc_ref_escape = 253,
+ _first_linker_op = bc_getstatic,
+ _last_linker_op = bc_invokestatic,
+ _num_linker_ops = (_last_linker_op - _first_linker_op) + 1,
+ _self_linker_op = bc_bytecode_limit,
+ _self_linker_aload_flag = 1 * _num_linker_ops,
+ _self_linker_super_flag = 2 * _num_linker_ops,
+ _self_linker_limit = _self_linker_op + 4 * _num_linker_ops,
+ _invokeinit_op = _self_linker_limit,
+ _invokeinit_self_option = 0,
+ _invokeinit_super_option = 1,
+ _invokeinit_new_option = 2,
+ _invokeinit_limit = _invokeinit_op + 3,
+ _xldc_op = _invokeinit_limit,
+ bc_aldc = bc_ldc,
+ bc_cldc = _xldc_op + 0,
+ bc_ildc = _xldc_op + 1,
+ bc_fldc = _xldc_op + 2,
+ bc_aldc_w = bc_ldc_w,
+ bc_cldc_w = _xldc_op + 3,
+ bc_ildc_w = _xldc_op + 4,
+ bc_fldc_w = _xldc_op + 5,
+ bc_lldc2_w = bc_ldc2_w,
+ bc_dldc2_w = _xldc_op + 6,
+ _xldc_limit = _xldc_op + 7,
+ _xxx_3_end
+};
diff --git a/depends/pack200/src/defines.h b/depends/pack200/src/defines.h
new file mode 100644
index 00000000..cfe5fc28
--- /dev/null
+++ b/depends/pack200/src/defines.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// random definitions
+
+#ifdef _MSC_VER
+#include <windows.h>
+#include <winuser.h>
+#else
+#include <unistd.h>
+#endif
+
+// Error messages that we have
+#define ERROR_ENOMEM "Memory allocation failed"
+#define ERROR_FORMAT "Corrupted pack file"
+#define ERROR_RESOURCE "Cannot extract resource file"
+#define ERROR_OVERFLOW "Internal buffer overflow"
+#define ERROR_INTERNAL "Internal error"
+
+#define lengthof(array) (sizeof(array) / sizeof(array[0]))
+
+#define NEW(T, n) (T *) must_malloc((int)(scale_size(n, sizeof(T))))
+#define U_NEW(T, n) (T *) u->alloc(scale_size(n, sizeof(T)))
+#define T_NEW(T, n) (T *) u->temp_alloc(scale_size(n, sizeof(T)))
+
+typedef signed char byte;
+
+#ifdef _MSC_VER
+#define MKDIR(dir) mkdir(dir)
+#define getpid() _getpid()
+#define PATH_MAX MAX_PATH
+#define dup2(a, b) _dup2(a, b)
+#define strcasecmp(s1, s2) _stricmp(s1, s2)
+#define tempname _tempname
+#define sleep Sleep
+#else
+#define MKDIR(dir) mkdir(dir, 0777);
+#endif
+
+/* Must cast to void *, then size_t, then int. */
+#define ptrlowbits(x) ((int)(size_t)(void *)(x))
+
+#define DEFAULT_ARCHIVE_MODTIME 1060000000 // Aug 04, 2003 5:26 PM PDT
diff --git a/depends/pack200/src/unpack.cpp b/depends/pack200/src/unpack.cpp
new file mode 100644
index 00000000..55d253b2
--- /dev/null
+++ b/depends/pack200/src/unpack.cpp
@@ -0,0 +1,4793 @@
+/*
+ * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// -*- C++ -*-
+// Program for unpacking specially compressed Java packages.
+// John R. Rose
+
+/*
+ * When compiling for a 64bit LP64 system (longs and pointers being 64bits),
+ * the printf format %ld is correct and use of %lld will cause warning
+ * errors from some compilers (gcc/g++).
+ * _LP64 can be explicitly set (used on Linux).
+ * Solaris compilers will define __sparcv9 or __x86_64 on 64bit compilations.
+ */
+#if defined(_LP64) || defined(__sparcv9) || defined(__x86_64)
+#define LONG_LONG_FORMAT "%ld"
+#define LONG_LONG_HEX_FORMAT "%lx"
+#else
+#define LONG_LONG_FORMAT "%lld"
+#define LONG_LONG_HEX_FORMAT "%016llx"
+#endif
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <limits.h>
+#include <time.h>
+#include <stdint.h>
+
+#include "defines.h"
+#include "bytes.h"
+#include "utils.h"
+#include "coding.h"
+#include "bands.h"
+
+#include "constants.h"
+
+#include "zip.h"
+
+#include "unpack.h"
+
+// tags, in canonical order:
+static const byte TAGS_IN_ORDER[] = {
+ CONSTANT_Utf8, CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long,
+ CONSTANT_Double, CONSTANT_String, CONSTANT_Class, CONSTANT_Signature,
+ CONSTANT_NameandType, CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_InterfaceMethodref};
+#define N_TAGS_IN_ORDER (sizeof TAGS_IN_ORDER)
+
+// REQUESTED must be -2 for u2 and REQUESTED_LDC must be -1 for u1
+enum
+{
+ NOT_REQUESTED = 0,
+ REQUESTED = -2,
+ REQUESTED_LDC = -1
+};
+
+#define NO_INORD ((uint32_t) - 1)
+
+struct entry
+{
+ byte tag;
+ unsigned short nrefs; // pack w/ tag
+
+ int outputIndex;
+ uint32_t inord; // &cp.entries[cp.tag_base[this->tag]+this->inord] == this
+
+ entry **refs;
+
+ // put last to pack best
+ union
+ {
+ bytes b;
+ int i;
+ int64_t l;
+ } value;
+
+ void requestOutputIndex(constant_pool &cp, int req = REQUESTED);
+ int getOutputIndex()
+ {
+ assert(outputIndex > NOT_REQUESTED);
+ return outputIndex;
+ }
+
+ entry *ref(int refnum)
+ {
+ assert((uint32_t)refnum < nrefs);
+ return refs[refnum];
+ }
+
+ const char *utf8String()
+ {
+ assert(tagMatches(CONSTANT_Utf8));
+ assert(value.b.len == strlen((const char *)value.b.ptr));
+ return (const char *)value.b.ptr;
+ }
+
+ entry *className()
+ {
+ assert(tagMatches(CONSTANT_Class));
+ return ref(0);
+ }
+
+ entry *memberClass()
+ {
+ assert(tagMatches(CONSTANT_Member));
+ return ref(0);
+ }
+
+ entry *memberDescr()
+ {
+ assert(tagMatches(CONSTANT_Member));
+ return ref(1);
+ }
+
+ entry *descrName()
+ {
+ assert(tagMatches(CONSTANT_NameandType));
+ return ref(0);
+ }
+
+ entry *descrType()
+ {
+ assert(tagMatches(CONSTANT_NameandType));
+ return ref(1);
+ }
+
+ int typeSize();
+
+ bytes &asUtf8();
+ int asInteger()
+ {
+ assert(tag == CONSTANT_Integer);
+ return value.i;
+ }
+
+ bool isUtf8(bytes &b)
+ {
+ return tagMatches(CONSTANT_Utf8) && value.b.equals(b);
+ }
+
+ bool isDoubleWord()
+ {
+ return tag == CONSTANT_Double || tag == CONSTANT_Long;
+ }
+
+ bool tagMatches(byte tag2)
+ {
+ return (tag2 == tag) || (tag2 == CONSTANT_Utf8 && tag == CONSTANT_Signature) ||
+ (tag2 == CONSTANT_Literal && tag >= CONSTANT_Integer && tag <= CONSTANT_String &&
+ tag != CONSTANT_Class) ||
+ (tag2 == CONSTANT_Member && tag >= CONSTANT_Fieldref &&
+ tag <= CONSTANT_InterfaceMethodref);
+ }
+};
+
+entry *cpindex::get(uint32_t i)
+{
+ if (i >= len)
+ return nullptr;
+ else if (base1 != nullptr)
+ // primary index
+ return &base1[i];
+ else
+ // secondary index
+ return base2[i];
+}
+
+inline bytes &entry::asUtf8()
+{
+ assert(tagMatches(CONSTANT_Utf8));
+ return value.b;
+}
+
+int entry::typeSize()
+{
+ assert(tagMatches(CONSTANT_Utf8));
+ const char *sigp = (char *)value.b.ptr;
+ switch (*sigp)
+ {
+ case '(':
+ sigp++;
+ break; // skip opening '('
+ case 'D':
+ case 'J':
+ return 2; // double field
+ default:
+ return 1; // field
+ }
+ int siglen = 0;
+ for (;;)
+ {
+ int ch = *sigp++;
+ switch (ch)
+ {
+ case 'D':
+ case 'J':
+ siglen += 1;
+ break;
+ case '[':
+ // Skip rest of array info.
+ while (ch == '[')
+ {
+ ch = *sigp++;
+ }
+ if (ch != 'L')
+ break;
+ // else fall through
+ case 'L':
+ sigp = strchr(sigp, ';');
+ if (sigp == nullptr)
+ {
+ unpack_abort("bad data");
+ return 0;
+ }
+ sigp += 1;
+ break;
+ case ')': // closing ')'
+ return siglen;
+ }
+ siglen += 1;
+ }
+}
+
+inline cpindex *constant_pool::getFieldIndex(entry *classRef)
+{
+ assert(classRef->tagMatches(CONSTANT_Class));
+ assert((uint32_t)classRef->inord < (uint32_t)tag_count[CONSTANT_Class]);
+ return &member_indexes[classRef->inord * 2 + 0];
+}
+inline cpindex *constant_pool::getMethodIndex(entry *classRef)
+{
+ assert(classRef->tagMatches(CONSTANT_Class));
+ assert((uint32_t)classRef->inord < (uint32_t)tag_count[CONSTANT_Class]);
+ return &member_indexes[classRef->inord * 2 + 1];
+}
+
+struct inner_class
+{
+ entry *inner;
+ entry *outer;
+ entry *name;
+ int flags;
+ inner_class *next_sibling;
+ bool requested;
+};
+
+// Here is where everything gets deallocated:
+void unpacker::free()
+{
+ int i;
+ if (jarout != nullptr)
+ jarout->reset();
+ if (gzin != nullptr)
+ {
+ gzin->free();
+ gzin = nullptr;
+ }
+ if (free_input)
+ input.free();
+ /*
+ * free everybody ever allocated with U_NEW or (recently) with T_NEW
+ */
+ assert(smallbuf.base() == nullptr || mallocs.contains(smallbuf.base()));
+ assert(tsmallbuf.base() == nullptr || tmallocs.contains(tsmallbuf.base()));
+ mallocs.freeAll();
+ tmallocs.freeAll();
+ smallbuf.init();
+ tsmallbuf.init();
+ bcimap.free();
+ class_fixup_type.free();
+ class_fixup_offset.free();
+ class_fixup_ref.free();
+ code_fixup_type.free();
+ code_fixup_offset.free();
+ code_fixup_source.free();
+ requested_ics.free();
+ cur_classfile_head.free();
+ cur_classfile_tail.free();
+ for (i = 0; i < ATTR_CONTEXT_LIMIT; i++)
+ attr_defs[i].free();
+
+ // free CP state
+ cp.outputEntries.free();
+ for (i = 0; i < CONSTANT_Limit; i++)
+ cp.tag_extras[i].free();
+}
+
+// input handling
+// Attempts to advance rplimit so that (rplimit-rp) is at least 'more'.
+// Will eagerly read ahead by larger chunks, if possible.
+// Returns false if (rplimit-rp) is not at least 'more',
+// unless rplimit hits input.limit().
+bool unpacker::ensure_input(int64_t more)
+{
+ uint64_t want = more - input_remaining();
+ if ((int64_t)want <= 0)
+ return true; // it's already in the buffer
+ if (rplimit == input.limit())
+ return true; // not expecting any more
+
+ if (read_input_fn == nullptr)
+ {
+ // assume it is already all there
+ bytes_read += input.limit() - rplimit;
+ rplimit = input.limit();
+ return true;
+ }
+
+ uint64_t remaining = (input.limit() - rplimit); // how much left to read?
+ byte *rpgoal = (want >= remaining) ? input.limit() : rplimit + (size_t)want;
+ enum
+ {
+ CHUNK_SIZE = (1 << 14)
+ };
+ uint64_t fetch = want;
+ if (fetch < CHUNK_SIZE)
+ fetch = CHUNK_SIZE;
+ if (fetch > remaining * 3 / 4)
+ fetch = remaining;
+ // Try to fetch at least "more" bytes.
+ while ((int64_t)fetch > 0)
+ {
+ int64_t nr = (*read_input_fn)(this, rplimit, fetch, remaining);
+ if (nr <= 0)
+ {
+ return (rplimit >= rpgoal);
+ }
+ remaining -= nr;
+ rplimit += nr;
+ fetch -= nr;
+ bytes_read += nr;
+ assert(remaining == (uint64_t)(input.limit() - rplimit));
+ }
+ return true;
+}
+
+// output handling
+
+fillbytes *unpacker::close_output(fillbytes *which)
+{
+ assert(wp != nullptr);
+ if (which == nullptr)
+ {
+ if (wpbase == cur_classfile_head.base())
+ {
+ which = &cur_classfile_head;
+ }
+ else
+ {
+ which = &cur_classfile_tail;
+ }
+ }
+ assert(wpbase == which->base());
+ assert(wplimit == which->end());
+ which->setLimit(wp);
+ wp = nullptr;
+ wplimit = nullptr;
+ // wpbase = nullptr;
+ return which;
+}
+
+// maybe_inline
+void unpacker::ensure_put_space(size_t size)
+{
+ if (wp + size <= wplimit)
+ return;
+ // Determine which segment needs expanding.
+ fillbytes *which = close_output();
+ byte *wp0 = which->grow(size);
+ wpbase = which->base();
+ wplimit = which->end();
+ wp = wp0;
+}
+
+byte *unpacker::put_space(size_t size)
+{
+ byte *wp0 = wp;
+ byte *wp1 = wp0 + size;
+ if (wp1 > wplimit)
+ {
+ ensure_put_space(size);
+ wp0 = wp;
+ wp1 = wp0 + size;
+ }
+ wp = wp1;
+ return wp0;
+}
+
+void unpacker::putu2_at(byte *wp, int n)
+{
+ if (n != (unsigned short)n)
+ {
+ unpack_abort(ERROR_OVERFLOW);
+ return;
+ }
+ wp[0] = (n) >> 8;
+ wp[1] = (n) >> 0;
+}
+
+void unpacker::putu4_at(byte *wp, int n)
+{
+ wp[0] = (n) >> 24;
+ wp[1] = (n) >> 16;
+ wp[2] = (n) >> 8;
+ wp[3] = (n) >> 0;
+}
+
+void unpacker::putu8_at(byte *wp, int64_t n)
+{
+ putu4_at(wp + 0, (int)((uint64_t)n >> 32));
+ putu4_at(wp + 4, (int)((uint64_t)n >> 0));
+}
+
+void unpacker::putu2(int n)
+{
+ putu2_at(put_space(2), n);
+}
+
+void unpacker::putu4(int n)
+{
+ putu4_at(put_space(4), n);
+}
+
+void unpacker::putu8(int64_t n)
+{
+ putu8_at(put_space(8), n);
+}
+
+int unpacker::putref_index(entry *e, int size)
+{
+ if (e == nullptr)
+ return 0;
+ else if (e->outputIndex > NOT_REQUESTED)
+ return e->outputIndex;
+ else if (e->tag == CONSTANT_Signature)
+ return putref_index(e->ref(0), size);
+ else
+ {
+ e->requestOutputIndex(cp, -size);
+ // Later on we'll fix the bits.
+ class_fixup_type.addByte(size);
+ class_fixup_offset.add((int)wpoffset());
+ class_fixup_ref.add(e);
+ return 0;
+ }
+}
+
+void unpacker::putref(entry *e)
+{
+ int oidx = putref_index(e, 2);
+ putu2_at(put_space(2), oidx);
+}
+
+void unpacker::putu1ref(entry *e)
+{
+ int oidx = putref_index(e, 1);
+ putu1_at(put_space(1), oidx);
+}
+
+// Allocation of small and large blocks.
+
+enum
+{
+ CHUNK = (1 << 14),
+ SMALL = (1 << 9)
+};
+
+// Call malloc. Try to combine small blocks and free much later.
+void *unpacker::alloc_heap(size_t size, bool smallOK, bool temp)
+{
+ if (!smallOK || size > SMALL)
+ {
+ void *res = must_malloc((int)size);
+ (temp ? &tmallocs : &mallocs)->add(res);
+ return res;
+ }
+ fillbytes &xsmallbuf = *(temp ? &tsmallbuf : &smallbuf);
+ if (!xsmallbuf.canAppend(size + 1))
+ {
+ xsmallbuf.init(CHUNK);
+ (temp ? &tmallocs : &mallocs)->add(xsmallbuf.base());
+ }
+ int growBy = (int)size;
+ growBy += -growBy & 7; // round up mod 8
+ return xsmallbuf.grow(growBy);
+}
+
+void unpacker::saveTo(bytes &b, byte *ptr, size_t len)
+{
+ b.ptr = U_NEW(byte, add_size(len, 1));
+ b.len = len;
+ b.copyFrom(ptr, len);
+}
+
+// Read up through band_headers.
+// Do the archive_size dance to set the size of the input mega-buffer.
+void unpacker::read_file_header()
+{
+ // Read file header to determine file type and total size.
+ enum
+ {
+ MAGIC_BYTES = 4,
+ AH_LENGTH_0 = 3, // minver, majver, options are outside of archive_size
+ AH_LENGTH_0_MAX = AH_LENGTH_0 + 1, // options might have 2 bytes
+ AH_LENGTH = 26, // maximum archive header length (w/ all fields)
+ // Length contributions from optional header fields:
+ AH_FILE_HEADER_LEN = 5, // sizehi/lo/next/modtime/files
+ AH_ARCHIVE_SIZE_LEN = 2, // sizehi/lo only; part of AH_FILE_HEADER_LEN
+ AH_CP_NUMBER_LEN = 4, // int/float/long/double
+ AH_SPECIAL_FORMAT_LEN = 2, // layouts/band-headers
+ AH_LENGTH_MIN =
+ AH_LENGTH - (AH_FILE_HEADER_LEN + AH_SPECIAL_FORMAT_LEN + AH_CP_NUMBER_LEN),
+ ARCHIVE_SIZE_MIN = AH_LENGTH_MIN - (AH_LENGTH_0 + AH_ARCHIVE_SIZE_LEN),
+ FIRST_READ = MAGIC_BYTES + AH_LENGTH_MIN
+ };
+
+ assert(AH_LENGTH_MIN == 15); // # of UNSIGNED5 fields required after archive_magic
+ assert(ARCHIVE_SIZE_MIN == 10); // # of UNSIGNED5 fields required after archive_size
+ // An absolute minimum nullptr archive is magic[4], {minver,majver,options}[3],
+ // archive_size[0], cp_counts[8], class_counts[4], for a total of 19 bytes.
+ // (Note that archive_size is optional; it may be 0..10 bytes in length.)
+ // The first read must capture everything up through the options field.
+ // This happens to work even if {minver,majver,options} is a pathological
+ // 15 bytes long. Legal pack files limit those three fields to 1+1+2 bytes.
+ assert(FIRST_READ >= MAGIC_BYTES + AH_LENGTH_0 * B_MAX);
+
+ // Up through archive_size, the largest possible archive header is
+ // magic[4], {minver,majver,options}[4], archive_size[10].
+ // (Note only the low 12 bits of options are allowed to be non-zero.)
+ // In order to parse archive_size, we need at least this many bytes
+ // in the first read. Of course, if archive_size_hi is more than
+ // a byte, we probably will fail to allocate the buffer, since it
+ // will be many gigabytes long. This is a practical, not an
+ // architectural limit to Pack200 archive sizes.
+ assert(FIRST_READ >= MAGIC_BYTES + AH_LENGTH_0_MAX + 2 * B_MAX);
+
+ bool foreign_buf = (read_input_fn == nullptr);
+ byte initbuf[(int)FIRST_READ + (int)C_SLOP + 200]; // 200 is for JAR I/O
+ if (foreign_buf)
+ {
+ // inbytes is all there is
+ input.set(inbytes);
+ rp = input.base();
+ rplimit = input.limit();
+ }
+ else
+ {
+ // inbytes, if not empty, contains some read-ahead we must use first
+ // ensure_input will take care of copying it into initbuf,
+ // then querying read_input_fn for any additional data needed.
+ // However, the caller must assume that we use up all of inbytes.
+ // There is no way to tell the caller that we used only part of them.
+ // Therefore, the caller must use only a bare minimum of read-ahead.
+ if (inbytes.len > FIRST_READ)
+ {
+ unpack_abort("too much read-ahead");
+ }
+ input.set(initbuf, sizeof(initbuf));
+ input.b.clear();
+ input.b.copyFrom(inbytes);
+ rplimit = rp = input.base();
+ rplimit += inbytes.len;
+ bytes_read += inbytes.len;
+ }
+ // Read only 19 bytes, which is certain to contain #archive_options fields,
+ // but is certain not to overflow past the archive_header.
+ input.b.len = FIRST_READ;
+ if (!ensure_input(FIRST_READ))
+ unpack_abort("EOF reading archive magic number");
+
+ if (rp[0] == 'P' && rp[1] == 'K')
+ {
+ // In the Unix-style program, we simply simulate a copy command.
+ // Copy until EOF; assume the JAR file is the last segment.
+ fprintf(stderr, "Copy-mode.\n");
+ for (;;)
+ {
+ jarout->write_data(rp, (int)input_remaining());
+ if (foreign_buf)
+ break; // one-time use of a passed in buffer
+ if (input.size() < CHUNK)
+ {
+ // Get some breathing room.
+ input.set(U_NEW(byte, (size_t)CHUNK + C_SLOP), (size_t)CHUNK);
+ }
+ rp = rplimit = input.base();
+ if (!ensure_input(1))
+ break;
+ }
+ jarout->closeJarFile(false);
+ return;
+ }
+
+ // Read the magic number.
+ magic = 0;
+ for (int i1 = 0; i1 < (int)sizeof(magic); i1++)
+ {
+ magic <<= 8;
+ magic += (*rp++ & 0xFF);
+ }
+
+ // Read the first 3 values from the header.
+ value_stream hdr;
+ int hdrVals = 0;
+ int hdrValsSkipped = 0; // debug only
+ hdr.init(rp, rplimit, UNSIGNED5_spec);
+ minver = hdr.getInt();
+ majver = hdr.getInt();
+ hdrVals += 2;
+
+ if (magic != (int)JAVA_PACKAGE_MAGIC ||
+ (majver != JAVA5_PACKAGE_MAJOR_VERSION && majver != JAVA6_PACKAGE_MAJOR_VERSION) ||
+ (minver != JAVA5_PACKAGE_MINOR_VERSION && minver != JAVA6_PACKAGE_MINOR_VERSION))
+ {
+ char message[200];
+ sprintf(message, "@" ERROR_FORMAT ": magic/ver = "
+ "%08X/%d.%d should be %08X/%d.%d OR %08X/%d.%d\n",
+ magic, majver, minver, JAVA_PACKAGE_MAGIC, JAVA5_PACKAGE_MAJOR_VERSION,
+ JAVA5_PACKAGE_MINOR_VERSION, JAVA_PACKAGE_MAGIC, JAVA6_PACKAGE_MAJOR_VERSION,
+ JAVA6_PACKAGE_MINOR_VERSION);
+ unpack_abort(message);
+ }
+
+ archive_options = hdr.getInt();
+ hdrVals += 1;
+ assert(hdrVals == AH_LENGTH_0); // first three fields only
+
+#define ORBIT(bit) | (bit)
+ int OPTION_LIMIT = (0 ARCHIVE_BIT_DO(ORBIT));
+#undef ORBIT
+ if ((archive_options & ~OPTION_LIMIT) != 0)
+ {
+ fprintf(stderr, "Warning: Illegal archive options 0x%x\n", archive_options);
+ unpack_abort("illegal archive options");
+ return;
+ }
+
+ if ((archive_options & AO_HAVE_FILE_HEADERS) != 0)
+ {
+ uint32_t hi = hdr.getInt();
+ uint32_t lo = hdr.getInt();
+ uint64_t x = band::makeLong(hi, lo);
+ archive_size = (size_t)x;
+ if (archive_size != x)
+ {
+ // Silly size specified; force overflow.
+ archive_size = PSIZE_MAX + 1;
+ }
+ hdrVals += 2;
+ }
+ else
+ {
+ hdrValsSkipped += 2;
+ }
+
+ // Now we can size the whole archive.
+ // Read everything else into a mega-buffer.
+ rp = hdr.rp;
+ int header_size_0 = (int)(rp - input.base()); // used-up header (4byte + 3int)
+ int header_size_1 = (int)(rplimit - rp); // buffered unused initial fragment
+ int header_size = header_size_0 + header_size_1;
+ unsized_bytes_read = header_size_0;
+ if (foreign_buf)
+ {
+ if (archive_size > (size_t)header_size_1)
+ {
+ unpack_abort("EOF reading fixed input buffer");
+ return;
+ }
+ }
+ else if (archive_size != 0)
+ {
+ if (archive_size < ARCHIVE_SIZE_MIN)
+ {
+ unpack_abort("impossible archive size"); // bad input data
+ return;
+ }
+ if (archive_size < (size_t)header_size_1)
+ {
+ unpack_abort("too much read-ahead"); // somehow we pre-fetched too much?
+ return;
+ }
+ input.set(U_NEW(byte, add_size(header_size_0, archive_size, C_SLOP)),
+ (size_t)header_size_0 + archive_size);
+ assert(input.limit()[0] == 0);
+ // Move all the bytes we read initially into the real buffer.
+ input.b.copyFrom(initbuf, header_size);
+ rp = input.b.ptr + header_size_0;
+ rplimit = input.b.ptr + header_size;
+ }
+ else
+ {
+ // It's more complicated and painful.
+ // A zero archive_size means that we must read until EOF.
+ input.init(CHUNK * 2);
+ input.b.len = input.allocated;
+ rp = rplimit = input.base();
+ // Set up input buffer as if we already read the header:
+ input.b.copyFrom(initbuf, header_size);
+ rplimit += header_size;
+ while (ensure_input(input.limit() - rp))
+ {
+ size_t dataSoFar = input_remaining();
+ size_t nextSize = add_size(dataSoFar, CHUNK);
+ input.ensureSize(nextSize);
+ input.b.len = input.allocated;
+ rp = rplimit = input.base();
+ rplimit += dataSoFar;
+ }
+ size_t dataSize = (rplimit - input.base());
+ input.b.len = dataSize;
+ input.grow(C_SLOP);
+ free_input = true; // free it later
+ input.b.len = dataSize;
+ assert(input.limit()[0] == 0);
+ rp = rplimit = input.base();
+ rplimit += dataSize;
+ rp += header_size_0; // already scanned these bytes...
+ }
+ live_input = true; // mark as "do not reuse"
+
+ // read the rest of the header fields
+ ensure_input((AH_LENGTH - AH_LENGTH_0) * B_MAX);
+ hdr.rp = rp;
+ hdr.rplimit = rplimit;
+
+ if ((archive_options & AO_HAVE_FILE_HEADERS) != 0)
+ {
+ archive_next_count = hdr.getInt();
+ if (archive_next_count < 0)
+ unpack_abort("bad archive_next_count");
+ archive_modtime = hdr.getInt();
+ file_count = hdr.getInt();
+ if (file_count < 0)
+ unpack_abort("bad file_count");
+ hdrVals += 3;
+ }
+ else
+ {
+ hdrValsSkipped += 3;
+ }
+
+ if ((archive_options & AO_HAVE_SPECIAL_FORMATS) != 0)
+ {
+ band_headers_size = hdr.getInt();
+ if (band_headers_size < 0)
+ unpack_abort("bad band_headers_size");
+ attr_definition_count = hdr.getInt();
+ if (attr_definition_count < 0)
+ unpack_abort("bad attr_definition_count");
+ hdrVals += 2;
+ }
+ else
+ {
+ hdrValsSkipped += 2;
+ }
+
+ int cp_counts[N_TAGS_IN_ORDER];
+ for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++)
+ {
+ if (!(archive_options & AO_HAVE_CP_NUMBERS))
+ {
+ switch (TAGS_IN_ORDER[k])
+ {
+ case CONSTANT_Integer:
+ case CONSTANT_Float:
+ case CONSTANT_Long:
+ case CONSTANT_Double:
+ cp_counts[k] = 0;
+ hdrValsSkipped += 1;
+ continue;
+ }
+ }
+ cp_counts[k] = hdr.getInt();
+ if (cp_counts[k] < 0)
+ unpack_abort("bad cp_counts");
+ hdrVals += 1;
+ }
+
+ ic_count = hdr.getInt();
+ if (ic_count < 0)
+ unpack_abort("bad ic_count");
+
+ default_class_minver = hdr.getInt();
+ default_class_majver = hdr.getInt();
+
+ class_count = hdr.getInt();
+ if (class_count < 0)
+ unpack_abort("bad class_count");
+
+ hdrVals += 4;
+
+ // done with archive_header
+ hdrVals += hdrValsSkipped;
+ assert(hdrVals == AH_LENGTH);
+
+ rp = hdr.rp;
+ if (rp > rplimit)
+ unpack_abort("EOF reading archive header");
+
+ // Now size the CP.
+ cp.init(this, cp_counts);
+
+ default_file_modtime = archive_modtime;
+ if (default_file_modtime == 0 && !(archive_options & AO_HAVE_FILE_MODTIME))
+ default_file_modtime = DEFAULT_ARCHIVE_MODTIME; // taken from driver
+ if ((archive_options & AO_DEFLATE_HINT) != 0)
+ default_file_options |= FO_DEFLATE_HINT;
+
+ // meta-bytes, if any, immediately follow archive header
+ // band_headers.readData(band_headers_size);
+ ensure_input(band_headers_size);
+ if (input_remaining() < (size_t)band_headers_size)
+ {
+ unpack_abort("EOF reading band headers");
+ return;
+ }
+ bytes band_headers;
+ // The "1+" allows an initial byte to be pushed on the front.
+ band_headers.set(1 + U_NEW(byte, 1 + band_headers_size + C_SLOP), band_headers_size);
+
+ // Start scanning band headers here:
+ band_headers.copyFrom(rp, band_headers.len);
+ rp += band_headers.len;
+ assert(rp <= rplimit);
+ meta_rp = band_headers.ptr;
+ // Put evil meta-codes at the end of the band headers,
+ // so we are sure to throw an error if we run off the end.
+ bytes::of(band_headers.limit(), C_SLOP).clear(_meta_error);
+}
+
+void unpacker::finish()
+{
+ if (verbose >= 1)
+ {
+ fprintf(stderr, "A total of " LONG_LONG_FORMAT " bytes were read in %d segment(s).\n",
+ (bytes_read_before_reset + bytes_read), segments_read_before_reset + 1);
+ fprintf(stderr, "A total of " LONG_LONG_FORMAT " file content bytes were written.\n",
+ (bytes_written_before_reset + bytes_written));
+ fprintf(stderr,
+ "A total of %d files (of which %d are classes) were written to output.\n",
+ files_written_before_reset + files_written,
+ classes_written_before_reset + classes_written);
+ }
+ if (jarout != nullptr)
+ jarout->closeJarFile(true);
+}
+
+// Cf. PackageReader.readConstantPoolCounts
+void constant_pool::init(unpacker *u_, int counts[NUM_COUNTS])
+{
+ this->u = u_;
+
+ // Fill-pointer for CP.
+ int next_entry = 0;
+
+ // Size the constant pool:
+ for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++)
+ {
+ byte tag = TAGS_IN_ORDER[k];
+ int len = counts[k];
+ tag_count[tag] = len;
+ tag_base[tag] = next_entry;
+ next_entry += len;
+ // Detect and defend against constant pool size overflow.
+ // (Pack200 forbids the sum of CP counts to exceed 2^29-1.)
+ enum
+ {
+ CP_SIZE_LIMIT = (1 << 29),
+ IMPLICIT_ENTRY_COUNT = 1 // empty Utf8 string
+ };
+ if (len >= (1 << 29) || len < 0 || next_entry >= CP_SIZE_LIMIT + IMPLICIT_ENTRY_COUNT)
+ {
+ unpack_abort("archive too large: constant pool limit exceeded");
+ }
+ }
+
+ // Close off the end of the CP:
+ nentries = next_entry;
+
+ // place a limit on future CP growth:
+ int generous = 0;
+ generous = add_size(generous, u->ic_count); // implicit name
+ generous = add_size(generous, u->ic_count); // outer
+ generous = add_size(generous, u->ic_count); // outer.utf8
+ generous = add_size(generous, 40); // WKUs, misc
+ generous = add_size(generous, u->class_count); // implicit SourceFile strings
+ maxentries = add_size(nentries, generous);
+
+ // Note that this CP does not include "empty" entries
+ // for longs and doubles. Those are introduced when
+ // the entries are renumbered for classfile output.
+
+ entries = U_NEW(entry, maxentries);
+
+ first_extra_entry = &entries[nentries];
+
+ // Initialize the standard indexes.
+ tag_count[CONSTANT_All] = nentries;
+ tag_base[CONSTANT_All] = 0;
+ for (int tag = 0; tag < CONSTANT_Limit; tag++)
+ {
+ entry *cpMap = &entries[tag_base[tag]];
+ tag_index[tag].init(tag_count[tag], cpMap, tag);
+ }
+
+ // Initialize hashTab to a generous power-of-two size.
+ uint32_t pow2 = 1;
+ uint32_t target = maxentries + maxentries / 2; // 60% full
+ while (pow2 < target)
+ pow2 <<= 1;
+ hashTab = U_NEW(entry *, hashTabLength = pow2);
+}
+
+static byte *store_Utf8_char(byte *cp, unsigned short ch)
+{
+ if (ch >= 0x001 && ch <= 0x007F)
+ {
+ *cp++ = (byte)ch;
+ }
+ else if (ch <= 0x07FF)
+ {
+ *cp++ = (byte)(0xC0 | ((ch >> 6) & 0x1F));
+ *cp++ = (byte)(0x80 | ((ch >> 0) & 0x3F));
+ }
+ else
+ {
+ *cp++ = (byte)(0xE0 | ((ch >> 12) & 0x0F));
+ *cp++ = (byte)(0x80 | ((ch >> 6) & 0x3F));
+ *cp++ = (byte)(0x80 | ((ch >> 0) & 0x3F));
+ }
+ return cp;
+}
+
+static byte *skip_Utf8_chars(byte *cp, int len)
+{
+ for (;; cp++)
+ {
+ int ch = *cp & 0xFF;
+ if ((ch & 0xC0) != 0x80)
+ {
+ if (len-- == 0)
+ return cp;
+ if (ch < 0x80 && len == 0)
+ return cp + 1;
+ }
+ }
+}
+
+static int compare_Utf8_chars(bytes &b1, bytes &b2)
+{
+ int l1 = (int)b1.len;
+ int l2 = (int)b2.len;
+ int l0 = (l1 < l2) ? l1 : l2;
+ byte *p1 = b1.ptr;
+ byte *p2 = b2.ptr;
+ int c0 = 0;
+ for (int i = 0; i < l0; i++)
+ {
+ int c1 = p1[i] & 0xFF;
+ int c2 = p2[i] & 0xFF;
+ if (c1 != c2)
+ {
+ // Before returning the obvious answer,
+ // check to see if c1 or c2 is part of a 0x0000,
+ // which encodes as {0xC0,0x80}. The 0x0000 is the
+ // lowest-sorting Java char value, and yet it encodes
+ // as if it were the first char after 0x7F, which causes
+ // strings containing nulls to sort too high. All other
+ // comparisons are consistent between Utf8 and Java chars.
+ if (c1 == 0xC0 && (p1[i + 1] & 0xFF) == 0x80)
+ c1 = 0;
+ if (c2 == 0xC0 && (p2[i + 1] & 0xFF) == 0x80)
+ c2 = 0;
+ if (c0 == 0xC0)
+ {
+ assert(((c1 | c2) & 0xC0) == 0x80); // c1 & c2 are extension chars
+ if (c1 == 0x80)
+ c1 = 0; // will sort below c2
+ if (c2 == 0x80)
+ c2 = 0; // will sort below c1
+ }
+ return c1 - c2;
+ }
+ c0 = c1; // save away previous char
+ }
+ // common prefix is identical; return length difference if any
+ return l1 - l2;
+}
+
+// Cf. PackageReader.readUtf8Bands
+void unpacker::read_Utf8_values(entry *cpMap, int len)
+{
+ // Implicit first Utf8 string is the empty string.
+ enum
+ {
+ // certain bands begin with implicit zeroes
+ PREFIX_SKIP_2 = 2,
+ SUFFIX_SKIP_1 = 1
+ };
+
+ int i;
+
+ // First band: Read lengths of shared prefixes.
+ if (len > PREFIX_SKIP_2)
+ cp_Utf8_prefix.readData(len - PREFIX_SKIP_2);
+
+ // Second band: Read lengths of unshared suffixes:
+ if (len > SUFFIX_SKIP_1)
+ cp_Utf8_suffix.readData(len - SUFFIX_SKIP_1);
+
+ bytes *allsuffixes = T_NEW(bytes, len);
+
+ int nbigsuf = 0;
+ fillbytes charbuf; // buffer to allocate small strings
+ charbuf.init();
+
+ // Third band: Read the char values in the unshared suffixes:
+ cp_Utf8_chars.readData(cp_Utf8_suffix.getIntTotal());
+ for (i = 0; i < len; i++)
+ {
+ int suffix = (i < SUFFIX_SKIP_1) ? 0 : cp_Utf8_suffix.getInt();
+ if (suffix < 0)
+ {
+ unpack_abort("bad utf8 suffix");
+ }
+ if (suffix == 0 && i >= SUFFIX_SKIP_1)
+ {
+ // chars are packed in cp_Utf8_big_chars
+ nbigsuf += 1;
+ continue;
+ }
+ bytes &chars = allsuffixes[i];
+ uint32_t size3 = suffix * 3; // max Utf8 length
+ bool isMalloc = (suffix > SMALL);
+ if (isMalloc)
+ {
+ chars.malloc(size3);
+ }
+ else
+ {
+ if (!charbuf.canAppend(size3 + 1))
+ {
+ assert(charbuf.allocated == 0 || tmallocs.contains(charbuf.base()));
+ charbuf.init(CHUNK); // Reset to new buffer.
+ tmallocs.add(charbuf.base());
+ }
+ chars.set(charbuf.grow(size3 + 1), size3);
+ }
+
+ byte *chp = chars.ptr;
+ for (int j = 0; j < suffix; j++)
+ {
+ unsigned short ch = cp_Utf8_chars.getInt();
+ chp = store_Utf8_char(chp, ch);
+ }
+ // shrink to fit:
+ if (isMalloc)
+ {
+ chars.realloc(chp - chars.ptr);
+ tmallocs.add(chars.ptr); // free it later
+ }
+ else
+ {
+ int shrink = (int)(chars.limit() - chp);
+ chars.len -= shrink;
+ charbuf.b.len -= shrink; // ungrow to reclaim buffer space
+ // Note that we did not reclaim the final '\0'.
+ assert(chars.limit() == charbuf.limit() - 1);
+ assert(strlen((char *)chars.ptr) == chars.len);
+ }
+ }
+ // cp_Utf8_chars.done();
+
+ // Fourth band: Go back and size the specially packed strings.
+ int maxlen = 0;
+ cp_Utf8_big_suffix.readData(nbigsuf);
+ cp_Utf8_suffix.rewind();
+ for (i = 0; i < len; i++)
+ {
+ int suffix = (i < SUFFIX_SKIP_1) ? 0 : cp_Utf8_suffix.getInt();
+ int prefix = (i < PREFIX_SKIP_2) ? 0 : cp_Utf8_prefix.getInt();
+ if (prefix < 0 || prefix + suffix < 0)
+ {
+ unpack_abort("bad utf8 prefix");
+ }
+ bytes &chars = allsuffixes[i];
+ if (suffix == 0 && i >= SUFFIX_SKIP_1)
+ {
+ suffix = cp_Utf8_big_suffix.getInt();
+ assert(chars.ptr == nullptr);
+ chars.len = suffix; // just a momentary hack
+ }
+ else
+ {
+ assert(chars.ptr != nullptr);
+ }
+ if (maxlen < prefix + suffix)
+ {
+ maxlen = prefix + suffix;
+ }
+ }
+ // cp_Utf8_suffix.done(); // will use allsuffixes[i].len (ptr!=nullptr)
+ // cp_Utf8_big_suffix.done(); // will use allsuffixes[i].len
+
+ // Fifth band(s): Get the specially packed characters.
+ cp_Utf8_big_suffix.rewind();
+ for (i = 0; i < len; i++)
+ {
+ bytes &chars = allsuffixes[i];
+ if (chars.ptr != nullptr)
+ continue; // already input
+ int suffix = (int)chars.len; // pick up the hack
+ uint32_t size3 = suffix * 3;
+ if (suffix == 0)
+ continue; // done with empty string
+ chars.malloc(size3);
+ byte *chp = chars.ptr;
+ band saved_band = cp_Utf8_big_chars;
+ cp_Utf8_big_chars.readData(suffix);
+ for (int j = 0; j < suffix; j++)
+ {
+ unsigned short ch = cp_Utf8_big_chars.getInt();
+ chp = store_Utf8_char(chp, ch);
+ }
+ chars.realloc(chp - chars.ptr);
+ tmallocs.add(chars.ptr); // free it later
+ // cp_Utf8_big_chars.done();
+ cp_Utf8_big_chars = saved_band; // reset the band for the next string
+ }
+ cp_Utf8_big_chars.readData(0); // zero chars
+ // cp_Utf8_big_chars.done();
+
+ // Finally, sew together all the prefixes and suffixes.
+ bytes bigbuf;
+ bigbuf.malloc(maxlen * 3 + 1); // max Utf8 length, plus slop for nullptr
+ int prevlen = 0; // previous string length (in chars)
+ tmallocs.add(bigbuf.ptr); // free after this block
+ cp_Utf8_prefix.rewind();
+ for (i = 0; i < len; i++)
+ {
+ bytes &chars = allsuffixes[i];
+ int prefix = (i < PREFIX_SKIP_2) ? 0 : cp_Utf8_prefix.getInt();
+ int suffix = (int)chars.len;
+ byte *fillp;
+ // by induction, the buffer is already filled with the prefix
+ // make sure the prefix value is not corrupted, though:
+ if (prefix > prevlen)
+ {
+ unpack_abort("utf8 prefix overflow");
+ return;
+ }
+ fillp = skip_Utf8_chars(bigbuf.ptr, prefix);
+ // copy the suffix into the same buffer:
+ fillp = chars.writeTo(fillp);
+ assert(bigbuf.inBounds(fillp));
+ *fillp = 0; // bigbuf must contain a well-formed Utf8 string
+ int length = (int)(fillp - bigbuf.ptr);
+ bytes &value = cpMap[i].value.b;
+ value.set(U_NEW(byte, add_size(length, 1)), length);
+ value.copyFrom(bigbuf.ptr, length);
+ // Index all Utf8 strings
+ entry *&htref = cp.hashTabRef(CONSTANT_Utf8, value);
+ if (htref == nullptr)
+ {
+ // Note that if two identical strings are transmitted,
+ // the first is taken to be the canonical one.
+ htref = &cpMap[i];
+ }
+ prevlen = prefix + suffix;
+ }
+ // cp_Utf8_prefix.done();
+
+ // Free intermediate buffers.
+ free_temps();
+}
+
+void unpacker::read_single_words(band &cp_band, entry *cpMap, int len)
+{
+ cp_band.readData(len);
+ for (int i = 0; i < len; i++)
+ {
+ cpMap[i].value.i = cp_band.getInt(); // coding handles signs OK
+ }
+}
+
+void unpacker::read_double_words(band &cp_bands, entry *cpMap, int len)
+{
+ band &cp_band_hi = cp_bands;
+ band &cp_band_lo = cp_bands.nextBand();
+ cp_band_hi.readData(len);
+ cp_band_lo.readData(len);
+ for (int i = 0; i < len; i++)
+ {
+ cpMap[i].value.l = cp_band_hi.getLong(cp_band_lo, true);
+ }
+ // cp_band_hi.done();
+ // cp_band_lo.done();
+}
+
+void unpacker::read_single_refs(band &cp_band, byte refTag, entry *cpMap, int len)
+{
+ assert(refTag == CONSTANT_Utf8);
+ cp_band.setIndexByTag(refTag);
+ cp_band.readData(len);
+ int indexTag = (cp_band.bn == e_cp_Class) ? CONSTANT_Class : 0;
+ for (int i = 0; i < len; i++)
+ {
+ entry &e = cpMap[i];
+ e.refs = U_NEW(entry *, e.nrefs = 1);
+ entry *utf = cp_band.getRef();
+ e.refs[0] = utf;
+ e.value.b = utf->value.b; // copy value of Utf8 string to self
+ if (indexTag != 0)
+ {
+ // Maintain cross-reference:
+ entry *&htref = cp.hashTabRef(indexTag, e.value.b);
+ if (htref == nullptr)
+ {
+ // Note that if two identical classes are transmitted,
+ // the first is taken to be the canonical one.
+ htref = &e;
+ }
+ }
+ }
+ // cp_band.done();
+}
+
+void unpacker::read_double_refs(band &cp_band, byte ref1Tag, byte ref2Tag, entry *cpMap,
+ int len)
+{
+ band &cp_band1 = cp_band;
+ band &cp_band2 = cp_band.nextBand();
+ cp_band1.setIndexByTag(ref1Tag);
+ cp_band2.setIndexByTag(ref2Tag);
+ cp_band1.readData(len);
+ cp_band2.readData(len);
+ for (int i = 0; i < len; i++)
+ {
+ entry &e = cpMap[i];
+ e.refs = U_NEW(entry *, e.nrefs = 2);
+ e.refs[0] = cp_band1.getRef();
+ e.refs[1] = cp_band2.getRef();
+ }
+ // cp_band1.done();
+ // cp_band2.done();
+}
+
+// Cf. PackageReader.readSignatureBands
+void unpacker::read_signature_values(entry *cpMap, int len)
+{
+ cp_Signature_form.setIndexByTag(CONSTANT_Utf8);
+ cp_Signature_form.readData(len);
+ int ncTotal = 0;
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ entry &e = cpMap[i];
+ entry &form = *cp_Signature_form.getRef();
+ int nc = 0;
+
+ for (const char *ncp = form.utf8String(); *ncp; ncp++)
+ {
+ if (*ncp == 'L')
+ nc++;
+ }
+
+ ncTotal += nc;
+ e.refs = U_NEW(entry *, cpMap[i].nrefs = 1 + nc);
+ e.refs[0] = &form;
+ }
+ // cp_Signature_form.done();
+ cp_Signature_classes.setIndexByTag(CONSTANT_Class);
+ cp_Signature_classes.readData(ncTotal);
+ for (i = 0; i < len; i++)
+ {
+ entry &e = cpMap[i];
+ for (int j = 1; j < e.nrefs; j++)
+ {
+ e.refs[j] = cp_Signature_classes.getRef();
+ }
+ }
+ // cp_Signature_classes.done();
+}
+
+// Cf. PackageReader.readConstantPool
+void unpacker::read_cp()
+{
+ int i;
+
+ for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++)
+ {
+ byte tag = TAGS_IN_ORDER[k];
+ int len = cp.tag_count[tag];
+ int base = cp.tag_base[tag];
+
+ entry *cpMap = &cp.entries[base];
+ for (i = 0; i < len; i++)
+ {
+ cpMap[i].tag = tag;
+ cpMap[i].inord = i;
+ }
+
+ switch (tag)
+ {
+ case CONSTANT_Utf8:
+ read_Utf8_values(cpMap, len);
+ break;
+ case CONSTANT_Integer:
+ read_single_words(cp_Int, cpMap, len);
+ break;
+ case CONSTANT_Float:
+ read_single_words(cp_Float, cpMap, len);
+ break;
+ case CONSTANT_Long:
+ read_double_words(cp_Long_hi /*& cp_Long_lo*/, cpMap, len);
+ break;
+ case CONSTANT_Double:
+ read_double_words(cp_Double_hi /*& cp_Double_lo*/, cpMap, len);
+ break;
+ case CONSTANT_String:
+ read_single_refs(cp_String, CONSTANT_Utf8, cpMap, len);
+ break;
+ case CONSTANT_Class:
+ read_single_refs(cp_Class, CONSTANT_Utf8, cpMap, len);
+ break;
+ case CONSTANT_Signature:
+ read_signature_values(cpMap, len);
+ break;
+ case CONSTANT_NameandType:
+ read_double_refs(cp_Descr_name /*& cp_Descr_type*/, CONSTANT_Utf8,
+ CONSTANT_Signature, cpMap, len);
+ break;
+ case CONSTANT_Fieldref:
+ read_double_refs(cp_Field_class /*& cp_Field_desc*/, CONSTANT_Class,
+ CONSTANT_NameandType, cpMap, len);
+ break;
+ case CONSTANT_Methodref:
+ read_double_refs(cp_Method_class /*& cp_Method_desc*/, CONSTANT_Class,
+ CONSTANT_NameandType, cpMap, len);
+ break;
+ case CONSTANT_InterfaceMethodref:
+ read_double_refs(cp_Imethod_class /*& cp_Imethod_desc*/, CONSTANT_Class,
+ CONSTANT_NameandType, cpMap, len);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ cp.expandSignatures();
+ cp.initMemberIndexes();
+
+#define SNAME(n, s) #s "\0"
+ const char *symNames = (ALL_ATTR_DO(SNAME) "<init>");
+#undef SNAME
+
+ for (int sn = 0; sn < constant_pool::s_LIMIT; sn++)
+ {
+ assert(symNames[0] >= '0' && symNames[0] <= 'Z'); // sanity
+ bytes name;
+ name.set(symNames);
+ if (name.len > 0 && name.ptr[0] != '0')
+ {
+ cp.sym[sn] = cp.ensureUtf8(name);
+ }
+ symNames += name.len + 1; // skip trailing nullptr to next name
+ }
+
+ band::initIndexes(this);
+}
+
+static band *no_bands[] = {nullptr}; // shared empty body
+
+inline band &unpacker::attr_definitions::fixed_band(int e_class_xxx)
+{
+ return u->all_bands[xxx_flags_hi_bn + (e_class_xxx - e_class_flags_hi)];
+}
+inline band &unpacker::attr_definitions::xxx_flags_hi()
+{
+ return fixed_band(e_class_flags_hi);
+}
+inline band &unpacker::attr_definitions::xxx_flags_lo()
+{
+ return fixed_band(e_class_flags_lo);
+}
+inline band &unpacker::attr_definitions::xxx_attr_count()
+{
+ return fixed_band(e_class_attr_count);
+}
+inline band &unpacker::attr_definitions::xxx_attr_indexes()
+{
+ return fixed_band(e_class_attr_indexes);
+}
+inline band &unpacker::attr_definitions::xxx_attr_calls()
+{
+ return fixed_band(e_class_attr_calls);
+}
+
+inline unpacker::layout_definition *
+unpacker::attr_definitions::defineLayout(int idx, entry *nameEntry, const char *layout)
+{
+ const char *name = nameEntry->value.b.strval();
+ layout_definition *lo = defineLayout(idx, name, layout);
+ lo->nameEntry = nameEntry;
+ return lo;
+}
+
+unpacker::layout_definition *unpacker::attr_definitions::defineLayout(int idx, const char *name,
+ const char *layout)
+{
+ assert(flag_limit != 0); // must be set up already
+ if (idx >= 0)
+ {
+ // Fixed attr.
+ if (idx >= (int)flag_limit)
+ unpack_abort("attribute index too large");
+ if (isRedefined(idx))
+ unpack_abort("redefined attribute index");
+ redef |= ((uint64_t)1 << idx);
+ }
+ else
+ {
+ idx = flag_limit + overflow_count.length();
+ overflow_count.add(0); // make a new counter
+ }
+ layout_definition *lo = U_NEW(layout_definition, 1);
+ lo->idx = idx;
+ lo->name = name;
+ lo->layout = layout;
+ for (int adds = (idx + 1) - layouts.length(); adds > 0; adds--)
+ {
+ layouts.add(nullptr);
+ }
+ layouts.get(idx) = lo;
+ return lo;
+}
+
+band **unpacker::attr_definitions::buildBands(unpacker::layout_definition *lo)
+{
+ int i;
+ if (lo->elems != nullptr)
+ return lo->bands();
+ if (lo->layout[0] == '\0')
+ {
+ lo->elems = no_bands;
+ }
+ else
+ {
+ // Create bands for this attribute by parsing the layout.
+ bool hasCallables = lo->hasCallables();
+ bands_made = 0x10000; // base number for bands made
+ const char *lp = lo->layout;
+ lp = parseLayout(lp, lo->elems, -1);
+ if (lp[0] != '\0' || band_stack.length() > 0)
+ {
+ unpack_abort("garbage at end of layout");
+ }
+ band_stack.popTo(0);
+
+ // Fix up callables to point at their callees.
+ band **bands = lo->elems;
+ assert(bands == lo->bands());
+ int num_callables = 0;
+ if (hasCallables)
+ {
+ while (bands[num_callables] != nullptr)
+ {
+ if (bands[num_callables]->le_kind != EK_CBLE)
+ {
+ unpack_abort("garbage mixed with callables");
+ break;
+ }
+ num_callables += 1;
+ }
+ }
+ for (i = 0; i < calls_to_link.length(); i++)
+ {
+ band &call = *(band *)calls_to_link.get(i);
+ assert(call.le_kind == EK_CALL);
+ // Determine the callee.
+ int call_num = call.le_len;
+ if (call_num < 0 || call_num >= num_callables)
+ {
+ unpack_abort("bad call in layout");
+ break;
+ }
+ band &cble = *bands[call_num];
+ // Link the call to it.
+ call.le_body[0] = &cble;
+ // Distinguish backward calls and callables:
+ assert(cble.le_kind == EK_CBLE);
+ // FIXME: hit this one
+ // assert(cble.le_len == call_num);
+ cble.le_back |= call.le_back;
+ }
+ calls_to_link.popTo(0);
+ }
+ return lo->elems;
+}
+
+/* attribute layout language parser
+
+ attribute_layout:
+ ( layout_element )* | ( callable )+
+ layout_element:
+ ( integral | replication | union | call | reference )
+
+ callable:
+ '[' body ']'
+ body:
+ ( layout_element )+
+
+ integral:
+ ( unsigned_int | signed_int | bc_index | bc_offset | flag )
+ unsigned_int:
+ uint_type
+ signed_int:
+ 'S' uint_type
+ any_int:
+ ( unsigned_int | signed_int )
+ bc_index:
+ ( 'P' uint_type | 'PO' uint_type )
+ bc_offset:
+ 'O' any_int
+ flag:
+ 'F' uint_type
+ uint_type:
+ ( 'B' | 'H' | 'I' | 'V' )
+
+ replication:
+ 'N' uint_type '[' body ']'
+
+ union:
+ 'T' any_int (union_case)* '(' ')' '[' (body)? ']'
+ union_case:
+ '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']'
+ union_case_tag:
+ ( numeral | numeral '-' numeral )
+ call:
+ '(' numeral ')'
+
+ reference:
+ reference_type ( 'N' )? uint_type
+ reference_type:
+ ( constant_ref | schema_ref | utf8_ref | untyped_ref )
+ constant_ref:
+ ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' )
+ schema_ref:
+ ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' )
+ utf8_ref:
+ 'RU'
+ untyped_ref:
+ 'RQ'
+
+ numeral:
+ '(' ('-')? (digit)+ ')'
+ digit:
+ ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
+
+*/
+
+const char *unpacker::attr_definitions::parseIntLayout(const char *lp, band *&res, byte le_kind,
+ bool can_be_signed)
+{
+ band *b = U_NEW(band, 1);
+ char le = *lp++;
+ int spec = UNSIGNED5_spec;
+ if (le == 'S' && can_be_signed)
+ {
+ // Note: This is the last use of sign. There is no 'EF_SIGN'.
+ spec = SIGNED5_spec;
+ le = *lp++;
+ }
+ else if (le == 'B')
+ {
+ spec = BYTE1_spec; // unsigned byte
+ }
+ b->init(u, bands_made++, spec);
+ b->le_kind = le_kind;
+ int le_len = 0;
+ switch (le)
+ {
+ case 'B':
+ le_len = 1;
+ break;
+ case 'H':
+ le_len = 2;
+ break;
+ case 'I':
+ le_len = 4;
+ break;
+ case 'V':
+ le_len = 0;
+ break;
+ default:
+ unpack_abort("bad layout element");
+ }
+ b->le_len = le_len;
+ band_stack.add(b);
+ res = b;
+ return lp;
+}
+
+const char *unpacker::attr_definitions::parseNumeral(const char *lp, int &res)
+{
+ bool sgn = false;
+ if (*lp == '0')
+ {
+ res = 0;
+ return lp + 1;
+ } // special case '0'
+ if (*lp == '-')
+ {
+ sgn = true;
+ lp++;
+ }
+ const char *dp = lp;
+ int con = 0;
+ while (*dp >= '0' && *dp <= '9')
+ {
+ int con0 = con;
+ con *= 10;
+ con += (*dp++) - '0';
+ if (con <= con0)
+ {
+ con = -1;
+ break;
+ } // numeral overflow
+ }
+ if (lp == dp)
+ {
+ unpack_abort("missing numeral in layout");
+ }
+ lp = dp;
+ if (con < 0 && !(sgn && con == -con))
+ {
+ // (Portability note: Misses the error if int is not 32 bits.)
+ unpack_abort("numeral overflow");
+ }
+ if (sgn)
+ con = -con;
+ res = con;
+ return lp;
+}
+
+band **unpacker::attr_definitions::popBody(int bs_base)
+{
+ // Return everything that was pushed, as a nullptr-terminated pointer array.
+ int bs_limit = band_stack.length();
+ if (bs_base == bs_limit)
+ {
+ return no_bands;
+ }
+ else
+ {
+ int nb = bs_limit - bs_base;
+ band **res = U_NEW(band *, add_size(nb, 1));
+ for (int i = 0; i < nb; i++)
+ {
+ band *b = (band *)band_stack.get(bs_base + i);
+ res[i] = b;
+ }
+ band_stack.popTo(bs_base);
+ return res;
+ }
+}
+
+const char *unpacker::attr_definitions::parseLayout(const char *lp, band **&res, int curCble)
+{
+ int bs_base = band_stack.length();
+ bool top_level = (bs_base == 0);
+ band *b;
+ enum
+ {
+ can_be_signed = true
+ }; // optional arg to parseIntLayout
+
+ for (bool done = false; !done;)
+ {
+ switch (*lp++)
+ {
+ case 'B':
+ case 'H':
+ case 'I':
+ case 'V': // unsigned_int
+ case 'S': // signed_int
+ --lp; // reparse
+ case 'F':
+ lp = parseIntLayout(lp, b, EK_INT);
+ break;
+ case 'P':
+ {
+ int le_bci = EK_BCI;
+ if (*lp == 'O')
+ {
+ ++lp;
+ le_bci = EK_BCID;
+ }
+ assert(*lp != 'S'); // no PSH, etc.
+ lp = parseIntLayout(lp, b, EK_INT);
+ b->le_bci = le_bci;
+ if (le_bci == EK_BCI)
+ b->defc = coding::findBySpec(BCI5_spec);
+ else
+ b->defc = coding::findBySpec(BRANCH5_spec);
+ }
+ break;
+ case 'O':
+ lp = parseIntLayout(lp, b, EK_INT, can_be_signed);
+ b->le_bci = EK_BCO;
+ b->defc = coding::findBySpec(BRANCH5_spec);
+ break;
+ case 'N': // replication: 'N' uint32_t '[' elem ... ']'
+ lp = parseIntLayout(lp, b, EK_REPL);
+ assert(*lp == '[');
+ ++lp;
+ lp = parseLayout(lp, b->le_body, curCble);
+ break;
+ case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
+ lp = parseIntLayout(lp, b, EK_UN, can_be_signed);
+ {
+ int union_base = band_stack.length();
+ for (;;)
+ { // for each case
+ band &k_case = *U_NEW(band, 1);
+ band_stack.add(&k_case);
+ k_case.le_kind = EK_CASE;
+ k_case.bn = bands_made++;
+ if (*lp++ != '(')
+ {
+ unpack_abort("bad union case");
+ return "";
+ }
+ if (*lp++ != ')')
+ {
+ --lp; // reparse
+ // Read some case values. (Use band_stack for temp. storage.)
+ int case_base = band_stack.length();
+ for (;;)
+ {
+ int caseval = 0;
+ lp = parseNumeral(lp, caseval);
+ band_stack.add((void *)(size_t)caseval);
+ if (*lp == '-')
+ {
+ // new in version 160, allow (1-5) for (1,2,3,4,5)
+ if (u->majver < JAVA6_PACKAGE_MAJOR_VERSION)
+ {
+ unpack_abort(
+ "bad range in union case label (old archive format)");
+ return "";
+ }
+ int caselimit = caseval;
+ lp++;
+ lp = parseNumeral(lp, caselimit);
+ if (caseval >= caselimit ||
+ (uint32_t)(caselimit - caseval) > 0x10000)
+ {
+ // Note: 0x10000 is arbitrary implementation restriction.
+ // We can remove it later if it's important to.
+ unpack_abort("bad range in union case label");
+ }
+ for (;;)
+ {
+ ++caseval;
+ band_stack.add((void *)(size_t)caseval);
+ if (caseval == caselimit)
+ break;
+ }
+ }
+ if (*lp != ',')
+ break;
+ lp++;
+ }
+ if (*lp++ != ')')
+ {
+ unpack_abort("bad case label");
+ }
+ // save away the case labels
+ int ntags = band_stack.length() - case_base;
+ int *tags = U_NEW(int, add_size(ntags, 1));
+ k_case.le_casetags = tags;
+ *tags++ = ntags;
+ for (int i = 0; i < ntags; i++)
+ {
+ *tags++ = ptrlowbits(band_stack.get(case_base + i));
+ }
+ band_stack.popTo(case_base);
+ }
+ // Got le_casetags. Now grab the body.
+ assert(*lp == '[');
+ ++lp;
+ lp = parseLayout(lp, k_case.le_body, curCble);
+ if (k_case.le_casetags == nullptr)
+ break; // done
+ }
+ b->le_body = popBody(union_base);
+ }
+ break;
+ case '(': // call: '(' -?NN* ')'
+ {
+ band &call = *U_NEW(band, 1);
+ band_stack.add(&call);
+ call.le_kind = EK_CALL;
+ call.bn = bands_made++;
+ call.le_body = U_NEW(band *, 2); // fill in later
+ int call_num = 0;
+ lp = parseNumeral(lp, call_num);
+ call.le_back = (call_num <= 0);
+ call_num += curCble; // numeral is self-relative offset
+ call.le_len = call_num; // use le_len as scratch
+ calls_to_link.add(&call);
+ if (*lp++ != ')')
+ {
+ unpack_abort("bad call label");
+ }
+ }
+ break;
+ case 'K': // reference_type: constant_ref
+ case 'R': // reference_type: schema_ref
+ {
+ int ixTag = CONSTANT_None;
+ if (lp[-1] == 'K')
+ {
+ switch (*lp++)
+ {
+ case 'I':
+ ixTag = CONSTANT_Integer;
+ break;
+ case 'J':
+ ixTag = CONSTANT_Long;
+ break;
+ case 'F':
+ ixTag = CONSTANT_Float;
+ break;
+ case 'D':
+ ixTag = CONSTANT_Double;
+ break;
+ case 'S':
+ ixTag = CONSTANT_String;
+ break;
+ case 'Q':
+ ixTag = CONSTANT_Literal;
+ break;
+ }
+ }
+ else
+ {
+ switch (*lp++)
+ {
+ case 'C':
+ ixTag = CONSTANT_Class;
+ break;
+ case 'S':
+ ixTag = CONSTANT_Signature;
+ break;
+ case 'D':
+ ixTag = CONSTANT_NameandType;
+ break;
+ case 'F':
+ ixTag = CONSTANT_Fieldref;
+ break;
+ case 'M':
+ ixTag = CONSTANT_Methodref;
+ break;
+ case 'I':
+ ixTag = CONSTANT_InterfaceMethodref;
+ break;
+ case 'U':
+ ixTag = CONSTANT_Utf8;
+ break; // utf8_ref
+ case 'Q':
+ ixTag = CONSTANT_All;
+ break; // untyped_ref
+ }
+ }
+ if (ixTag == CONSTANT_None)
+ {
+ unpack_abort("bad reference layout");
+ break;
+ }
+ bool nullOK = false;
+ if (*lp == 'N')
+ {
+ nullOK = true;
+ lp++;
+ }
+ lp = parseIntLayout(lp, b, EK_REF);
+ b->defc = coding::findBySpec(UNSIGNED5_spec);
+ b->initRef(ixTag, nullOK);
+ }
+ break;
+ case '[':
+ {
+ // [callable1][callable2]...
+ if (!top_level)
+ {
+ unpack_abort("bad nested callable");
+ break;
+ }
+ curCble += 1;
+ band &cble = *U_NEW(band, 1);
+ band_stack.add(&cble);
+ cble.le_kind = EK_CBLE;
+ cble.bn = bands_made++;
+ lp = parseLayout(lp, cble.le_body, curCble);
+ }
+ break;
+ case ']':
+ // Hit a closing brace. This ends whatever body we were in.
+ done = true;
+ break;
+ case '\0':
+ // Hit a nullptr. Also ends the (top-level) body.
+ --lp; // back up, so caller can see the nullptr also
+ done = true;
+ break;
+ default:
+ unpack_abort("bad layout");
+ }
+ }
+
+ // Return the accumulated bands:
+ res = popBody(bs_base);
+ return lp;
+}
+
+void unpacker::read_attr_defs()
+{
+ int i;
+
+ // Tell each AD which attrc it is and where its fixed flags are:
+ attr_defs[ATTR_CONTEXT_CLASS].attrc = ATTR_CONTEXT_CLASS;
+ attr_defs[ATTR_CONTEXT_CLASS].xxx_flags_hi_bn = e_class_flags_hi;
+ attr_defs[ATTR_CONTEXT_FIELD].attrc = ATTR_CONTEXT_FIELD;
+ attr_defs[ATTR_CONTEXT_FIELD].xxx_flags_hi_bn = e_field_flags_hi;
+ attr_defs[ATTR_CONTEXT_METHOD].attrc = ATTR_CONTEXT_METHOD;
+ attr_defs[ATTR_CONTEXT_METHOD].xxx_flags_hi_bn = e_method_flags_hi;
+ attr_defs[ATTR_CONTEXT_CODE].attrc = ATTR_CONTEXT_CODE;
+ attr_defs[ATTR_CONTEXT_CODE].xxx_flags_hi_bn = e_code_flags_hi;
+
+ // Decide whether bands for the optional high flag words are present.
+ attr_defs[ATTR_CONTEXT_CLASS]
+ .setHaveLongFlags((archive_options & AO_HAVE_CLASS_FLAGS_HI) != 0);
+ attr_defs[ATTR_CONTEXT_FIELD]
+ .setHaveLongFlags((archive_options & AO_HAVE_FIELD_FLAGS_HI) != 0);
+ attr_defs[ATTR_CONTEXT_METHOD]
+ .setHaveLongFlags((archive_options & AO_HAVE_METHOD_FLAGS_HI) != 0);
+ attr_defs[ATTR_CONTEXT_CODE]
+ .setHaveLongFlags((archive_options & AO_HAVE_CODE_FLAGS_HI) != 0);
+
+ // Set up built-in attrs.
+ // (The simple ones are hard-coded. The metadata layouts are not.)
+ const char *md_layout = (
+// parameter annotations:
+#define MDL0 "[NB[(1)]]"
+ MDL0
+// annotations:
+#define MDL1 \
+ "[NH[(1)]]" \
+ "[RSHNH[RUH(1)]]"
+ MDL1
+ // member_value:
+ "[TB"
+ "(66,67,73,83,90)[KIH]"
+ "(68)[KDH]"
+ "(70)[KFH]"
+ "(74)[KJH]"
+ "(99)[RSH]"
+ "(101)[RSHRUH]"
+ "(115)[RUH]"
+ "(91)[NH[(0)]]"
+ "(64)["
+ // nested annotation:
+ "RSH"
+ "NH[RUH(0)]"
+ "]"
+ "()[]"
+ "]");
+
+ const char *md_layout_P = md_layout;
+ const char *md_layout_A = md_layout + strlen(MDL0);
+ const char *md_layout_V = md_layout + strlen(MDL0 MDL1);
+ assert(0 == strncmp(&md_layout_A[-3], ")]][", 4));
+ assert(0 == strncmp(&md_layout_V[-3], ")]][", 4));
+
+ for (i = 0; i < ATTR_CONTEXT_LIMIT; i++)
+ {
+ attr_definitions &ad = attr_defs[i];
+ ad.defineLayout(X_ATTR_RuntimeVisibleAnnotations, "RuntimeVisibleAnnotations",
+ md_layout_A);
+ ad.defineLayout(X_ATTR_RuntimeInvisibleAnnotations, "RuntimeInvisibleAnnotations",
+ md_layout_A);
+ if (i != ATTR_CONTEXT_METHOD)
+ continue;
+ ad.defineLayout(METHOD_ATTR_RuntimeVisibleParameterAnnotations,
+ "RuntimeVisibleParameterAnnotations", md_layout_P);
+ ad.defineLayout(METHOD_ATTR_RuntimeInvisibleParameterAnnotations,
+ "RuntimeInvisibleParameterAnnotations", md_layout_P);
+ ad.defineLayout(METHOD_ATTR_AnnotationDefault, "AnnotationDefault", md_layout_V);
+ }
+
+ attr_definition_headers.readData(attr_definition_count);
+ attr_definition_name.readData(attr_definition_count);
+ attr_definition_layout.readData(attr_definition_count);
+
+// Initialize correct predef bits, to distinguish predefs from new defs.
+#define ORBIT(n, s) | ((uint64_t)1 << n)
+ attr_defs[ATTR_CONTEXT_CLASS].predef = (0 X_ATTR_DO(ORBIT) CLASS_ATTR_DO(ORBIT));
+ attr_defs[ATTR_CONTEXT_FIELD].predef = (0 X_ATTR_DO(ORBIT) FIELD_ATTR_DO(ORBIT));
+ attr_defs[ATTR_CONTEXT_METHOD].predef = (0 X_ATTR_DO(ORBIT) METHOD_ATTR_DO(ORBIT));
+ attr_defs[ATTR_CONTEXT_CODE].predef = (0 O_ATTR_DO(ORBIT) CODE_ATTR_DO(ORBIT));
+#undef ORBIT
+ // Clear out the redef bits, folding them back into predef.
+ for (i = 0; i < ATTR_CONTEXT_LIMIT; i++)
+ {
+ attr_defs[i].predef |= attr_defs[i].redef;
+ attr_defs[i].redef = 0;
+ }
+
+ // Now read the transmitted locally defined attrs.
+ // This will set redef bits again.
+ for (i = 0; i < attr_definition_count; i++)
+ {
+ int header = attr_definition_headers.getByte();
+ int attrc = ADH_BYTE_CONTEXT(header);
+ int idx = ADH_BYTE_INDEX(header);
+ entry *name = attr_definition_name.getRef();
+ entry *layout = attr_definition_layout.getRef();
+ attr_defs[attrc].defineLayout(idx, name, layout->value.b.strval());
+ }
+}
+
+#define NO_ENTRY_YET ((entry *)-1)
+
+static bool isDigitString(bytes &x, int beg, int end)
+{
+ if (beg == end)
+ return false; // nullptr string
+ byte *xptr = x.ptr;
+ for (int i = beg; i < end; i++)
+ {
+ char ch = xptr[i];
+ if (!(ch >= '0' && ch <= '9'))
+ return false;
+ }
+ return true;
+}
+
+enum
+{ // constants for parsing class names
+ SLASH_MIN = '.',
+ SLASH_MAX = '/',
+ DOLLAR_MIN = 0,
+ DOLLAR_MAX = '-'};
+
+static int lastIndexOf(int chmin, int chmax, bytes &x, int pos)
+{
+ byte *ptr = x.ptr;
+ for (byte *cp = ptr + pos; --cp >= ptr;)
+ {
+ assert(x.inBounds(cp));
+ if (*cp >= chmin && *cp <= chmax)
+ return (int)(cp - ptr);
+ }
+ return -1;
+}
+
+inner_class *constant_pool::getIC(entry *inner)
+{
+ if (inner == nullptr)
+ return nullptr;
+ assert(inner->tag == CONSTANT_Class);
+ if (inner->inord == NO_INORD)
+ return nullptr;
+ inner_class *ic = ic_index[inner->inord];
+ assert(ic == nullptr || ic->inner == inner);
+ return ic;
+}
+
+inner_class *constant_pool::getFirstChildIC(entry *outer)
+{
+ if (outer == nullptr)
+ return nullptr;
+ assert(outer->tag == CONSTANT_Class);
+ if (outer->inord == NO_INORD)
+ return nullptr;
+ inner_class *ic = ic_child_index[outer->inord];
+ assert(ic == nullptr || ic->outer == outer);
+ return ic;
+}
+
+inner_class *constant_pool::getNextChildIC(inner_class *child)
+{
+ inner_class *ic = child->next_sibling;
+ assert(ic == nullptr || ic->outer == child->outer);
+ return ic;
+}
+
+void unpacker::read_ics()
+{
+ int i;
+ int index_size = cp.tag_count[CONSTANT_Class];
+ inner_class **ic_index = U_NEW(inner_class *, index_size);
+ inner_class **ic_child_index = U_NEW(inner_class *, index_size);
+ cp.ic_index = ic_index;
+ cp.ic_child_index = ic_child_index;
+ ics = U_NEW(inner_class, ic_count);
+ ic_this_class.readData(ic_count);
+ ic_flags.readData(ic_count);
+ // Scan flags to get count of long-form bands.
+ int long_forms = 0;
+ for (i = 0; i < ic_count; i++)
+ {
+ int flags = ic_flags.getInt(); // may be long form!
+ if ((flags & ACC_IC_LONG_FORM) != 0)
+ {
+ long_forms += 1;
+ ics[i].name = NO_ENTRY_YET;
+ }
+ flags &= ~ACC_IC_LONG_FORM;
+ entry *inner = ic_this_class.getRef();
+ uint32_t inord = inner->inord;
+ assert(inord < (uint32_t)cp.tag_count[CONSTANT_Class]);
+ if (ic_index[inord] != nullptr)
+ {
+ unpack_abort("identical inner class");
+ break;
+ }
+ ic_index[inord] = &ics[i];
+ ics[i].inner = inner;
+ ics[i].flags = flags;
+ assert(cp.getIC(inner) == &ics[i]);
+ }
+ // ic_this_class.done();
+ // ic_flags.done();
+ ic_outer_class.readData(long_forms);
+ ic_name.readData(long_forms);
+ for (i = 0; i < ic_count; i++)
+ {
+ if (ics[i].name == NO_ENTRY_YET)
+ {
+ // Long form.
+ ics[i].outer = ic_outer_class.getRefN();
+ ics[i].name = ic_name.getRefN();
+ }
+ else
+ {
+ // Fill in outer and name based on inner.
+ bytes &n = ics[i].inner->value.b;
+ bytes pkgOuter;
+ bytes number;
+ bytes name;
+ // Parse n into pkgOuter and name (and number).
+ int dollar1, dollar2; // pointers to $ in the pattern
+ // parse n = (<pkg>/)*<outer>($<number>)?($<name>)?
+ int nlen = (int)n.len;
+ int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, nlen) + 1;
+ dollar2 = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, n, nlen);
+ if (dollar2 < 0)
+ {
+ unpack_abort();
+ }
+ assert(dollar2 >= pkglen);
+ if (isDigitString(n, dollar2 + 1, nlen))
+ {
+ // n = (<pkg>/)*<outer>$<number>
+ number = n.slice(dollar2 + 1, nlen);
+ name.set(nullptr, 0);
+ dollar1 = dollar2;
+ }
+ else if (pkglen < (dollar1 = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, n, dollar2 - 1)) &&
+ isDigitString(n, dollar1 + 1, dollar2))
+ {
+ // n = (<pkg>/)*<outer>$<number>$<name>
+ number = n.slice(dollar1 + 1, dollar2);
+ name = n.slice(dollar2 + 1, nlen);
+ }
+ else
+ {
+ // n = (<pkg>/)*<outer>$<name>
+ dollar1 = dollar2;
+ number.set(nullptr, 0);
+ name = n.slice(dollar2 + 1, nlen);
+ }
+ if (number.ptr == nullptr)
+ pkgOuter = n.slice(0, dollar1);
+ else
+ pkgOuter.set(nullptr, 0);
+
+ if (pkgOuter.ptr != nullptr)
+ ics[i].outer = cp.ensureClass(pkgOuter);
+
+ if (name.ptr != nullptr)
+ ics[i].name = cp.ensureUtf8(name);
+ }
+
+ // update child/sibling list
+ if (ics[i].outer != nullptr)
+ {
+ uint32_t outord = ics[i].outer->inord;
+ if (outord != NO_INORD)
+ {
+ assert(outord < (uint32_t)cp.tag_count[CONSTANT_Class]);
+ ics[i].next_sibling = ic_child_index[outord];
+ ic_child_index[outord] = &ics[i];
+ }
+ }
+ }
+ // ic_outer_class.done();
+ // ic_name.done();
+}
+
+void unpacker::read_classes()
+{
+ class_this.readData(class_count);
+ class_super.readData(class_count);
+ class_interface_count.readData(class_count);
+ class_interface.readData(class_interface_count.getIntTotal());
+
+#if 0
+ int i;
+ // Make a little mark on super-classes.
+ for (i = 0; i < class_count; i++) {
+ entry* e = class_super.getRefN();
+ if (e != nullptr) e->bits |= entry::EB_SUPER;
+ }
+ class_super.rewind();
+#endif
+
+ // Members.
+ class_field_count.readData(class_count);
+ class_method_count.readData(class_count);
+
+ int field_count = class_field_count.getIntTotal();
+ int method_count = class_method_count.getIntTotal();
+
+ field_descr.readData(field_count);
+ read_attrs(ATTR_CONTEXT_FIELD, field_count);
+ method_descr.readData(method_count);
+ read_attrs(ATTR_CONTEXT_METHOD, method_count);
+ read_attrs(ATTR_CONTEXT_CLASS, class_count);
+ read_code_headers();
+}
+
+int unpacker::attr_definitions::predefCount(uint32_t idx)
+{
+ return isPredefined(idx) ? flag_count[idx] : 0;
+}
+
+void unpacker::read_attrs(int attrc, int obj_count)
+{
+ attr_definitions &ad = attr_defs[attrc];
+ assert(ad.attrc == attrc);
+
+ int i, idx, count;
+
+ bool haveLongFlags = ad.haveLongFlags();
+
+ band &xxx_flags_hi = ad.xxx_flags_hi();
+ if (haveLongFlags)
+ xxx_flags_hi.readData(obj_count);
+
+ band &xxx_flags_lo = ad.xxx_flags_lo();
+ xxx_flags_lo.readData(obj_count);
+
+ // pre-scan flags, counting occurrences of each index bit
+ uint64_t indexMask = ad.flagIndexMask(); // which flag bits are index bits?
+ for (i = 0; i < obj_count; i++)
+ {
+ uint64_t indexBits = xxx_flags_hi.getLong(xxx_flags_lo, haveLongFlags);
+ if ((indexBits & ~indexMask) > (ushort) - 1)
+ {
+ unpack_abort("undefined attribute flag bit");
+ return;
+ }
+ indexBits &= indexMask; // ignore classfile flag bits
+ for (idx = 0; indexBits != 0; idx++, indexBits >>= 1)
+ {
+ ad.flag_count[idx] += (int)(indexBits & 1);
+ }
+ }
+ // we'll scan these again later for output:
+ xxx_flags_lo.rewind();
+ xxx_flags_hi.rewind();
+
+ band &xxx_attr_count = ad.xxx_attr_count();
+ // There is one count element for each 1<<16 bit set in flags:
+ xxx_attr_count.readData(ad.predefCount(X_ATTR_OVERFLOW));
+
+ band &xxx_attr_indexes = ad.xxx_attr_indexes();
+ int overflowIndexCount = xxx_attr_count.getIntTotal();
+ xxx_attr_indexes.readData(overflowIndexCount);
+ // pre-scan attr indexes, counting occurrences of each value
+ for (i = 0; i < overflowIndexCount; i++)
+ {
+ idx = xxx_attr_indexes.getInt();
+ if (!ad.isIndex(idx))
+ {
+ unpack_abort("attribute index out of bounds");
+ return;
+ }
+ ad.getCount(idx) += 1;
+ }
+ xxx_attr_indexes.rewind(); // we'll scan it again later for output
+
+ // We will need a backward call count for each used backward callable.
+ int backwardCounts = 0;
+ for (idx = 0; idx < ad.layouts.length(); idx++)
+ {
+ layout_definition *lo = ad.getLayout(idx);
+ if (lo != nullptr && ad.getCount(idx) != 0)
+ {
+ // Build the bands lazily, only when they are used.
+ band **bands = ad.buildBands(lo);
+ if (lo->hasCallables())
+ {
+ for (i = 0; bands[i] != nullptr; i++)
+ {
+ if (bands[i]->le_back)
+ {
+ assert(bands[i]->le_kind == EK_CBLE);
+ backwardCounts += 1;
+ }
+ }
+ }
+ }
+ }
+ ad.xxx_attr_calls().readData(backwardCounts);
+
+ // Read built-in bands.
+ // Mostly, these are hand-coded equivalents to readBandData().
+ switch (attrc)
+ {
+ case ATTR_CONTEXT_CLASS:
+
+ count = ad.predefCount(CLASS_ATTR_SourceFile);
+ class_SourceFile_RUN.readData(count);
+
+ count = ad.predefCount(CLASS_ATTR_EnclosingMethod);
+ class_EnclosingMethod_RC.readData(count);
+ class_EnclosingMethod_RDN.readData(count);
+
+ count = ad.predefCount(X_ATTR_Signature);
+ class_Signature_RS.readData(count);
+
+ ad.readBandData(X_ATTR_RuntimeVisibleAnnotations);
+ ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations);
+
+ count = ad.predefCount(CLASS_ATTR_InnerClasses);
+ class_InnerClasses_N.readData(count);
+
+ count = class_InnerClasses_N.getIntTotal();
+ class_InnerClasses_RC.readData(count);
+ class_InnerClasses_F.readData(count);
+
+ // Drop remaining columns wherever flags are zero:
+ count -= class_InnerClasses_F.getIntCount(0);
+ class_InnerClasses_outer_RCN.readData(count);
+ class_InnerClasses_name_RUN.readData(count);
+
+ count = ad.predefCount(CLASS_ATTR_ClassFile_version);
+ class_ClassFile_version_minor_H.readData(count);
+ class_ClassFile_version_major_H.readData(count);
+ break;
+
+ case ATTR_CONTEXT_FIELD:
+
+ count = ad.predefCount(FIELD_ATTR_ConstantValue);
+ field_ConstantValue_KQ.readData(count);
+
+ count = ad.predefCount(X_ATTR_Signature);
+ field_Signature_RS.readData(count);
+
+ ad.readBandData(X_ATTR_RuntimeVisibleAnnotations);
+ ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations);
+ break;
+
+ case ATTR_CONTEXT_METHOD:
+
+ code_count = ad.predefCount(METHOD_ATTR_Code);
+ // Code attrs are handled very specially below...
+
+ count = ad.predefCount(METHOD_ATTR_Exceptions);
+ method_Exceptions_N.readData(count);
+ count = method_Exceptions_N.getIntTotal();
+ method_Exceptions_RC.readData(count);
+
+ count = ad.predefCount(X_ATTR_Signature);
+ method_Signature_RS.readData(count);
+
+ ad.readBandData(X_ATTR_RuntimeVisibleAnnotations);
+ ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations);
+ ad.readBandData(METHOD_ATTR_RuntimeVisibleParameterAnnotations);
+ ad.readBandData(METHOD_ATTR_RuntimeInvisibleParameterAnnotations);
+ ad.readBandData(METHOD_ATTR_AnnotationDefault);
+ break;
+
+ case ATTR_CONTEXT_CODE:
+ // (keep this code aligned with its brother in unpacker::write_attrs)
+ count = ad.predefCount(CODE_ATTR_StackMapTable);
+ // disable this feature in old archives!
+ if (count != 0 && majver < JAVA6_PACKAGE_MAJOR_VERSION)
+ {
+ unpack_abort("undefined StackMapTable attribute (old archive format)");
+ return;
+ }
+ code_StackMapTable_N.readData(count);
+ count = code_StackMapTable_N.getIntTotal();
+ code_StackMapTable_frame_T.readData(count);
+ // the rest of it depends in a complicated way on frame tags
+ {
+ int fat_frame_count = 0;
+ int offset_count = 0;
+ int type_count = 0;
+ for (int k = 0; k < count; k++)
+ {
+ int tag = code_StackMapTable_frame_T.getByte();
+ if (tag <= 127)
+ {
+ // (64-127) [(2)]
+ if (tag >= 64)
+ type_count++;
+ }
+ else if (tag <= 251)
+ {
+ // (247) [(1)(2)]
+ // (248-251) [(1)]
+ if (tag >= 247)
+ offset_count++;
+ if (tag == 247)
+ type_count++;
+ }
+ else if (tag <= 254)
+ {
+ // (252) [(1)(2)]
+ // (253) [(1)(2)(2)]
+ // (254) [(1)(2)(2)(2)]
+ offset_count++;
+ type_count += (tag - 251);
+ }
+ else
+ {
+ // (255) [(1)NH[(2)]NH[(2)]]
+ fat_frame_count++;
+ }
+ }
+
+ // done pre-scanning frame tags:
+ code_StackMapTable_frame_T.rewind();
+
+ // deal completely with fat frames:
+ offset_count += fat_frame_count;
+ code_StackMapTable_local_N.readData(fat_frame_count);
+ type_count += code_StackMapTable_local_N.getIntTotal();
+ code_StackMapTable_stack_N.readData(fat_frame_count);
+ type_count += code_StackMapTable_stack_N.getIntTotal();
+ // read the rest:
+ code_StackMapTable_offset.readData(offset_count);
+ code_StackMapTable_T.readData(type_count);
+ // (7) [RCH]
+ count = code_StackMapTable_T.getIntCount(7);
+ code_StackMapTable_RC.readData(count);
+ // (8) [PH]
+ count = code_StackMapTable_T.getIntCount(8);
+ code_StackMapTable_P.readData(count);
+ }
+
+ count = ad.predefCount(CODE_ATTR_LineNumberTable);
+ code_LineNumberTable_N.readData(count);
+ count = code_LineNumberTable_N.getIntTotal();
+ code_LineNumberTable_bci_P.readData(count);
+ code_LineNumberTable_line.readData(count);
+
+ count = ad.predefCount(CODE_ATTR_LocalVariableTable);
+ code_LocalVariableTable_N.readData(count);
+ count = code_LocalVariableTable_N.getIntTotal();
+ code_LocalVariableTable_bci_P.readData(count);
+ code_LocalVariableTable_span_O.readData(count);
+ code_LocalVariableTable_name_RU.readData(count);
+ code_LocalVariableTable_type_RS.readData(count);
+ code_LocalVariableTable_slot.readData(count);
+
+ count = ad.predefCount(CODE_ATTR_LocalVariableTypeTable);
+ code_LocalVariableTypeTable_N.readData(count);
+ count = code_LocalVariableTypeTable_N.getIntTotal();
+ code_LocalVariableTypeTable_bci_P.readData(count);
+ code_LocalVariableTypeTable_span_O.readData(count);
+ code_LocalVariableTypeTable_name_RU.readData(count);
+ code_LocalVariableTypeTable_type_RS.readData(count);
+ code_LocalVariableTypeTable_slot.readData(count);
+ break;
+ }
+
+ // Read compressor-defined bands.
+ for (idx = 0; idx < ad.layouts.length(); idx++)
+ {
+ if (ad.getLayout(idx) == nullptr)
+ continue; // none at this fixed index <32
+ if (idx < (int)ad.flag_limit && ad.isPredefined(idx))
+ continue; // already handled
+ if (ad.getCount(idx) == 0)
+ continue; // no attributes of this type (then why transmit layouts?)
+ ad.readBandData(idx);
+ }
+}
+
+void unpacker::attr_definitions::readBandData(int idx)
+{
+ int j;
+ uint32_t count = getCount(idx);
+ if (count == 0)
+ return;
+ layout_definition *lo = getLayout(idx);
+ bool hasCallables = lo->hasCallables();
+ band **bands = lo->bands();
+ if (!hasCallables)
+ {
+ // Read through the rest of the bands in a regular way.
+ readBandData(bands, count);
+ }
+ else
+ {
+ // Deal with the callables.
+ // First set up the forward entry count for each callable.
+ // This is stored on band::length of the callable.
+ bands[0]->expectMoreLength(count);
+ for (j = 0; bands[j] != nullptr; j++)
+ {
+ band &j_cble = *bands[j];
+ assert(j_cble.le_kind == EK_CBLE);
+ if (j_cble.le_back)
+ {
+ // Add in the predicted effects of backward calls, too.
+ int back_calls = xxx_attr_calls().getInt();
+ j_cble.expectMoreLength(back_calls);
+ // In a moment, more forward calls may increment j_cble.length.
+ }
+ }
+ // Now consult whichever callables have non-zero entry counts.
+ readBandData(bands, (uint32_t) - 1);
+ }
+}
+
+// Recursive helper to the previous function:
+void unpacker::attr_definitions::readBandData(band **body, uint32_t count)
+{
+ int j, k;
+ for (j = 0; body[j] != nullptr; j++)
+ {
+ band &b = *body[j];
+ if (b.defc != nullptr)
+ {
+ // It has data, so read it.
+ b.readData(count);
+ }
+ switch (b.le_kind)
+ {
+ case EK_REPL:
+ {
+ int reps = b.getIntTotal();
+ readBandData(b.le_body, reps);
+ }
+ break;
+ case EK_UN:
+ {
+ int remaining = count;
+ for (k = 0; b.le_body[k] != nullptr; k++)
+ {
+ band &k_case = *b.le_body[k];
+ int k_count = 0;
+ if (k_case.le_casetags == nullptr)
+ {
+ k_count = remaining; // last (empty) case
+ }
+ else
+ {
+ int *tags = k_case.le_casetags;
+ int ntags = *tags++; // 1st element is length (why not?)
+ while (ntags-- > 0)
+ {
+ int tag = *tags++;
+ k_count += b.getIntCount(tag);
+ }
+ }
+ readBandData(k_case.le_body, k_count);
+ remaining -= k_count;
+ }
+ assert(remaining == 0);
+ }
+ break;
+ case EK_CALL:
+ // Push the count forward, if it is not a backward call.
+ if (!b.le_back)
+ {
+ band &cble = *b.le_body[0];
+ assert(cble.le_kind == EK_CBLE);
+ cble.expectMoreLength(count);
+ }
+ break;
+ case EK_CBLE:
+ assert((int)count == -1); // incoming count is meaningless
+ k = b.length;
+ assert(k >= 0);
+ // This is intended and required for non production mode.
+ assert((b.length = -1)); // make it unable to accept more calls now.
+ readBandData(b.le_body, k);
+ break;
+ }
+ }
+}
+
+static inline band **findMatchingCase(int matchTag, band **cases)
+{
+ for (int k = 0; cases[k] != nullptr; k++)
+ {
+ band &k_case = *cases[k];
+ if (k_case.le_casetags != nullptr)
+ {
+ // If it has tags, it must match a tag.
+ int *tags = k_case.le_casetags;
+ int ntags = *tags++; // 1st element is length
+ for (; ntags > 0; ntags--)
+ {
+ int tag = *tags++;
+ if (tag == matchTag)
+ break;
+ }
+ if (ntags == 0)
+ continue; // does not match
+ }
+ return k_case.le_body;
+ }
+ return nullptr;
+}
+
+// write attribute band data:
+void unpacker::putlayout(band **body)
+{
+ int i;
+ int prevBII = -1;
+ int prevBCI = -1;
+ if (body == NULL)
+ {
+ unpack_abort("putlayout: unexpected NULL for body");
+ return;
+ }
+ for (i = 0; body[i] != nullptr; i++)
+ {
+ band &b = *body[i];
+ byte le_kind = b.le_kind;
+
+ // Handle scalar part, if any.
+ int x = 0;
+ entry *e = nullptr;
+ if (b.defc != nullptr)
+ {
+ // It has data, so unparse an element.
+ if (b.ixTag != CONSTANT_None)
+ {
+ assert(le_kind == EK_REF);
+ if (b.ixTag == CONSTANT_Literal)
+ e = b.getRefUsing(cp.getKQIndex());
+ else
+ e = b.getRefN();
+ switch (b.le_len)
+ {
+ case 0:
+ break;
+ case 1:
+ putu1ref(e);
+ break;
+ case 2:
+ putref(e);
+ break;
+ case 4:
+ putu2(0);
+ putref(e);
+ break;
+ default:
+ assert(false);
+ }
+ }
+ else
+ {
+ assert(le_kind == EK_INT || le_kind == EK_REPL || le_kind == EK_UN);
+ x = b.getInt();
+
+ assert(!b.le_bci || prevBCI == (int)to_bci(prevBII));
+ switch (b.le_bci)
+ {
+ case EK_BCI: // PH: transmit R(bci), store bci
+ x = to_bci(prevBII = x);
+ prevBCI = x;
+ break;
+ case EK_BCID: // POH: transmit D(R(bci)), store bci
+ x = to_bci(prevBII += x);
+ prevBCI = x;
+ break;
+ case EK_BCO: // OH: transmit D(R(bci)), store D(bci)
+ x = to_bci(prevBII += x) - prevBCI;
+ prevBCI += x;
+ break;
+ }
+ assert(!b.le_bci || prevBCI == (int)to_bci(prevBII));
+
+ switch (b.le_len)
+ {
+ case 0:
+ break;
+ case 1:
+ putu1(x);
+ break;
+ case 2:
+ putu2(x);
+ break;
+ case 4:
+ putu4(x);
+ break;
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ // Handle subparts, if any.
+ switch (le_kind)
+ {
+ case EK_REPL:
+ // x is the repeat count
+ while (x-- > 0)
+ {
+ putlayout(b.le_body);
+ }
+ break;
+ case EK_UN:
+ // x is the tag
+ putlayout(findMatchingCase(x, b.le_body));
+ break;
+ case EK_CALL:
+ {
+ band &cble = *b.le_body[0];
+ assert(cble.le_kind == EK_CBLE);
+ // FIXME: hit this one
+ // assert(cble.le_len == b.le_len);
+ putlayout(cble.le_body);
+ }
+ break;
+
+ case EK_CBLE:
+ case EK_CASE:
+ assert(false); // should not reach here
+ }
+ }
+}
+
+void unpacker::read_files()
+{
+ file_name.readData(file_count);
+ if ((archive_options & AO_HAVE_FILE_SIZE_HI) != 0)
+ file_size_hi.readData(file_count);
+ file_size_lo.readData(file_count);
+ if ((archive_options & AO_HAVE_FILE_MODTIME) != 0)
+ file_modtime.readData(file_count);
+ int allFiles = file_count + class_count;
+ if ((archive_options & AO_HAVE_FILE_OPTIONS) != 0)
+ {
+ file_options.readData(file_count);
+ // FO_IS_CLASS_STUB might be set, causing overlap between classes and files
+ for (int i = 0; i < file_count; i++)
+ {
+ if ((file_options.getInt() & FO_IS_CLASS_STUB) != 0)
+ {
+ allFiles -= 1; // this one counts as both class and file
+ }
+ }
+ file_options.rewind();
+ }
+ assert((default_file_options & FO_IS_CLASS_STUB) == 0);
+ files_remaining = allFiles;
+}
+
+void unpacker::get_code_header(int &max_stack, int &max_na_locals, int &handler_count,
+ int &cflags)
+{
+ int sc = code_headers.getByte();
+ if (sc == 0)
+ {
+ max_stack = max_na_locals = handler_count = cflags = -1;
+ return;
+ }
+ // Short code header is the usual case:
+ int nh;
+ int mod;
+ if (sc < 1 + 12 * 12)
+ {
+ sc -= 1;
+ nh = 0;
+ mod = 12;
+ }
+ else if (sc < 1 + 12 * 12 + 8 * 8)
+ {
+ sc -= 1 + 12 * 12;
+ nh = 1;
+ mod = 8;
+ }
+ else
+ {
+ assert(sc < 1 + 12 * 12 + 8 * 8 + 7 * 7);
+ sc -= 1 + 12 * 12 + 8 * 8;
+ nh = 2;
+ mod = 7;
+ }
+ max_stack = sc % mod;
+ max_na_locals = sc / mod; // caller must add static, siglen
+ handler_count = nh;
+ if ((archive_options & AO_HAVE_ALL_CODE_FLAGS) != 0)
+ cflags = -1;
+ else
+ cflags = 0; // this one has no attributes
+}
+
+// Cf. PackageReader.readCodeHeaders
+void unpacker::read_code_headers()
+{
+ code_headers.readData(code_count);
+ int totalHandlerCount = 0;
+ int totalFlagsCount = 0;
+ for (int i = 0; i < code_count; i++)
+ {
+ int max_stack, max_locals, handler_count, cflags;
+ get_code_header(max_stack, max_locals, handler_count, cflags);
+ if (max_stack < 0)
+ code_max_stack.expectMoreLength(1);
+ if (max_locals < 0)
+ code_max_na_locals.expectMoreLength(1);
+ if (handler_count < 0)
+ code_handler_count.expectMoreLength(1);
+ else
+ totalHandlerCount += handler_count;
+ if (cflags < 0)
+ totalFlagsCount += 1;
+ }
+ code_headers.rewind(); // replay later during writing
+
+ code_max_stack.readData();
+ code_max_na_locals.readData();
+ code_handler_count.readData();
+ totalHandlerCount += code_handler_count.getIntTotal();
+
+ // Read handler specifications.
+ // Cf. PackageReader.readCodeHandlers.
+ code_handler_start_P.readData(totalHandlerCount);
+ code_handler_end_PO.readData(totalHandlerCount);
+ code_handler_catch_PO.readData(totalHandlerCount);
+ code_handler_class_RCN.readData(totalHandlerCount);
+
+ read_attrs(ATTR_CONTEXT_CODE, totalFlagsCount);
+}
+
+static inline bool is_in_range(uint32_t n, uint32_t min, uint32_t max)
+{
+ return n - min <= max - min; // unsigned arithmetic!
+}
+static inline bool is_field_op(int bc)
+{
+ return is_in_range(bc, bc_getstatic, bc_putfield);
+}
+static inline bool is_invoke_init_op(int bc)
+{
+ return is_in_range(bc, _invokeinit_op, _invokeinit_limit - 1);
+}
+static inline bool is_self_linker_op(int bc)
+{
+ return is_in_range(bc, _self_linker_op, _self_linker_limit - 1);
+}
+static bool is_branch_op(int bc)
+{
+ return is_in_range(bc, bc_ifeq, bc_jsr) || is_in_range(bc, bc_ifnull, bc_jsr_w);
+}
+static bool is_local_slot_op(int bc)
+{
+ return is_in_range(bc, bc_iload, bc_aload) || is_in_range(bc, bc_istore, bc_astore) ||
+ bc == bc_iinc || bc == bc_ret;
+}
+band *unpacker::ref_band_for_op(int bc)
+{
+ switch (bc)
+ {
+ case bc_ildc:
+ case bc_ildc_w:
+ return &bc_intref;
+ case bc_fldc:
+ case bc_fldc_w:
+ return &bc_floatref;
+ case bc_lldc2_w:
+ return &bc_longref;
+ case bc_dldc2_w:
+ return &bc_doubleref;
+ case bc_aldc:
+ case bc_aldc_w:
+ return &bc_stringref;
+ case bc_cldc:
+ case bc_cldc_w:
+ return &bc_classref;
+
+ case bc_getstatic:
+ case bc_putstatic:
+ case bc_getfield:
+ case bc_putfield:
+ return &bc_fieldref;
+
+ case bc_invokevirtual:
+ case bc_invokespecial:
+ case bc_invokestatic:
+ return &bc_methodref;
+ case bc_invokeinterface:
+ return &bc_imethodref;
+
+ case bc_new:
+ case bc_anewarray:
+ case bc_checkcast:
+ case bc_instanceof:
+ case bc_multianewarray:
+ return &bc_classref;
+ }
+ return nullptr;
+}
+
+band *unpacker::ref_band_for_self_op(int bc, bool &isAloadVar, int &origBCVar)
+{
+ if (!is_self_linker_op(bc))
+ return nullptr;
+ int idx = (bc - _self_linker_op);
+ bool isSuper = (idx >= _self_linker_super_flag);
+ if (isSuper)
+ idx -= _self_linker_super_flag;
+ bool isAload = (idx >= _self_linker_aload_flag);
+ if (isAload)
+ idx -= _self_linker_aload_flag;
+ int origBC = _first_linker_op + idx;
+ bool isField = is_field_op(origBC);
+ isAloadVar = isAload;
+ origBCVar = _first_linker_op + idx;
+ if (!isSuper)
+ return isField ? &bc_thisfield : &bc_thismethod;
+ else
+ return isField ? &bc_superfield : &bc_supermethod;
+}
+
+// Cf. PackageReader.readByteCodes
+inline // called exactly once => inline
+ void
+unpacker::read_bcs()
+{
+ // read from bc_codes and bc_case_count
+ fillbytes all_switch_ops;
+ all_switch_ops.init();
+
+ // Read directly from rp/rplimit.
+ // Do this later: bc_codes.readData(...)
+ byte *rp0 = rp;
+
+ band *bc_which;
+ byte *opptr = rp;
+ byte *oplimit = rplimit;
+
+ bool isAload; // passed by ref and then ignored
+ int junkBC; // passed by ref and then ignored
+ for (int k = 0; k < code_count; k++)
+ {
+ // Scan one method:
+ for (;;)
+ {
+ if (opptr + 2 > oplimit)
+ {
+ rp = opptr;
+ ensure_input(2);
+ oplimit = rplimit;
+ rp = rp0; // back up
+ }
+ if (opptr == oplimit)
+ {
+ unpack_abort();
+ }
+ int bc = *opptr++ & 0xFF;
+ bool isWide = false;
+ if (bc == bc_wide)
+ {
+ if (opptr == oplimit)
+ {
+ unpack_abort();
+ }
+ bc = *opptr++ & 0xFF;
+ isWide = true;
+ }
+ // Adjust expectations of various band sizes.
+ switch (bc)
+ {
+ case bc_tableswitch:
+ case bc_lookupswitch:
+ all_switch_ops.addByte(bc);
+ break;
+ case bc_iinc:
+ bc_local.expectMoreLength(1);
+ bc_which = isWide ? &bc_short : &bc_byte;
+ bc_which->expectMoreLength(1);
+ break;
+ case bc_sipush:
+ bc_short.expectMoreLength(1);
+ break;
+ case bc_bipush:
+ bc_byte.expectMoreLength(1);
+ break;
+ case bc_newarray:
+ bc_byte.expectMoreLength(1);
+ break;
+ case bc_multianewarray:
+ assert(ref_band_for_op(bc) == &bc_classref);
+ bc_classref.expectMoreLength(1);
+ bc_byte.expectMoreLength(1);
+ break;
+ case bc_ref_escape:
+ bc_escrefsize.expectMoreLength(1);
+ bc_escref.expectMoreLength(1);
+ break;
+ case bc_byte_escape:
+ bc_escsize.expectMoreLength(1);
+ // bc_escbyte will have to be counted too
+ break;
+ default:
+ if (is_invoke_init_op(bc))
+ {
+ bc_initref.expectMoreLength(1);
+ break;
+ }
+ bc_which = ref_band_for_self_op(bc, isAload, junkBC);
+ if (bc_which != nullptr)
+ {
+ bc_which->expectMoreLength(1);
+ break;
+ }
+ if (is_branch_op(bc))
+ {
+ bc_label.expectMoreLength(1);
+ break;
+ }
+ bc_which = ref_band_for_op(bc);
+ if (bc_which != nullptr)
+ {
+ bc_which->expectMoreLength(1);
+ assert(bc != bc_multianewarray); // handled elsewhere
+ break;
+ }
+ if (is_local_slot_op(bc))
+ {
+ bc_local.expectMoreLength(1);
+ break;
+ }
+ break;
+ case bc_end_marker:
+ // Increment k and test against code_count.
+ goto doneScanningMethod;
+ }
+ }
+ doneScanningMethod:
+ {
+ }
+ }
+
+ // Go through the formality, so we can use it in a regular fashion later:
+ assert(rp == rp0);
+ bc_codes.readData((int)(opptr - rp));
+
+ int i = 0;
+
+ // To size instruction bands correctly, we need info on switches:
+ bc_case_count.readData((int)all_switch_ops.size());
+ for (i = 0; i < (int)all_switch_ops.size(); i++)
+ {
+ int caseCount = bc_case_count.getInt();
+ int bc = all_switch_ops.getByte(i);
+ bc_label.expectMoreLength(1 + caseCount); // default label + cases
+ bc_case_value.expectMoreLength(bc == bc_tableswitch ? 1 : caseCount);
+ }
+ bc_case_count.rewind(); // uses again for output
+
+ all_switch_ops.free();
+
+ for (i = e_bc_case_value; i <= e_bc_escsize; i++)
+ {
+ all_bands[i].readData();
+ }
+
+ // The bc_escbyte band is counted by the immediately previous band.
+ bc_escbyte.readData(bc_escsize.getIntTotal());
+}
+
+void unpacker::read_bands()
+{
+ read_file_header();
+
+ if (cp.nentries == 0)
+ {
+ // read_file_header failed to read a CP, because it copied a JAR.
+ return;
+ }
+
+ // Do this after the file header has been read:
+ check_options();
+
+ read_cp();
+ read_attr_defs();
+ read_ics();
+ read_classes();
+ read_bcs();
+ read_files();
+}
+
+/// CP routines
+
+entry *&constant_pool::hashTabRef(byte tag, bytes &b)
+{
+ uint32_t hash = tag + (int)b.len;
+ for (int i = 0; i < (int)b.len; i++)
+ {
+ hash = hash * 31 + (0xFF & b.ptr[i]);
+ }
+ entry **ht = hashTab;
+ int hlen = hashTabLength;
+ assert((hlen & (hlen - 1)) == 0); // must be power of 2
+ uint32_t hash1 = hash & (hlen - 1); // == hash % hlen
+ uint32_t hash2 = 0; // lazily computed (requires mod op.)
+ int probes = 0;
+ while (ht[hash1] != nullptr)
+ {
+ entry &e = *ht[hash1];
+ if (e.value.b.equals(b) && e.tag == tag)
+ break;
+ if (hash2 == 0)
+ // Note: hash2 must be relatively prime to hlen, hence the "|1".
+ hash2 = (((hash % 499) & (hlen - 1)) | 1);
+ hash1 += hash2;
+ if (hash1 >= (uint32_t)hlen)
+ hash1 -= hlen;
+ assert(hash1 < (uint32_t)hlen);
+ assert(++probes < hlen);
+ }
+ return ht[hash1];
+}
+
+static void insert_extra(entry *e, ptrlist &extras)
+{
+ // This ordering helps implement the Pack200 requirement
+ // of a predictable CP order in the class files produced.
+ e->inord = NO_INORD; // mark as an "extra"
+ extras.add(e);
+ // Note: We will sort the list (by string-name) later.
+}
+
+entry *constant_pool::ensureUtf8(bytes &b)
+{
+ entry *&ix = hashTabRef(CONSTANT_Utf8, b);
+ if (ix != nullptr)
+ return ix;
+ // Make one.
+ if (nentries == maxentries)
+ {
+ unpack_abort("cp utf8 overflow");
+ return &entries[tag_base[CONSTANT_Utf8]]; // return something
+ }
+ entry &e = entries[nentries++];
+ e.tag = CONSTANT_Utf8;
+ u->saveTo(e.value.b, b);
+ assert(&e >= first_extra_entry);
+ insert_extra(&e, tag_extras[CONSTANT_Utf8]);
+ return ix = &e;
+}
+
+entry *constant_pool::ensureClass(bytes &b)
+{
+ entry *&ix = hashTabRef(CONSTANT_Class, b);
+ if (ix != nullptr)
+ return ix;
+ // Make one.
+ if (nentries == maxentries)
+ {
+ unpack_abort("cp class overflow");
+ return &entries[tag_base[CONSTANT_Class]]; // return something
+ }
+ entry &e = entries[nentries++];
+ e.tag = CONSTANT_Class;
+ e.nrefs = 1;
+ e.refs = U_NEW(entry *, 1);
+ ix = &e; // hold my spot in the index
+ entry *utf = ensureUtf8(b);
+ e.refs[0] = utf;
+ e.value.b = utf->value.b;
+ assert(&e >= first_extra_entry);
+ insert_extra(&e, tag_extras[CONSTANT_Class]);
+ return &e;
+}
+
+void constant_pool::expandSignatures()
+{
+ int i;
+ int nsigs = 0;
+ int nreused = 0;
+ int first_sig = tag_base[CONSTANT_Signature];
+ int sig_limit = tag_count[CONSTANT_Signature] + first_sig;
+ fillbytes buf;
+ buf.init(1 << 10);
+ for (i = first_sig; i < sig_limit; i++)
+ {
+ entry &e = entries[i];
+ assert(e.tag == CONSTANT_Signature);
+ int refnum = 0;
+ bytes form = e.refs[refnum++]->asUtf8();
+ buf.empty();
+ for (int j = 0; j < (int)form.len; j++)
+ {
+ int c = form.ptr[j];
+ buf.addByte(c);
+ if (c == 'L')
+ {
+ entry *cls = e.refs[refnum++];
+ buf.append(cls->className()->asUtf8());
+ }
+ }
+ assert(refnum == e.nrefs);
+ bytes &sig = buf.b;
+
+ // try to find a pre-existing Utf8:
+ entry *&e2 = hashTabRef(CONSTANT_Utf8, sig);
+ if (e2 != nullptr)
+ {
+ assert(e2->isUtf8(sig));
+ e.value.b = e2->value.b;
+ e.refs[0] = e2;
+ e.nrefs = 1;
+ nreused++;
+ }
+ else
+ {
+ // there is no other replacement; reuse this CP entry as a Utf8
+ u->saveTo(e.value.b, sig);
+ e.tag = CONSTANT_Utf8;
+ e.nrefs = 0;
+ e2 = &e;
+ }
+ nsigs++;
+ }
+ buf.free();
+
+ // go expunge all references to remaining signatures:
+ for (i = 0; i < (int)nentries; i++)
+ {
+ entry &e = entries[i];
+ for (int j = 0; j < e.nrefs; j++)
+ {
+ entry *&e2 = e.refs[j];
+ if (e2 != nullptr && e2->tag == CONSTANT_Signature)
+ e2 = e2->refs[0];
+ }
+ }
+}
+
+void constant_pool::initMemberIndexes()
+{
+ // This function does NOT refer to any class schema.
+ // It is totally internal to the cpool.
+ int i, j;
+
+ // Get the pre-existing indexes:
+ int nclasses = tag_count[CONSTANT_Class];
+ // entry *classes = tag_base[CONSTANT_Class] + entries; // UNUSED
+ int nfields = tag_count[CONSTANT_Fieldref];
+ entry *fields = tag_base[CONSTANT_Fieldref] + entries;
+ int nmethods = tag_count[CONSTANT_Methodref];
+ entry *methods = tag_base[CONSTANT_Methodref] + entries;
+
+ int *field_counts = T_NEW(int, nclasses);
+ int *method_counts = T_NEW(int, nclasses);
+ cpindex *all_indexes = U_NEW(cpindex, nclasses * 2);
+ entry **field_ix = U_NEW(entry *, add_size(nfields, nclasses));
+ entry **method_ix = U_NEW(entry *, add_size(nmethods, nclasses));
+
+ for (j = 0; j < nfields; j++)
+ {
+ entry &f = fields[j];
+ i = f.memberClass()->inord;
+ assert(i < nclasses);
+ field_counts[i]++;
+ }
+ for (j = 0; j < nmethods; j++)
+ {
+ entry &m = methods[j];
+ i = m.memberClass()->inord;
+ assert(i < nclasses);
+ method_counts[i]++;
+ }
+
+ int fbase = 0, mbase = 0;
+ for (i = 0; i < nclasses; i++)
+ {
+ int fc = field_counts[i];
+ int mc = method_counts[i];
+ all_indexes[i * 2 + 0].init(fc, field_ix + fbase, CONSTANT_Fieldref + SUBINDEX_BIT);
+ all_indexes[i * 2 + 1].init(mc, method_ix + mbase, CONSTANT_Methodref + SUBINDEX_BIT);
+ // reuse field_counts and member_counts as fill pointers:
+ field_counts[i] = fbase;
+ method_counts[i] = mbase;
+ fbase += fc + 1;
+ mbase += mc + 1;
+ // (the +1 leaves a space between every subarray)
+ }
+ assert(fbase == nfields + nclasses);
+ assert(mbase == nmethods + nclasses);
+
+ for (j = 0; j < nfields; j++)
+ {
+ entry &f = fields[j];
+ i = f.memberClass()->inord;
+ field_ix[field_counts[i]++] = &f;
+ }
+ for (j = 0; j < nmethods; j++)
+ {
+ entry &m = methods[j];
+ i = m.memberClass()->inord;
+ method_ix[method_counts[i]++] = &m;
+ }
+
+ member_indexes = all_indexes;
+
+ // Free intermediate buffers.
+ u->free_temps();
+}
+
+void entry::requestOutputIndex(constant_pool &cp, int req)
+{
+ assert(outputIndex <= NOT_REQUESTED); // must not have assigned indexes yet
+ if (tag == CONSTANT_Signature)
+ {
+ ref(0)->requestOutputIndex(cp, req);
+ return;
+ }
+ assert(req == REQUESTED || req == REQUESTED_LDC);
+ if (outputIndex != NOT_REQUESTED)
+ {
+ if (req == REQUESTED_LDC)
+ outputIndex = req; // this kind has precedence
+ return;
+ }
+ outputIndex = req;
+ // assert(!cp.outputEntries.contains(this));
+ assert(tag != CONSTANT_Signature);
+ cp.outputEntries.add(this);
+ for (int j = 0; j < nrefs; j++)
+ {
+ ref(j)->requestOutputIndex(cp);
+ }
+}
+
+void constant_pool::resetOutputIndexes()
+{
+ int i;
+ int noes = outputEntries.length();
+ entry **oes = (entry **)outputEntries.base();
+ for (i = 0; i < noes; i++)
+ {
+ entry &e = *oes[i];
+ e.outputIndex = NOT_REQUESTED;
+ }
+ outputIndexLimit = 0;
+ outputEntries.empty();
+}
+
+static const byte TAG_ORDER[CONSTANT_Limit] = {0, 1, 0, 2, 3, 4, 5, 7, 6, 10, 11, 12, 9, 8};
+
+extern "C" int outputEntry_cmp(const void *e1p, const void *e2p)
+{
+ // Sort entries according to the Pack200 rules for deterministic
+ // constant pool ordering.
+ //
+ // The four sort keys as follows, in order of decreasing importance:
+ // 1. ldc first, then non-ldc guys
+ // 2. normal cp_All entries by input order (i.e., address order)
+ // 3. after that, extra entries by lexical order (as in tag_extras[*])
+ entry &e1 = *(entry *)*(void **)e1p;
+ entry &e2 = *(entry *)*(void **)e2p;
+ int oi1 = e1.outputIndex;
+ int oi2 = e2.outputIndex;
+ assert(oi1 == REQUESTED || oi1 == REQUESTED_LDC);
+ assert(oi2 == REQUESTED || oi2 == REQUESTED_LDC);
+ if (oi1 != oi2)
+ {
+ if (oi1 == REQUESTED_LDC)
+ return 0 - 1;
+ if (oi2 == REQUESTED_LDC)
+ return 1 - 0;
+ // Else fall through; neither is an ldc request.
+ }
+ if (e1.inord != NO_INORD || e2.inord != NO_INORD)
+ {
+ // One or both is normal. Use input order.
+ if (&e1 > &e2)
+ return 1 - 0;
+ if (&e1 < &e2)
+ return 0 - 1;
+ return 0; // equal pointers
+ }
+ // Both are extras. Sort by tag and then by value.
+ if (e1.tag != e2.tag)
+ {
+ return TAG_ORDER[e1.tag] - TAG_ORDER[e2.tag];
+ }
+ // If the tags are the same, use string comparison.
+ return compare_Utf8_chars(e1.value.b, e2.value.b);
+}
+
+void constant_pool::computeOutputIndexes()
+{
+ int i;
+
+ int noes = outputEntries.length();
+ entry **oes = (entry **)outputEntries.base();
+
+ // Sort the output constant pool into the order required by Pack200.
+ PTRLIST_QSORT(outputEntries, outputEntry_cmp);
+
+ // Allocate a new index for each entry that needs one.
+ // We do this in two passes, one for LDC entries and one for the rest.
+ int nextIndex = 1; // always skip index #0 in output cpool
+ for (i = 0; i < noes; i++)
+ {
+ entry &e = *oes[i];
+ assert(e.outputIndex == REQUESTED || e.outputIndex == REQUESTED_LDC);
+ e.outputIndex = nextIndex++;
+ if (e.isDoubleWord())
+ nextIndex++; // do not use the next index
+ }
+ outputIndexLimit = nextIndex;
+}
+
+// Unpacker Start
+// Deallocate all internal storage and reset to a clean state.
+// Do not disturb any input or output connections, including
+// infileptr, inbytes, read_input_fn, jarout, or errstrm.
+// Do not reset any unpack options.
+void unpacker::reset()
+{
+ bytes_read_before_reset += bytes_read;
+ bytes_written_before_reset += bytes_written;
+ files_written_before_reset += files_written;
+ classes_written_before_reset += classes_written;
+ segments_read_before_reset += 1;
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "After segment %d, " LONG_LONG_FORMAT
+ " bytes read and " LONG_LONG_FORMAT " bytes written.\n",
+ segments_read_before_reset - 1, bytes_read_before_reset,
+ bytes_written_before_reset);
+ fprintf(stderr,
+ "After segment %d, %d files (of which %d are classes) written to output.\n",
+ segments_read_before_reset - 1, files_written_before_reset,
+ classes_written_before_reset);
+ if (archive_next_count != 0)
+ {
+ fprintf(stderr, "After segment %d, %d segment%s remaining (estimated).\n",
+ segments_read_before_reset - 1, archive_next_count,
+ archive_next_count == 1 ? "" : "s");
+ }
+ }
+
+ unpacker save_u = (*this); // save bytewise image
+ infileptr = nullptr; // make asserts happy
+ jarout = nullptr; // do not close the output jar
+ gzin = nullptr; // do not close the input gzip stream
+ this->free();
+ this->init(read_input_fn);
+
+ // restore selected interface state:
+ infileptr = save_u.infileptr;
+ inbytes = save_u.inbytes;
+ jarout = save_u.jarout;
+ gzin = save_u.gzin;
+ verbose = save_u.verbose;
+ deflate_hint_or_zero = save_u.deflate_hint_or_zero;
+ modification_time_or_zero = save_u.modification_time_or_zero;
+ bytes_read_before_reset = save_u.bytes_read_before_reset;
+ bytes_written_before_reset = save_u.bytes_written_before_reset;
+ files_written_before_reset = save_u.files_written_before_reset;
+ classes_written_before_reset = save_u.classes_written_before_reset;
+ segments_read_before_reset = save_u.segments_read_before_reset;
+ // Note: If we use strip_names, watch out: They get nuked here.
+}
+
+void unpacker::init(read_input_fn_t input_fn)
+{
+ int i;
+ BYTES_OF(*this).clear();
+ this->u = this; // self-reference for U_NEW macro
+ read_input_fn = input_fn;
+ all_bands = band::makeBands(this);
+ // Make a default jar buffer; caller may safely overwrite it.
+ jarout = U_NEW(jar, 1);
+ jarout->init(this);
+ for (i = 0; i < ATTR_CONTEXT_LIMIT; i++)
+ attr_defs[i].u = u; // set up outer ptr
+}
+
+// Usage: unpack a byte buffer
+// packptr is a reference to byte buffer containing a
+// packed file and len is the length of the buffer.
+// If nullptr, the callback is used to fill an internal buffer.
+void unpacker::start(void *packptr, size_t len)
+{
+ if (packptr != nullptr && len != 0)
+ {
+ inbytes.set((byte *)packptr, len);
+ }
+ read_bands();
+}
+
+void unpacker::check_options()
+{
+ if (deflate_hint_or_zero != 0)
+ {
+ bool force_deflate_hint = (deflate_hint_or_zero > 0);
+ if (force_deflate_hint)
+ default_file_options |= FO_DEFLATE_HINT;
+ else
+ default_file_options &= ~FO_DEFLATE_HINT;
+ // Turn off per-file deflate hint by force.
+ suppress_file_options |= FO_DEFLATE_HINT;
+ }
+ if (modification_time_or_zero != 0)
+ {
+ default_file_modtime = modification_time_or_zero;
+ // Turn off per-file modtime by force.
+ archive_options &= ~AO_HAVE_FILE_MODTIME;
+ }
+}
+
+// classfile writing
+
+void unpacker::reset_cur_classfile()
+{
+ // set defaults
+ cur_class_minver = default_class_minver;
+ cur_class_majver = default_class_majver;
+
+ // reset constant pool state
+ cp.resetOutputIndexes();
+
+ // reset fixups
+ class_fixup_type.empty();
+ class_fixup_offset.empty();
+ class_fixup_ref.empty();
+ requested_ics.empty();
+}
+
+cpindex *constant_pool::getKQIndex()
+{
+ char ch = '?';
+ if (u->cur_descr != nullptr)
+ {
+ entry *type = u->cur_descr->descrType();
+ ch = type->value.b.ptr[0];
+ }
+ byte tag = CONSTANT_Integer;
+ switch (ch)
+ {
+ case 'L':
+ tag = CONSTANT_String;
+ break;
+ case 'I':
+ tag = CONSTANT_Integer;
+ break;
+ case 'J':
+ tag = CONSTANT_Long;
+ break;
+ case 'F':
+ tag = CONSTANT_Float;
+ break;
+ case 'D':
+ tag = CONSTANT_Double;
+ break;
+ case 'B':
+ case 'S':
+ case 'C':
+ case 'Z':
+ tag = CONSTANT_Integer;
+ break;
+ default:
+ unpack_abort("bad KQ reference");
+ break;
+ }
+ return getIndex(tag);
+}
+
+uint32_t unpacker::to_bci(uint32_t bii)
+{
+ uint32_t len = bcimap.length();
+ uint32_t *map = (uint32_t *)bcimap.base();
+ assert(len > 0); // must be initialized before using to_bci
+ if (bii < len)
+ return map[bii];
+ // Else it's a fractional or out-of-range BCI.
+ uint32_t key = bii - len;
+ for (int i = len;; i--)
+ {
+ if (map[i - 1] - (i - 1) <= key)
+ break;
+ else
+ --bii;
+ }
+ return bii;
+}
+
+void unpacker::put_stackmap_type()
+{
+ int tag = code_StackMapTable_T.getByte();
+ putu1(tag);
+ switch (tag)
+ {
+ case 7: // (7) [RCH]
+ putref(code_StackMapTable_RC.getRef());
+ break;
+ case 8: // (8) [PH]
+ putu2(to_bci(code_StackMapTable_P.getInt()));
+ break;
+ }
+}
+
+// Functions for writing code.
+
+void unpacker::put_label(int curIP, int size)
+{
+ code_fixup_type.addByte(size);
+ code_fixup_offset.add((int)put_empty(size));
+ code_fixup_source.add(curIP);
+}
+
+inline // called exactly once => inline
+ void
+unpacker::write_bc_ops()
+{
+ bcimap.empty();
+ code_fixup_type.empty();
+ code_fixup_offset.empty();
+ code_fixup_source.empty();
+
+ band *bc_which;
+
+ byte *opptr = bc_codes.curRP();
+ // No need for oplimit, since the codes are pre-counted.
+
+ size_t codeBase = wpoffset();
+
+ bool isAload; // copy-out result
+ int origBC;
+
+ entry *thisClass = cur_class;
+ entry *superClass = cur_super;
+ entry *newClass = nullptr; // class of last _new opcode
+
+ // overwrite any prior index on these bands; it changes w/ current class:
+ bc_thisfield.setIndex(cp.getFieldIndex(thisClass));
+ bc_thismethod.setIndex(cp.getMethodIndex(thisClass));
+ if (superClass != nullptr)
+ {
+ bc_superfield.setIndex(cp.getFieldIndex(superClass));
+ bc_supermethod.setIndex(cp.getMethodIndex(superClass));
+ }
+
+ for (int curIP = 0;; curIP++)
+ {
+ int curPC = (int)(wpoffset() - codeBase);
+ bcimap.add(curPC);
+ ensure_put_space(10); // covers most instrs w/o further bounds check
+ int bc = *opptr++ & 0xFF;
+
+ putu1_fast(bc);
+ // Note: See '--wp' below for pseudo-bytecodes like bc_end_marker.
+
+ bool isWide = false;
+ if (bc == bc_wide)
+ {
+ bc = *opptr++ & 0xFF;
+ putu1_fast(bc);
+ isWide = true;
+ }
+ switch (bc)
+ {
+ case bc_end_marker:
+ --wp; // not really part of the code
+ assert(opptr <= bc_codes.maxRP());
+ bc_codes.curRP() = opptr; // advance over this in bc_codes
+ goto doneScanningMethod;
+ case bc_tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label))
+ case bc_lookupswitch: // apc: (df, nc, nc*(case, label))
+ {
+ int caseCount = bc_case_count.getInt();
+ while (((wpoffset() - codeBase) % 4) != 0)
+ putu1_fast(0);
+ ensure_put_space(30 + caseCount * 8);
+ put_label(curIP, 4); // int df = bc_label.getInt();
+ if (bc == bc_tableswitch)
+ {
+ int lo = bc_case_value.getInt();
+ int hi = lo + caseCount - 1;
+ putu4(lo);
+ putu4(hi);
+ for (int j = 0; j < caseCount; j++)
+ {
+ put_label(curIP, 4); // int lVal = bc_label.getInt();
+ // int cVal = lo + j;
+ }
+ }
+ else
+ {
+ putu4(caseCount);
+ for (int j = 0; j < caseCount; j++)
+ {
+ int cVal = bc_case_value.getInt();
+ putu4(cVal);
+ put_label(curIP, 4); // int lVal = bc_label.getInt();
+ }
+ }
+ assert((int)to_bci(curIP) == curPC);
+ continue;
+ }
+ case bc_iinc:
+ {
+ int local = bc_local.getInt();
+ int delta = (isWide ? bc_short : bc_byte).getInt();
+ if (isWide)
+ {
+ putu2(local);
+ putu2(delta);
+ }
+ else
+ {
+ putu1_fast(local);
+ putu1_fast(delta);
+ }
+ continue;
+ }
+ case bc_sipush:
+ {
+ int val = bc_short.getInt();
+ putu2(val);
+ continue;
+ }
+ case bc_bipush:
+ case bc_newarray:
+ {
+ int val = bc_byte.getByte();
+ putu1_fast(val);
+ continue;
+ }
+ case bc_ref_escape:
+ {
+ // Note that insnMap has one entry for this.
+ --wp; // not really part of the code
+ int size = bc_escrefsize.getInt();
+ entry *ref = bc_escref.getRefN();
+ switch (size)
+ {
+ case 1:
+ putu1ref(ref);
+ break;
+ case 2:
+ putref(ref);
+ break;
+ default:
+ assert(false);
+ }
+ continue;
+ }
+ case bc_byte_escape:
+ {
+ // Note that insnMap has one entry for all these bytes.
+ --wp; // not really part of the code
+ int size = bc_escsize.getInt();
+ ensure_put_space(size);
+ for (int j = 0; j < size; j++)
+ putu1_fast(bc_escbyte.getByte());
+ continue;
+ }
+ default:
+ if (is_invoke_init_op(bc))
+ {
+ origBC = bc_invokespecial;
+ entry *classRef;
+ switch (bc - _invokeinit_op)
+ {
+ case _invokeinit_self_option:
+ classRef = thisClass;
+ break;
+ case _invokeinit_super_option:
+ classRef = superClass;
+ break;
+ default:
+ assert(bc == _invokeinit_op + _invokeinit_new_option);
+ case _invokeinit_new_option:
+ classRef = newClass;
+ break;
+ }
+ wp[-1] = origBC; // overwrite with origBC
+ int coding = bc_initref.getInt();
+ // Find the nth overloading of <init> in classRef.
+ entry *ref = nullptr;
+ cpindex *ix = (classRef == nullptr) ? nullptr : cp.getMethodIndex(classRef);
+ for (int j = 0, which_init = 0;; j++)
+ {
+ ref = (ix == nullptr) ? nullptr : ix->get(j);
+ if (ref == nullptr)
+ break; // oops, bad input
+ assert(ref->tag == CONSTANT_Methodref);
+ if (ref->memberDescr()->descrName() == cp.sym[constant_pool::s_lt_init_gt])
+ {
+ if (which_init++ == coding)
+ break;
+ }
+ }
+ putref(ref);
+ continue;
+ }
+ bc_which = ref_band_for_self_op(bc, isAload, origBC);
+ if (bc_which != nullptr)
+ {
+ if (!isAload)
+ {
+ wp[-1] = origBC; // overwrite with origBC
+ }
+ else
+ {
+ wp[-1] = bc_aload_0; // overwrite with _aload_0
+ // Note: insnMap keeps the _aload_0 separate.
+ bcimap.add(++curPC);
+ ++curIP;
+ putu1_fast(origBC);
+ }
+ entry *ref = bc_which->getRef();
+ putref(ref);
+ continue;
+ }
+ if (is_branch_op(bc))
+ {
+ // int lVal = bc_label.getInt();
+ if (bc < bc_goto_w)
+ {
+ put_label(curIP, 2); // putu2(lVal & 0xFFFF);
+ }
+ else
+ {
+ assert(bc <= bc_jsr_w);
+ put_label(curIP, 4); // putu4(lVal);
+ }
+ assert((int)to_bci(curIP) == curPC);
+ continue;
+ }
+ bc_which = ref_band_for_op(bc);
+ if (bc_which != nullptr)
+ {
+ entry *ref = bc_which->getRefCommon(bc_which->ix, bc_which->nullOK);
+ if (ref == nullptr && bc_which == &bc_classref)
+ {
+ // Shorthand for class self-references.
+ ref = thisClass;
+ }
+ origBC = bc;
+ switch (bc)
+ {
+ case bc_ildc:
+ case bc_cldc:
+ case bc_fldc:
+ case bc_aldc:
+ origBC = bc_ldc;
+ break;
+ case bc_ildc_w:
+ case bc_cldc_w:
+ case bc_fldc_w:
+ case bc_aldc_w:
+ origBC = bc_ldc_w;
+ break;
+ case bc_lldc2_w:
+ case bc_dldc2_w:
+ origBC = bc_ldc2_w;
+ break;
+ case bc_new:
+ newClass = ref;
+ break;
+ }
+ wp[-1] = origBC; // overwrite with origBC
+ if (origBC == bc_ldc)
+ {
+ putu1ref(ref);
+ }
+ else
+ {
+ putref(ref);
+ }
+ if (origBC == bc_multianewarray)
+ {
+ // Copy the trailing byte also.
+ int val = bc_byte.getByte();
+ putu1_fast(val);
+ }
+ else if (origBC == bc_invokeinterface)
+ {
+ int argSize = ref->memberDescr()->descrType()->typeSize();
+ putu1_fast(1 + argSize);
+ putu1_fast(0);
+ }
+ continue;
+ }
+ if (is_local_slot_op(bc))
+ {
+ int local = bc_local.getInt();
+ if (isWide)
+ {
+ putu2(local);
+ if (bc == bc_iinc)
+ {
+ int iVal = bc_short.getInt();
+ putu2(iVal);
+ }
+ }
+ else
+ {
+ putu1_fast(local);
+ if (bc == bc_iinc)
+ {
+ int iVal = bc_byte.getByte();
+ putu1_fast(iVal);
+ }
+ }
+ continue;
+ }
+ // Random bytecode. Just copy it.
+ assert(bc < bc_bytecode_limit);
+ }
+ }
+doneScanningMethod:
+{
+}
+ // bcimap.add(curPC); // PC limit is already also in map, from bc_end_marker
+
+ // Armed with a bcimap, we can now fix up all the labels.
+ for (int i = 0; i < (int)code_fixup_type.size(); i++)
+ {
+ int type = code_fixup_type.getByte(i);
+ byte *bp = wp_at(code_fixup_offset.get(i));
+ int curIP = code_fixup_source.get(i);
+ int destIP = curIP + bc_label.getInt();
+ int span = to_bci(destIP) - to_bci(curIP);
+ switch (type)
+ {
+ case 2:
+ putu2_at(bp, (ushort)span);
+ break;
+ case 4:
+ putu4_at(bp, span);
+ break;
+ default:
+ assert(false);
+ }
+ }
+}
+
+inline // called exactly once => inline
+ void
+unpacker::write_code()
+{
+ int j;
+
+ int max_stack, max_locals, handler_count, cflags;
+ get_code_header(max_stack, max_locals, handler_count, cflags);
+
+ if (max_stack < 0)
+ max_stack = code_max_stack.getInt();
+ if (max_locals < 0)
+ max_locals = code_max_na_locals.getInt();
+ if (handler_count < 0)
+ handler_count = code_handler_count.getInt();
+
+ int siglen = cur_descr->descrType()->typeSize();
+ if ((cur_descr_flags & ACC_STATIC) == 0)
+ siglen++;
+ max_locals += siglen;
+
+ putu2(max_stack);
+ putu2(max_locals);
+ size_t bcbase = put_empty(4);
+
+ // Write the bytecodes themselves.
+ write_bc_ops();
+
+ byte *bcbasewp = wp_at(bcbase);
+ putu4_at(bcbasewp, (int)(wp - (bcbasewp + 4))); // size of code attr
+
+ putu2(handler_count);
+ for (j = 0; j < handler_count; j++)
+ {
+ int bii = code_handler_start_P.getInt();
+ putu2(to_bci(bii));
+ bii += code_handler_end_PO.getInt();
+ putu2(to_bci(bii));
+ bii += code_handler_catch_PO.getInt();
+ putu2(to_bci(bii));
+ putref(code_handler_class_RCN.getRefN());
+ }
+
+ uint64_t indexBits = cflags;
+ if (cflags < 0)
+ {
+ bool haveLongFlags = attr_defs[ATTR_CONTEXT_CODE].haveLongFlags();
+ indexBits = code_flags_hi.getLong(code_flags_lo, haveLongFlags);
+ }
+ write_attrs(ATTR_CONTEXT_CODE, indexBits);
+}
+
+int unpacker::write_attrs(int attrc, uint64_t indexBits)
+{
+ if (indexBits == 0)
+ {
+ // Quick short-circuit.
+ putu2(0);
+ return 0;
+ }
+
+ attr_definitions &ad = attr_defs[attrc];
+
+ int i, j, j2, idx, count;
+
+ int oiCount = 0;
+ if (ad.isPredefined(X_ATTR_OVERFLOW) && (indexBits & ((uint64_t)1 << X_ATTR_OVERFLOW)) != 0)
+ {
+ indexBits -= ((uint64_t)1 << X_ATTR_OVERFLOW);
+ oiCount = ad.xxx_attr_count().getInt();
+ }
+
+ int bitIndexes[X_ATTR_LIMIT_FLAGS_HI];
+ int biCount = 0;
+
+ // Fill bitIndexes with index bits, in order.
+ for (idx = 0; indexBits != 0; idx++, indexBits >>= 1)
+ {
+ if ((indexBits & 1) != 0)
+ bitIndexes[biCount++] = idx;
+ }
+ assert(biCount <= (int)lengthof(bitIndexes));
+
+ // Write a provisional attribute count, perhaps to be corrected later.
+ int naOffset = (int)wpoffset();
+ int na0 = biCount + oiCount;
+ putu2(na0);
+
+ int na = 0;
+ for (i = 0; i < na0; i++)
+ {
+ if (i < biCount)
+ idx = bitIndexes[i];
+ else
+ idx = ad.xxx_attr_indexes().getInt();
+ assert(ad.isIndex(idx));
+ entry *aname = nullptr;
+ entry *ref; // scratch
+ size_t abase = put_empty(2 + 4);
+ if (idx < (int)ad.flag_limit && ad.isPredefined(idx))
+ {
+ // Switch on the attrc and idx simultaneously.
+ switch (ADH_BYTE(attrc, idx))
+ {
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, X_ATTR_OVERFLOW) :
+ case ADH_BYTE(ATTR_CONTEXT_FIELD, X_ATTR_OVERFLOW) :
+ case ADH_BYTE(ATTR_CONTEXT_METHOD, X_ATTR_OVERFLOW) :
+ case ADH_BYTE(ATTR_CONTEXT_CODE, X_ATTR_OVERFLOW) :
+ // no attribute at all, so back up on this one
+ wp = wp_at(abase);
+ continue;
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_ClassFile_version) :
+ cur_class_minver = class_ClassFile_version_minor_H.getInt();
+ cur_class_majver = class_ClassFile_version_major_H.getInt();
+ // back up; not a real attribute
+ wp = wp_at(abase);
+ continue;
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_InnerClasses) :
+ // note the existence of this attr, but save for later
+ if (cur_class_has_local_ics)
+ unpack_abort("too many InnerClasses attrs");
+ cur_class_has_local_ics = true;
+ wp = wp_at(abase);
+ continue;
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_SourceFile) :
+ aname = cp.sym[constant_pool::s_SourceFile];
+ ref = class_SourceFile_RUN.getRefN();
+ if (ref == nullptr)
+ {
+ bytes &n = cur_class->ref(0)->value.b;
+ // parse n = (<pkg>/)*<outer>?($<id>)*
+ int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, (int)n.len) + 1;
+ bytes prefix = n.slice(pkglen, n.len);
+ for (;;)
+ {
+ // Work backwards, finding all '$', '#', etc.
+ int dollar =
+ lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, prefix, (int)prefix.len);
+ if (dollar < 0)
+ break;
+ prefix = prefix.slice(0, dollar);
+ }
+ const char *suffix = ".java";
+ int len = (int)(prefix.len + strlen(suffix));
+ bytes name;
+ name.set(T_NEW(byte, add_size(len, 1)), len);
+ name.strcat(prefix).strcat(suffix);
+ ref = cp.ensureUtf8(name);
+ }
+ putref(ref);
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_EnclosingMethod) :
+ aname = cp.sym[constant_pool::s_EnclosingMethod];
+ putref(class_EnclosingMethod_RC.getRefN());
+ putref(class_EnclosingMethod_RDN.getRefN());
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_FIELD, FIELD_ATTR_ConstantValue) :
+ aname = cp.sym[constant_pool::s_ConstantValue];
+ putref(field_ConstantValue_KQ.getRefUsing(cp.getKQIndex()));
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_METHOD, METHOD_ATTR_Code) :
+ aname = cp.sym[constant_pool::s_Code];
+ write_code();
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_METHOD, METHOD_ATTR_Exceptions) :
+ aname = cp.sym[constant_pool::s_Exceptions];
+ putu2(count = method_Exceptions_N.getInt());
+ for (j = 0; j < count; j++)
+ {
+ putref(method_Exceptions_RC.getRefN());
+ }
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_StackMapTable) :
+ aname = cp.sym[constant_pool::s_StackMapTable];
+ // (keep this code aligned with its brother in unpacker::read_attrs)
+ putu2(count = code_StackMapTable_N.getInt());
+ for (j = 0; j < count; j++)
+ {
+ int tag = code_StackMapTable_frame_T.getByte();
+ putu1(tag);
+ if (tag <= 127)
+ {
+ // (64-127) [(2)]
+ if (tag >= 64)
+ put_stackmap_type();
+ }
+ else if (tag <= 251)
+ {
+ // (247) [(1)(2)]
+ // (248-251) [(1)]
+ if (tag >= 247)
+ putu2(code_StackMapTable_offset.getInt());
+ if (tag == 247)
+ put_stackmap_type();
+ }
+ else if (tag <= 254)
+ {
+ // (252) [(1)(2)]
+ // (253) [(1)(2)(2)]
+ // (254) [(1)(2)(2)(2)]
+ putu2(code_StackMapTable_offset.getInt());
+ for (int k = (tag - 251); k > 0; k--)
+ {
+ put_stackmap_type();
+ }
+ }
+ else
+ {
+ // (255) [(1)NH[(2)]NH[(2)]]
+ putu2(code_StackMapTable_offset.getInt());
+ putu2(j2 = code_StackMapTable_local_N.getInt());
+ while (j2-- > 0)
+ put_stackmap_type();
+ putu2(j2 = code_StackMapTable_stack_N.getInt());
+ while (j2-- > 0)
+ put_stackmap_type();
+ }
+ }
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_LineNumberTable) :
+ aname = cp.sym[constant_pool::s_LineNumberTable];
+ putu2(count = code_LineNumberTable_N.getInt());
+ for (j = 0; j < count; j++)
+ {
+ putu2(to_bci(code_LineNumberTable_bci_P.getInt()));
+ putu2(code_LineNumberTable_line.getInt());
+ }
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_LocalVariableTable) :
+ aname = cp.sym[constant_pool::s_LocalVariableTable];
+ putu2(count = code_LocalVariableTable_N.getInt());
+ for (j = 0; j < count; j++)
+ {
+ int bii = code_LocalVariableTable_bci_P.getInt();
+ int bci = to_bci(bii);
+ putu2(bci);
+ bii += code_LocalVariableTable_span_O.getInt();
+ putu2(to_bci(bii) - bci);
+ putref(code_LocalVariableTable_name_RU.getRefN());
+ putref(code_LocalVariableTable_type_RS.getRefN());
+ putu2(code_LocalVariableTable_slot.getInt());
+ }
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_LocalVariableTypeTable) :
+ aname = cp.sym[constant_pool::s_LocalVariableTypeTable];
+ putu2(count = code_LocalVariableTypeTable_N.getInt());
+ for (j = 0; j < count; j++)
+ {
+ int bii = code_LocalVariableTypeTable_bci_P.getInt();
+ int bci = to_bci(bii);
+ putu2(bci);
+ bii += code_LocalVariableTypeTable_span_O.getInt();
+ putu2(to_bci(bii) - bci);
+ putref(code_LocalVariableTypeTable_name_RU.getRefN());
+ putref(code_LocalVariableTypeTable_type_RS.getRefN());
+ putu2(code_LocalVariableTypeTable_slot.getInt());
+ }
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, X_ATTR_Signature) :
+ aname = cp.sym[constant_pool::s_Signature];
+ putref(class_Signature_RS.getRefN());
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_FIELD, X_ATTR_Signature) :
+ aname = cp.sym[constant_pool::s_Signature];
+ putref(field_Signature_RS.getRefN());
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_METHOD, X_ATTR_Signature) :
+ aname = cp.sym[constant_pool::s_Signature];
+ putref(method_Signature_RS.getRefN());
+ break;
+
+ case ADH_BYTE(ATTR_CONTEXT_CLASS, X_ATTR_Deprecated) :
+ case ADH_BYTE(ATTR_CONTEXT_FIELD, X_ATTR_Deprecated) :
+ case ADH_BYTE(ATTR_CONTEXT_METHOD, X_ATTR_Deprecated) :
+ aname = cp.sym[constant_pool::s_Deprecated];
+ // no data
+ break;
+ }
+ }
+
+ if (aname == nullptr)
+ {
+ // Unparse a compressor-defined attribute.
+ layout_definition *lo = ad.getLayout(idx);
+ if (lo == nullptr)
+ {
+ unpack_abort("bad layout index");
+ break;
+ }
+ assert((int)lo->idx == idx);
+ aname = lo->nameEntry;
+ if (aname == nullptr)
+ {
+ bytes nameb;
+ nameb.set(lo->name);
+ aname = cp.ensureUtf8(nameb);
+ // Cache the name entry for next time.
+ lo->nameEntry = aname;
+ }
+ // Execute all the layout elements.
+ band **bands = lo->bands();
+ if (lo->hasCallables())
+ {
+ band &cble = *bands[0];
+ assert(cble.le_kind == EK_CBLE);
+ bands = cble.le_body;
+ }
+ putlayout(bands);
+ }
+
+ if (aname == nullptr)
+ unpack_abort("bad attribute index");
+
+ byte *wp1 = wp;
+ wp = wp_at(abase);
+
+ // DTRT if this attr is on the strip-list.
+ // (Note that we emptied the data out of the band first.)
+ if (ad.strip_names.contains(aname))
+ {
+ continue;
+ }
+
+ // patch the name and length
+ putref(aname);
+ putu4((int)(wp1 - (wp + 4))); // put the attr size
+ wp = wp1;
+ na++; // count the attrs actually written
+ }
+
+ if (na != na0)
+ // Refresh changed count.
+ putu2_at(wp_at(naOffset), na);
+ return na;
+}
+
+void unpacker::write_members(int num, int attrc)
+{
+ attr_definitions &ad = attr_defs[attrc];
+ band &member_flags_hi = ad.xxx_flags_hi();
+ band &member_flags_lo = ad.xxx_flags_lo();
+ band &member_descr = (&member_flags_hi)[e_field_descr - e_field_flags_hi];
+ bool haveLongFlags = ad.haveLongFlags();
+
+ putu2(num);
+ uint64_t indexMask = attr_defs[attrc].flagIndexMask();
+ for (int i = 0; i < num; i++)
+ {
+ uint64_t mflags = member_flags_hi.getLong(member_flags_lo, haveLongFlags);
+ entry *mdescr = member_descr.getRef();
+ cur_descr = mdescr;
+ putu2(cur_descr_flags = (ushort)(mflags & ~indexMask));
+ putref(mdescr->descrName());
+ putref(mdescr->descrType());
+ write_attrs(attrc, (mflags & indexMask));
+ }
+ cur_descr = nullptr;
+}
+
+extern "C" int raw_address_cmp(const void *p1p, const void *p2p)
+{
+ void *p1 = *(void **)p1p;
+ void *p2 = *(void **)p2p;
+ return (p1 > p2) ? 1 : (p1 < p2) ? -1 : 0;
+}
+
+void unpacker::write_classfile_tail()
+{
+ cur_classfile_tail.empty();
+ set_output(&cur_classfile_tail);
+
+ int i, num;
+
+ attr_definitions &ad = attr_defs[ATTR_CONTEXT_CLASS];
+
+ bool haveLongFlags = ad.haveLongFlags();
+ uint64_t kflags = class_flags_hi.getLong(class_flags_lo, haveLongFlags);
+ uint64_t indexMask = ad.flagIndexMask();
+
+ cur_class = class_this.getRef();
+ cur_super = class_super.getRef();
+
+ if (cur_super == cur_class)
+ cur_super = nullptr;
+ // special representation for java/lang/Object
+
+ putu2((ushort)(kflags & ~indexMask));
+ putref(cur_class);
+ putref(cur_super);
+
+ putu2(num = class_interface_count.getInt());
+ for (i = 0; i < num; i++)
+ {
+ putref(class_interface.getRef());
+ }
+
+ write_members(class_field_count.getInt(), ATTR_CONTEXT_FIELD);
+ write_members(class_method_count.getInt(), ATTR_CONTEXT_METHOD);
+
+ cur_class_has_local_ics = false; // may be set true by write_attrs
+
+ int naOffset = (int)wpoffset();
+ int na = write_attrs(ATTR_CONTEXT_CLASS, (kflags & indexMask));
+
+// at the very last, choose which inner classes (if any) pertain to k:
+#ifdef ASSERT
+ for (i = 0; i < ic_count; i++)
+ {
+ assert(!ics[i].requested);
+ }
+#endif
+ // First, consult the global table and the local constant pool,
+ // and decide on the globally implied inner classes.
+ // (Note that we read the cpool's outputIndex fields, but we
+ // do not yet write them, since the local IC attribute might
+ // reverse a global decision to declare an IC.)
+ assert(requested_ics.length() == 0); // must start out empty
+ // Always include all members of the current class.
+ for (inner_class *child = cp.getFirstChildIC(cur_class); child != nullptr;
+ child = cp.getNextChildIC(child))
+ {
+ child->requested = true;
+ requested_ics.add(child);
+ }
+ // And, for each inner class mentioned in the constant pool,
+ // include it and all its outers.
+ int noes = cp.outputEntries.length();
+ entry **oes = (entry **)cp.outputEntries.base();
+ for (i = 0; i < noes; i++)
+ {
+ entry &e = *oes[i];
+ if (e.tag != CONSTANT_Class)
+ continue; // wrong sort
+ for (inner_class *ic = cp.getIC(&e); ic != nullptr; ic = cp.getIC(ic->outer))
+ {
+ if (ic->requested)
+ break; // already processed
+ ic->requested = true;
+ requested_ics.add(ic);
+ }
+ }
+ int local_ics = requested_ics.length();
+ // Second, consult a local attribute (if any) and adjust the global set.
+ inner_class *extra_ics = nullptr;
+ int num_extra_ics = 0;
+ if (cur_class_has_local_ics)
+ {
+ // adjust the set of ICs by symmetric set difference w/ the locals
+ num_extra_ics = class_InnerClasses_N.getInt();
+ if (num_extra_ics == 0)
+ {
+ // Explicit zero count has an irregular meaning: It deletes the attr.
+ local_ics = 0; // (short-circuit all tests of requested bits)
+ }
+ else
+ {
+ extra_ics = T_NEW(inner_class, num_extra_ics);
+ // Note: extra_ics will be freed up by next call to get_next_file().
+ }
+ }
+ for (i = 0; i < num_extra_ics; i++)
+ {
+ inner_class &extra_ic = extra_ics[i];
+ extra_ic.inner = class_InnerClasses_RC.getRef();
+ // Find the corresponding equivalent global IC:
+ inner_class *global_ic = cp.getIC(extra_ic.inner);
+ int flags = class_InnerClasses_F.getInt();
+ if (flags == 0)
+ {
+ // The extra IC is simply a copy of a global IC.
+ if (global_ic == nullptr)
+ {
+ unpack_abort("bad reference to inner class");
+ break;
+ }
+ extra_ic = (*global_ic); // fill in rest of fields
+ }
+ else
+ {
+ flags &= ~ACC_IC_LONG_FORM; // clear high bit if set to get clean zero
+ extra_ic.flags = flags;
+ extra_ic.outer = class_InnerClasses_outer_RCN.getRefN();
+ extra_ic.name = class_InnerClasses_name_RUN.getRefN();
+ // Detect if this is an exact copy of the global tuple.
+ if (global_ic != nullptr)
+ {
+ if (global_ic->flags != extra_ic.flags || global_ic->outer != extra_ic.outer ||
+ global_ic->name != extra_ic.name)
+ {
+ global_ic = nullptr; // not really the same, so break the link
+ }
+ }
+ }
+ if (global_ic != nullptr && global_ic->requested)
+ {
+ // This local repetition reverses the globally implied request.
+ global_ic->requested = false;
+ extra_ic.requested = false;
+ local_ics -= 1;
+ }
+ else
+ {
+ // The global either does not exist, or is not yet requested.
+ extra_ic.requested = true;
+ local_ics += 1;
+ }
+ }
+ // Finally, if there are any that survived, put them into an attribute.
+ // (Note that a zero-count attribute is always deleted.)
+ // The putref calls below will tell the constant pool to add any
+ // necessary local CP references to support the InnerClasses attribute.
+ // This step must be the last round of additions to the local CP.
+ if (local_ics > 0)
+ {
+ // append the new attribute:
+ putref(cp.sym[constant_pool::s_InnerClasses]);
+ putu4(2 + 2 * 4 * local_ics);
+ putu2(local_ics);
+ PTRLIST_QSORT(requested_ics, raw_address_cmp);
+ int num_global_ics = requested_ics.length();
+ for (i = -num_global_ics; i < num_extra_ics; i++)
+ {
+ inner_class *ic;
+ if (i < 0)
+ ic = (inner_class *)requested_ics.get(num_global_ics + i);
+ else
+ ic = &extra_ics[i];
+ if (ic->requested)
+ {
+ putref(ic->inner);
+ putref(ic->outer);
+ putref(ic->name);
+ putu2(ic->flags);
+ }
+ }
+ putu2_at(wp_at(naOffset), ++na); // increment class attr count
+ }
+
+ // Tidy up global 'requested' bits:
+ for (i = requested_ics.length(); --i >= 0;)
+ {
+ inner_class *ic = (inner_class *)requested_ics.get(i);
+ ic->requested = false;
+ }
+ requested_ics.empty();
+
+ close_output();
+
+ // rewrite CP references in the tail
+ cp.computeOutputIndexes();
+ int nextref = 0;
+ for (i = 0; i < (int)class_fixup_type.size(); i++)
+ {
+ int type = class_fixup_type.getByte(i);
+ byte *fixp = wp_at(class_fixup_offset.get(i));
+ entry *e = (entry *)class_fixup_ref.get(nextref++);
+ int idx = e->getOutputIndex();
+ switch (type)
+ {
+ case 1:
+ putu1_at(fixp, idx);
+ break;
+ case 2:
+ putu2_at(fixp, idx);
+ break;
+ default:
+ assert(false); // should not reach here
+ }
+ }
+}
+
+void unpacker::write_classfile_head()
+{
+ cur_classfile_head.empty();
+ set_output(&cur_classfile_head);
+
+ putu4(JAVA_MAGIC);
+ putu2(cur_class_minver);
+ putu2(cur_class_majver);
+ putu2(cp.outputIndexLimit);
+
+ int checkIndex = 1;
+ int noes = cp.outputEntries.length();
+ entry **oes = (entry **)cp.outputEntries.base();
+ for (int i = 0; i < noes; i++)
+ {
+ entry &e = *oes[i];
+ assert(e.getOutputIndex() == checkIndex++);
+ byte tag = e.tag;
+ assert(tag != CONSTANT_Signature);
+ putu1(tag);
+ switch (tag)
+ {
+ case CONSTANT_Utf8:
+ putu2((int)e.value.b.len);
+ put_bytes(e.value.b);
+ break;
+ case CONSTANT_Integer:
+ case CONSTANT_Float:
+ putu4(e.value.i);
+ break;
+ case CONSTANT_Long:
+ case CONSTANT_Double:
+ putu8(e.value.l);
+ assert(checkIndex++);
+ break;
+ case CONSTANT_Class:
+ case CONSTANT_String:
+ // just write the ref
+ putu2(e.refs[0]->getOutputIndex());
+ break;
+ case CONSTANT_Fieldref:
+ case CONSTANT_Methodref:
+ case CONSTANT_InterfaceMethodref:
+ case CONSTANT_NameandType:
+ putu2(e.refs[0]->getOutputIndex());
+ putu2(e.refs[1]->getOutputIndex());
+ break;
+ default:
+ unpack_abort(ERROR_INTERNAL);
+ }
+ }
+ close_output();
+}
+
+unpacker::file *unpacker::get_next_file()
+{
+ free_temps();
+ if (files_remaining == 0)
+ {
+ // Leave a clue that we're exhausted.
+ cur_file.name = nullptr;
+ cur_file.size = 0;
+ if (archive_size != 0)
+ {
+ uint64_t predicted_size = unsized_bytes_read + archive_size;
+ if (predicted_size != bytes_read)
+ unpack_abort("archive header had incorrect size");
+ }
+ return nullptr;
+ }
+ files_remaining -= 1;
+ assert(files_written < file_count || classes_written < class_count);
+ cur_file.name = "";
+ cur_file.size = 0;
+ cur_file.modtime = default_file_modtime;
+ cur_file.options = default_file_options;
+ cur_file.data[0].set(nullptr, 0);
+ cur_file.data[1].set(nullptr, 0);
+ if (files_written < file_count)
+ {
+ entry *e = file_name.getRef();
+ cur_file.name = e->utf8String();
+ bool haveLongSize = ((archive_options & AO_HAVE_FILE_SIZE_HI) != 0);
+ cur_file.size = file_size_hi.getLong(file_size_lo, haveLongSize);
+ if ((archive_options & AO_HAVE_FILE_MODTIME) != 0)
+ cur_file.modtime += file_modtime.getInt(); // relative to archive modtime
+ if ((archive_options & AO_HAVE_FILE_OPTIONS) != 0)
+ cur_file.options |= file_options.getInt() & ~suppress_file_options;
+ }
+ else if (classes_written < class_count)
+ {
+ // there is a class for a missing file record
+ cur_file.options |= FO_IS_CLASS_STUB;
+ }
+ if ((cur_file.options & FO_IS_CLASS_STUB) != 0)
+ {
+ assert(classes_written < class_count);
+ classes_written += 1;
+ if (cur_file.size != 0)
+ {
+ unpack_abort("class file size transmitted");
+ }
+ reset_cur_classfile();
+
+ // write the meat of the classfile:
+ write_classfile_tail();
+ cur_file.data[1] = cur_classfile_tail.b;
+
+ // write the CP of the classfile, second:
+ write_classfile_head();
+ cur_file.data[0] = cur_classfile_head.b;
+
+ cur_file.size += cur_file.data[0].len;
+ cur_file.size += cur_file.data[1].len;
+ if (cur_file.name[0] == '\0')
+ {
+ bytes &prefix = cur_class->ref(0)->value.b;
+ const char *suffix = ".class";
+ int len = (int)(prefix.len + strlen(suffix));
+ bytes name;
+ name.set(T_NEW(byte, add_size(len, 1)), len);
+ cur_file.name = name.strcat(prefix).strcat(suffix).strval();
+ }
+ }
+ else
+ {
+ // If there is buffered file data, produce a pointer to it.
+ if (cur_file.size != (size_t)cur_file.size)
+ {
+ // Silly size specified.
+ unpack_abort("resource file too large");
+ }
+ size_t rpleft = input_remaining();
+ if (rpleft > 0)
+ {
+ if (rpleft > cur_file.size)
+ rpleft = (size_t)cur_file.size;
+ cur_file.data[0].set(rp, rpleft);
+ rp += rpleft;
+ }
+ if (rpleft < cur_file.size)
+ {
+ // Caller must read the rest.
+ size_t fleft = (size_t)cur_file.size - rpleft;
+ bytes_read += fleft; // Credit it to the overall archive size.
+ }
+ }
+ bytes_written += cur_file.size;
+ files_written += 1;
+ return &cur_file;
+}
+
+// Write a file to jarout.
+void unpacker::write_file_to_jar(unpacker::file *f)
+{
+ size_t htsize = f->data[0].len + f->data[1].len;
+ uint64_t fsize = f->size;
+ if (htsize == fsize)
+ {
+ jarout->addJarEntry(f->name, f->deflate_hint(), f->modtime, f->data[0], f->data[1]);
+ }
+ else
+ {
+ assert(input_remaining() == 0);
+ bytes part1, part2;
+ part1.len = f->data[0].len;
+ part1.set(T_NEW(byte, part1.len), part1.len);
+ part1.copyFrom(f->data[0]);
+ assert(f->data[1].len == 0);
+ part2.set(nullptr, 0);
+ size_t fleft = (size_t)fsize - part1.len;
+ assert(bytes_read > fleft); // part2 already credited by get_next_file
+ bytes_read -= fleft;
+ if (fleft > 0)
+ {
+ // Must read some more.
+ if (live_input)
+ {
+ // Stop using the input buffer. Make a new one:
+ if (free_input)
+ input.free();
+ input.init(fleft > (1 << 12) ? fleft : (1 << 12));
+ free_input = true;
+ live_input = false;
+ }
+ else
+ {
+ // Make it large enough.
+ assert(free_input); // must be reallocable
+ input.ensureSize(fleft);
+ }
+ rplimit = rp = input.base();
+ input.setLimit(rp + fleft);
+ if (!ensure_input(fleft))
+ unpack_abort("EOF reading resource file");
+ part2.ptr = input_scan();
+ part2.len = input_remaining();
+ rplimit = rp = input.base();
+ }
+ jarout->addJarEntry(f->name, f->deflate_hint(), f->modtime, part1, part2);
+ }
+ if (verbose >= 3)
+ {
+ fprintf(stderr, "Wrote " LONG_LONG_FORMAT " bytes to: %s\n", fsize, f->name);
+ }
+}
diff --git a/depends/pack200/src/unpack.h b/depends/pack200/src/unpack.h
new file mode 100644
index 00000000..0100700d
--- /dev/null
+++ b/depends/pack200/src/unpack.h
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// Global Structures
+struct jar;
+struct gunzip;
+struct band;
+struct constant_pool;
+struct entry;
+struct cpindex;
+struct inner_class;
+struct value_stream;
+
+struct cpindex
+{
+ uint32_t len;
+ entry *base1; // base of primary index
+ entry **base2; // base of secondary index
+ byte ixTag; // type of entries (!= CONSTANT_None), plus 64 if sub-index
+ enum
+ {
+ SUB_TAG = 64
+ };
+
+ entry *get(uint32_t i);
+
+ void init(int len_, entry *base1_, int ixTag_)
+ {
+ len = len_;
+ base1 = base1_;
+ base2 = nullptr;
+ ixTag = ixTag_;
+ }
+ void init(int len_, entry **base2_, int ixTag_)
+ {
+ len = len_;
+ base1 = nullptr;
+ base2 = base2_;
+ ixTag = ixTag_;
+ }
+};
+
+struct constant_pool
+{
+ uint32_t nentries;
+ entry *entries;
+ entry *first_extra_entry;
+ uint32_t maxentries; // total allocated size of entries
+
+ // Position and size of each homogeneous subrange:
+ int tag_count[CONSTANT_Limit];
+ int tag_base[CONSTANT_Limit];
+ cpindex tag_index[CONSTANT_Limit];
+ ptrlist tag_extras[CONSTANT_Limit];
+
+ cpindex *member_indexes; // indexed by 2*CONSTANT_Class.inord
+ cpindex *getFieldIndex(entry *classRef);
+ cpindex *getMethodIndex(entry *classRef);
+
+ inner_class **ic_index;
+ inner_class **ic_child_index;
+ inner_class *getIC(entry *inner);
+ inner_class *getFirstChildIC(entry *outer);
+ inner_class *getNextChildIC(inner_class *child);
+
+ int outputIndexLimit; // index limit after renumbering
+ ptrlist outputEntries; // list of entry* needing output idx assigned
+
+ entry **hashTab;
+ uint32_t hashTabLength;
+ entry *&hashTabRef(byte tag, bytes &b);
+ entry *ensureUtf8(bytes &b);
+ entry *ensureClass(bytes &b);
+
+ // Well-known Utf8 symbols.
+ enum
+ {
+#define SNAME(n, s) s_##s,
+ ALL_ATTR_DO(SNAME)
+#undef SNAME
+ s_lt_init_gt, // <init>
+ s_LIMIT
+ };
+ entry *sym[s_LIMIT];
+
+ // read counts from hdr, allocate main arrays
+ enum
+ {
+ NUM_COUNTS = 12
+ };
+ void init(unpacker *u, int counts[NUM_COUNTS]);
+
+ // pointer to outer unpacker, for error checks etc.
+ unpacker *u;
+
+ int getCount(byte tag)
+ {
+ assert((uint32_t)tag < CONSTANT_Limit);
+ return tag_count[tag];
+ }
+ cpindex *getIndex(byte tag)
+ {
+ assert((uint32_t)tag < CONSTANT_Limit);
+ return &tag_index[tag];
+ }
+ cpindex *getKQIndex(); // uses cur_descr
+
+ void expandSignatures();
+ void initMemberIndexes();
+
+ void computeOutputOrder();
+ void computeOutputIndexes();
+ void resetOutputIndexes();
+};
+
+/*
+ * The unpacker provides the entry points to the unpack engine,
+ * as well as maintains the state of the engine.
+ */
+struct unpacker
+{
+ // One element of the resulting JAR.
+ struct file
+ {
+ const char *name;
+ uint64_t size;
+ int modtime;
+ int options;
+ bytes data[2];
+ // Note: If Sum(data[*].len) < size,
+ // remaining bytes must be read directly from the input stream.
+ bool deflate_hint()
+ {
+ return ((options & FO_DEFLATE_HINT) != 0);
+ }
+ };
+
+ // if running Unix-style, here are the inputs and outputs
+ FILE *infileptr; // buffered
+ bytes inbytes; // direct
+ gunzip *gzin; // gunzip filter, if any
+ jar *jarout; // output JAR file
+
+ // pointer to self, for U_NEW macro
+ unpacker *u;
+
+ ptrlist mallocs; // list of guys to free when we are all done
+ ptrlist tmallocs; // list of guys to free on next client request
+ fillbytes smallbuf; // supplies small alloc requests
+ fillbytes tsmallbuf; // supplies temporary small alloc requests
+
+ // option management members
+ int verbose; // verbose level, 0 means no output
+ int deflate_hint_or_zero; // ==0 means not set, otherwise -1 or 1
+ int modification_time_or_zero;
+
+ // input stream
+ fillbytes input; // the whole block (size is predicted, has slop too)
+ bool live_input; // is the data in this block live?
+ bool free_input; // must the input buffer be freed?
+ byte *rp; // read pointer (< rplimit <= input.limit())
+ byte *rplimit; // how much of the input block has been read?
+ uint64_t bytes_read;
+ int unsized_bytes_read;
+
+ // callback to read at least one byte, up to available input
+ typedef int64_t (*read_input_fn_t)(unpacker *self, void *buf, int64_t minlen,
+ int64_t maxlen);
+ read_input_fn_t read_input_fn;
+
+ // archive header fields
+ int magic, minver, majver;
+ size_t archive_size;
+ int archive_next_count, archive_options, archive_modtime;
+ int band_headers_size;
+ int file_count, attr_definition_count, ic_count, class_count;
+ int default_class_minver, default_class_majver;
+ int default_file_options, suppress_file_options; // not header fields
+ int default_archive_modtime, default_file_modtime; // not header fields
+ int code_count; // not a header field
+ int files_remaining; // not a header field
+
+ // engine state
+ band *all_bands; // indexed by band_number
+ byte *meta_rp; // read-pointer into (copy of) band_headers
+ constant_pool cp; // all constant pool information
+ inner_class *ics; // InnerClasses
+
+ // output stream
+ bytes output; // output block (either classfile head or tail)
+ byte *wp; // write pointer (< wplimit == output.limit())
+ byte *wpbase; // write pointer starting address (<= wp)
+ byte *wplimit; // how much of the output block has been written?
+
+ // output state
+ file cur_file;
+ entry *cur_class; // CONSTANT_Class entry
+ entry *cur_super; // CONSTANT_Class entry or nullptr
+ entry *cur_descr; // CONSTANT_NameandType entry
+ int cur_descr_flags; // flags corresponding to cur_descr
+ int cur_class_minver, cur_class_majver;
+ bool cur_class_has_local_ics;
+ fillbytes cur_classfile_head;
+ fillbytes cur_classfile_tail;
+ int files_written; // also tells which file we're working on
+ int classes_written; // also tells which class we're working on
+ uint64_t bytes_written;
+ intlist bcimap;
+ fillbytes class_fixup_type;
+ intlist class_fixup_offset;
+ ptrlist class_fixup_ref;
+ fillbytes code_fixup_type; // which format of branch operand?
+ intlist code_fixup_offset; // location of operand needing fixup
+ intlist code_fixup_source; // encoded ID of branch insn
+ ptrlist requested_ics; // which ics need output?
+
+ // stats pertaining to multiple segments (updated on reset)
+ uint64_t bytes_read_before_reset;
+ uint64_t bytes_written_before_reset;
+ int files_written_before_reset;
+ int classes_written_before_reset;
+ int segments_read_before_reset;
+
+ // attribute state
+ struct layout_definition
+ {
+ uint32_t idx; // index (0..31...) which identifies this layout
+ const char *name; // name of layout
+ entry *nameEntry;
+ const char *layout; // string of layout (not yet parsed)
+ band **elems; // array of top-level layout elems (or callables)
+
+ bool hasCallables()
+ {
+ return layout[0] == '[';
+ }
+ band **bands()
+ {
+ assert(elems != nullptr);
+ return elems;
+ }
+ };
+ struct attr_definitions
+ {
+ unpacker *u; // pointer to self, for U_NEW macro
+ int xxx_flags_hi_bn; // locator for flags, count, indexes, calls bands
+ int attrc; // ATTR_CONTEXT_CLASS, etc.
+ uint32_t flag_limit; // 32 or 63, depending on archive_options bit
+ uint64_t predef; // mask of built-in definitions
+ uint64_t redef; // mask of local flag definitions or redefinitions
+ ptrlist layouts; // local (compressor-defined) defs, in index order
+ int flag_count[X_ATTR_LIMIT_FLAGS_HI];
+ intlist overflow_count;
+ ptrlist strip_names; // what attribute names are being stripped?
+ ptrlist band_stack; // Temp., used during layout parsing.
+ ptrlist calls_to_link; // (ditto)
+ int bands_made; // (ditto)
+
+ void free()
+ {
+ layouts.free();
+ overflow_count.free();
+ strip_names.free();
+ band_stack.free();
+ calls_to_link.free();
+ }
+
+ // Locate the five fixed bands.
+ band &xxx_flags_hi();
+ band &xxx_flags_lo();
+ band &xxx_attr_count();
+ band &xxx_attr_indexes();
+ band &xxx_attr_calls();
+ band &fixed_band(int e_class_xxx);
+
+ // Register a new layout, and make bands for it.
+ layout_definition *defineLayout(int idx, const char *name, const char *layout);
+ layout_definition *defineLayout(int idx, entry *nameEntry, const char *layout);
+ band **buildBands(layout_definition *lo);
+
+ // Parse a layout string or part of one, recursively if necessary.
+ const char *parseLayout(const char *lp, band **&res, int curCble);
+ const char *parseNumeral(const char *lp, int &res);
+ const char *parseIntLayout(const char *lp, band *&res, byte le_kind,
+ bool can_be_signed = false);
+ band **popBody(int band_stack_base); // pops a body off band_stack
+
+ // Read data into the bands of the idx-th layout.
+ void readBandData(int idx); // parse layout, make bands, read data
+ void readBandData(band **body, uint32_t count); // recursive helper
+
+ layout_definition *getLayout(uint32_t idx)
+ {
+ if (idx >= (uint32_t)layouts.length())
+ return nullptr;
+ return (layout_definition *)layouts.get(idx);
+ }
+
+ void setHaveLongFlags(bool z)
+ {
+ assert(flag_limit == 0); // not set up yet
+ flag_limit = (z ? X_ATTR_LIMIT_FLAGS_HI : X_ATTR_LIMIT_NO_FLAGS_HI);
+ }
+ bool haveLongFlags()
+ {
+ assert(flag_limit == X_ATTR_LIMIT_NO_FLAGS_HI ||
+ flag_limit == X_ATTR_LIMIT_FLAGS_HI);
+ return flag_limit == X_ATTR_LIMIT_FLAGS_HI;
+ }
+
+ // Return flag_count if idx is predef and not redef, else zero.
+ int predefCount(uint32_t idx);
+
+ bool isRedefined(uint32_t idx)
+ {
+ if (idx >= flag_limit)
+ return false;
+ return (bool)((redef >> idx) & 1);
+ }
+ bool isPredefined(uint32_t idx)
+ {
+ if (idx >= flag_limit)
+ return false;
+ return (bool)(((predef & ~redef) >> idx) & 1);
+ }
+ uint64_t flagIndexMask()
+ {
+ return (predef | redef);
+ }
+ bool isIndex(uint32_t idx)
+ {
+ assert(flag_limit != 0); // must be set up already
+ if (idx < flag_limit)
+ return (bool)(((predef | redef) >> idx) & 1);
+ else
+ return (idx - flag_limit < (uint32_t)overflow_count.length());
+ }
+ int &getCount(uint32_t idx)
+ {
+ assert(isIndex(idx));
+ if (idx < flag_limit)
+ return flag_count[idx];
+ else
+ return overflow_count.get(idx - flag_limit);
+ }
+ };
+
+ attr_definitions attr_defs[ATTR_CONTEXT_LIMIT];
+
+ // Initialization
+ void init(read_input_fn_t input_fn = nullptr);
+ // Resets to a known sane state
+ void reset();
+ // Deallocates all storage.
+ void free();
+ // Deallocates temporary storage (volatile after next client call).
+ void free_temps()
+ {
+ tsmallbuf.init();
+ tmallocs.freeAll();
+ }
+
+ // Option management methods
+ bool set_option(const char *option, const char *value);
+ const char *get_option(const char *option);
+
+ // Fetching input.
+ bool ensure_input(int64_t more);
+ byte *input_scan()
+ {
+ return rp;
+ }
+ size_t input_remaining()
+ {
+ return rplimit - rp;
+ }
+ size_t input_consumed()
+ {
+ return rp - input.base();
+ }
+
+ // Entry points to the unpack engine
+ static int run(int argc, char **argv); // Unix-style entry point.
+ void check_options();
+ void start(void *packptr = nullptr, size_t len = 0);
+ void write_file_to_jar(file *f);
+ void finish();
+
+ // Public post unpack methods
+ int get_files_remaining()
+ {
+ return files_remaining;
+ }
+ int get_segments_remaining()
+ {
+ return archive_next_count;
+ }
+ file *get_next_file(); // returns nullptr on last file
+
+ // General purpose methods
+ void *alloc(size_t size)
+ {
+ return alloc_heap(size, true);
+ }
+ void *temp_alloc(size_t size)
+ {
+ return alloc_heap(size, true, true);
+ }
+ void *alloc_heap(size_t size, bool smallOK = false, bool temp = false);
+ void saveTo(bytes &b, const char *str)
+ {
+ saveTo(b, (byte *)str, strlen(str));
+ }
+ void saveTo(bytes &b, bytes &data)
+ {
+ saveTo(b, data.ptr, data.len);
+ }
+ void saveTo(bytes &b, byte *ptr, size_t len); //{ b.ptr = U_NEW...}
+ const char *saveStr(const char *str)
+ {
+ bytes buf;
+ saveTo(buf, str);
+ return buf.strval();
+ }
+ const char *saveIntStr(int num)
+ {
+ char buf[30];
+ sprintf(buf, "%d", num);
+ return saveStr(buf);
+ }
+ static unpacker *current(); // find current instance
+
+ // Output management
+ void set_output(fillbytes *which)
+ {
+ assert(wp == nullptr);
+ which->ensureSize(1 << 12); // covers the average classfile
+ wpbase = which->base();
+ wp = which->limit();
+ wplimit = which->end();
+ }
+ fillbytes *close_output(fillbytes *which = nullptr); // inverse of set_output
+
+ // These take an implicit parameter of wp/wplimit, and resize as necessary:
+ byte *put_space(size_t len); // allocates space at wp, returns pointer
+ size_t put_empty(size_t s)
+ {
+ byte *p = put_space(s);
+ return p - wpbase;
+ }
+ void ensure_put_space(size_t len);
+ void put_bytes(bytes &b)
+ {
+ b.writeTo(put_space(b.len));
+ }
+ void putu1(int n)
+ {
+ putu1_at(put_space(1), n);
+ }
+ void putu1_fast(int n)
+ {
+ putu1_at(wp++, n);
+ }
+ void putu2(int n); // { putu2_at(put_space(2), n); }
+ void putu4(int n); // { putu4_at(put_space(4), n); }
+ void putu8(int64_t n); // { putu8_at(put_space(8), n); }
+ void putref(entry *e); // { putu2_at(put_space(2), putref_index(e, 2)); }
+ void putu1ref(entry *e); // { putu1_at(put_space(1), putref_index(e, 1)); }
+ int putref_index(entry *e, int size); // size in [1..2]
+ void put_label(int curIP, int size); // size in {2,4}
+ void putlayout(band **body);
+ void put_stackmap_type();
+
+ size_t wpoffset()
+ {
+ return (size_t)(wp - wpbase);
+ } // (unvariant across overflow)
+ byte *wp_at(size_t offset)
+ {
+ return wpbase + offset;
+ }
+ uint32_t to_bci(uint32_t bii);
+ void get_code_header(int &max_stack, int &max_na_locals, int &handler_count, int &cflags);
+ band *ref_band_for_self_op(int bc, bool &isAloadVar, int &origBCVar);
+ band *ref_band_for_op(int bc);
+
+ // Definitions of standard classfile int formats:
+ static void putu1_at(byte *wp, int n)
+ {
+ assert(n == (n & 0xFF));
+ wp[0] = n;
+ }
+ static void putu2_at(byte *wp, int n);
+ static void putu4_at(byte *wp, int n);
+ static void putu8_at(byte *wp, int64_t n);
+
+ // Private stuff
+ void reset_cur_classfile();
+ void write_classfile_tail();
+ void write_classfile_head();
+ void write_code();
+ void write_bc_ops();
+ void write_members(int num, int attrc); // attrc=ATTR_CONTEXT_FIELD/METHOD
+ int write_attrs(int attrc, uint64_t indexBits);
+
+ // The readers
+ void read_bands();
+ void read_file_header();
+ void read_cp();
+ void read_cp_counts(value_stream &hdr);
+ void read_attr_defs();
+ void read_ics();
+ void read_attrs(int attrc, int obj_count);
+ void read_classes();
+ void read_code_headers();
+ void read_bcs();
+ void read_bc_ops();
+ void read_files();
+ void read_Utf8_values(entry *cpMap, int len);
+ void read_single_words(band &cp_band, entry *cpMap, int len);
+ void read_double_words(band &cp_bands, entry *cpMap, int len);
+ void read_single_refs(band &cp_band, byte refTag, entry *cpMap, int len);
+ void read_double_refs(band &cp_band, byte ref1Tag, byte ref2Tag, entry *cpMap, int len);
+ void read_signature_values(entry *cpMap, int len);
+};
diff --git a/depends/pack200/src/unpack200.cpp b/depends/pack200/src/unpack200.cpp
new file mode 100644
index 00000000..22b7f3b0
--- /dev/null
+++ b/depends/pack200/src/unpack200.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#include <time.h>
+#include <stdint.h>
+
+#include "constants.h"
+#include "utils.h"
+#include "defines.h"
+#include "bytes.h"
+#include "coding.h"
+#include "unpack200.h"
+#include "unpack.h"
+#include "zip.h"
+
+// Callback for fetching data, Unix style.
+static int64_t read_input_via_stdio(unpacker *u, void *buf, int64_t minlen, int64_t maxlen)
+{
+ assert(u->infileptr != nullptr);
+ assert(minlen <= maxlen); // don't talk nonsense
+ int64_t numread = 0;
+ char *bufptr = (char *)buf;
+ while (numread < minlen)
+ {
+ // read available input, up to buf.length or maxlen
+ int readlen = (1 << 16);
+ if (readlen > (maxlen - numread))
+ readlen = (int)(maxlen - numread);
+ int nr = 0;
+
+ nr = (int)fread(bufptr, 1, readlen, u->infileptr);
+ if (nr <= 0)
+ {
+ if (errno != EINTR)
+ break;
+ nr = 0;
+ }
+ numread += nr;
+ bufptr += nr;
+ assert(numread <= maxlen);
+ }
+ return numread;
+}
+
+enum
+{
+ EOF_MAGIC = 0,
+ BAD_MAGIC = -1
+};
+
+static int read_magic(unpacker *u, char peek[], int peeklen)
+{
+ assert(peeklen == 4); // magic numbers are always 4 bytes
+ int64_t nr = (u->read_input_fn)(u, peek, peeklen, peeklen);
+ if (nr != peeklen)
+ {
+ return (nr == 0) ? EOF_MAGIC : BAD_MAGIC;
+ }
+ int magic = 0;
+ for (int i = 0; i < peeklen; i++)
+ {
+ magic <<= 8;
+ magic += peek[i] & 0xFF;
+ }
+ return magic;
+}
+
+void unpack_200(FILE *input, FILE *output)
+{
+ unpacker u;
+ u.init(read_input_via_stdio);
+
+ // initialize jar output
+ // the output takes ownership of the file handle
+ jar jarout;
+ jarout.init(&u);
+ jarout.jarfp = output;
+
+ // the input doesn't
+ u.infileptr = input;
+
+ // read the magic!
+ char peek[4];
+ int magic;
+ magic = read_magic(&u, peek, (int)sizeof(peek));
+
+ // if it is a gzip encoded file, we need an extra gzip input filter
+ if ((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC)
+ {
+ gunzip *gzin = NEW(gunzip, 1);
+ gzin->init(&u);
+ // FIXME: why the side effects? WHY?
+ u.gzin->start(magic);
+ u.start();
+ }
+ else
+ {
+ // otherwise, feed the bytes to the unpacker directly
+ u.start(peek, sizeof(peek));
+ }
+
+ // Note: The checks to u.aborting() are necessary to gracefully
+ // terminate processing when the first segment throws an error.
+ for (;;)
+ {
+ // Each trip through this loop unpacks one segment
+ // and then resets the unpacker.
+ for (unpacker::file *filep; (filep = u.get_next_file()) != nullptr;)
+ {
+ u.write_file_to_jar(filep);
+ }
+
+ // Peek ahead for more data.
+ magic = read_magic(&u, peek, (int)sizeof(peek));
+ if (magic != (int)JAVA_PACKAGE_MAGIC)
+ {
+ // we do not feel strongly about this kind of thing...
+ /*
+ if (magic != EOF_MAGIC)
+ unpack_abort("garbage after end of pack archive");
+ */
+ break; // all done
+ }
+
+ // Release all storage from parsing the old segment.
+ u.reset();
+ // Restart, beginning with the peek-ahead.
+ u.start(peek, sizeof(peek));
+ }
+ u.finish();
+ u.free(); // tidy up malloc blocks
+ fclose(input);
+}
diff --git a/depends/pack200/src/utils.cpp b/depends/pack200/src/utils.cpp
new file mode 100644
index 00000000..0b7d91ca
--- /dev/null
+++ b/depends/pack200/src/utils.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdint.h>
+
+#include <sys/stat.h>
+
+#ifdef _MSC_VER
+#include <direct.h>
+#include <io.h>
+#include <process.h>
+#else
+#include <unistd.h>
+#endif
+
+#include "constants.h"
+#include "defines.h"
+#include "bytes.h"
+#include "utils.h"
+
+#include "unpack.h"
+
+void *must_malloc(size_t size)
+{
+ size_t msize = size;
+ void *ptr = (msize > PSIZE_MAX) ? nullptr : malloc(msize);
+ if (ptr != nullptr)
+ {
+ memset(ptr, 0, size);
+ }
+ else
+ {
+ throw std::runtime_error(ERROR_ENOMEM);
+ }
+ return ptr;
+}
+
+void unpack_abort(const char *msg)
+{
+ if (msg == nullptr)
+ msg = "corrupt pack file or internal error";
+ throw std::runtime_error(msg);
+}
diff --git a/depends/pack200/src/utils.h b/depends/pack200/src/utils.h
new file mode 100644
index 00000000..5a3dc8f6
--- /dev/null
+++ b/depends/pack200/src/utils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// Definitions of our util functions
+
+#include <stdexcept>
+
+void *must_malloc(size_t size);
+
+// overflow management
+#define OVERFLOW ((size_t) - 1)
+#define PSIZE_MAX (OVERFLOW / 2) /* normal size limit */
+
+inline size_t scale_size(size_t size, size_t scale)
+{
+ return (size > PSIZE_MAX / scale) ? OVERFLOW : size * scale;
+}
+
+inline size_t add_size(size_t size1, size_t size2)
+{
+ return ((size1 | size2 | (size1 + size2)) > PSIZE_MAX) ? OVERFLOW : size1 + size2;
+}
+
+inline size_t add_size(size_t size1, size_t size2, int size3)
+{
+ return add_size(add_size(size1, size2), size3);
+}
+
+struct unpacker;
+/// This throws an exception!
+extern void unpack_abort(const char *msg = nullptr);
diff --git a/depends/pack200/src/zip.cpp b/depends/pack200/src/zip.cpp
new file mode 100644
index 00000000..32e8bd50
--- /dev/null
+++ b/depends/pack200/src/zip.cpp
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Note: Lifted from uncrunch.c from jdk sources
+ */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stdint.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#ifndef _MSC_VER
+#include <strings.h>
+#endif
+
+#include "defines.h"
+#include "bytes.h"
+#include "utils.h"
+
+#include "constants.h"
+#include "unpack.h"
+
+#include "zip.h"
+
+#include "zlib.h"
+
+inline uint32_t jar::get_crc32(uint32_t c, uchar *ptr, uint32_t len)
+{
+ return crc32(c, ptr, len);
+}
+
+// FIXME: this is bullshit. Do real endianness detection.
+#ifdef sparc
+#define SWAP_BYTES(a) ((((a) << 8) & 0xff00) | 0x00ff) & (((a) >> 8) | 0xff00)
+#else
+#define SWAP_BYTES(a) (a)
+#endif
+
+#define GET_INT_LO(a) SWAP_BYTES(a & 0xFFFF)
+
+#define GET_INT_HI(a) SWAP_BYTES((a >> 16) & 0xFFFF);
+
+void jar::init(unpacker *u_)
+{
+ BYTES_OF(*this).clear();
+ u = u_;
+ u->jarout = this;
+}
+
+// Write data to the ZIP output stream.
+void jar::write_data(void *buff, int len)
+{
+ while (len > 0)
+ {
+ int rc = (int)fwrite(buff, 1, len, jarfp);
+ if (rc <= 0)
+ {
+ fprintf(stderr, "Error: write on output file failed err=%d\n", errno);
+ exit(1); // Called only from the native standalone unpacker
+ }
+ output_file_offset += rc;
+ buff = ((char *)buff) + rc;
+ len -= rc;
+ }
+}
+
+void jar::add_to_jar_directory(const char *fname, bool store, int modtime, int len, int clen,
+ uint32_t crc)
+{
+ uint32_t fname_length = (uint32_t)strlen(fname);
+ ushort header[23];
+ if (modtime == 0)
+ modtime = default_modtime;
+ uint32_t dostime = get_dostime(modtime);
+
+ header[0] = (ushort)SWAP_BYTES(0x4B50);
+ header[1] = (ushort)SWAP_BYTES(0x0201);
+ header[2] = (ushort)SWAP_BYTES(0xA);
+
+ // required version
+ header[3] = (ushort)SWAP_BYTES(0xA);
+
+ // flags 02 = maximum sub-compression flag
+ header[4] = (store) ? 0x0 : SWAP_BYTES(0x2);
+
+ // Compression method 8=deflate.
+ header[5] = (store) ? 0x0 : SWAP_BYTES(0x08);
+
+ // Last modified date and time.
+ header[6] = (ushort)GET_INT_LO(dostime);
+ header[7] = (ushort)GET_INT_HI(dostime);
+
+ // CRC
+ header[8] = (ushort)GET_INT_LO(crc);
+ header[9] = (ushort)GET_INT_HI(crc);
+
+ // Compressed length:
+ header[10] = (ushort)GET_INT_LO(clen);
+ header[11] = (ushort)GET_INT_HI(clen);
+
+ // Uncompressed length.
+ header[12] = (ushort)GET_INT_LO(len);
+ header[13] = (ushort)GET_INT_HI(len);
+
+ // Filename length
+ header[14] = (ushort)SWAP_BYTES(fname_length);
+ // So called "extra field" length.
+ header[15] = 0;
+ // So called "comment" length.
+ header[16] = 0;
+ // Disk number start
+ header[17] = 0;
+ // File flags => binary
+ header[18] = 0;
+ // More file flags
+ header[19] = 0;
+ header[20] = 0;
+ // Offset within ZIP file.
+ header[21] = (ushort)GET_INT_LO(output_file_offset);
+ header[22] = (ushort)GET_INT_HI(output_file_offset);
+
+ // Copy the whole thing into the central directory.
+ central_directory.append(header, sizeof(header));
+
+ // Copy the fname to the header.
+ central_directory.append(fname, fname_length);
+
+ central_directory_count++;
+}
+
+void jar::write_jar_header(const char *fname, bool store, int modtime, int len, int clen,
+ uint32_t crc)
+{
+ uint32_t fname_length = (uint32_t)strlen(fname);
+ ushort header[15];
+ if (modtime == 0)
+ modtime = default_modtime;
+ uint32_t dostime = get_dostime(modtime);
+
+ // ZIP LOC magic.
+ header[0] = (ushort)SWAP_BYTES(0x4B50);
+ header[1] = (ushort)SWAP_BYTES(0x0403);
+
+ // Version
+ header[2] = (ushort)SWAP_BYTES(0xA);
+
+ // flags 02 = maximum sub-compression flag
+ header[3] = (store) ? 0x0 : SWAP_BYTES(0x2);
+
+ // Compression method = deflate
+ header[4] = (store) ? 0x0 : SWAP_BYTES(0x08);
+
+ // Last modified date and time.
+ header[5] = (ushort)GET_INT_LO(dostime);
+ header[6] = (ushort)GET_INT_HI(dostime);
+
+ // CRC
+ header[7] = (ushort)GET_INT_LO(crc);
+ header[8] = (ushort)GET_INT_HI(crc);
+
+ // Compressed length:
+ header[9] = (ushort)GET_INT_LO(clen);
+ header[10] = (ushort)GET_INT_HI(clen);
+
+ // Uncompressed length.
+ header[11] = (ushort)GET_INT_LO(len);
+ header[12] = (ushort)GET_INT_HI(len);
+
+ // Filename length
+ header[13] = (ushort)SWAP_BYTES(fname_length);
+ // So called "extra field" length.
+ header[14] = 0;
+
+ // Write the LOC header to the output file.
+ write_data(header, (int)sizeof(header));
+
+ // Copy the fname to the header.
+ write_data((char *)fname, (int)fname_length);
+}
+
+void jar::write_central_directory()
+{
+ bytes mc;
+ mc.set("PACK200");
+
+ ushort header[11];
+
+ // Create the End of Central Directory structure.
+ header[0] = (ushort)SWAP_BYTES(0x4B50);
+ header[1] = (ushort)SWAP_BYTES(0x0605);
+ // disk numbers
+ header[2] = 0;
+ header[3] = 0;
+ // Number of entries in central directory.
+ header[4] = (ushort)SWAP_BYTES(central_directory_count);
+ header[5] = (ushort)SWAP_BYTES(central_directory_count);
+ // Size of the central directory}
+ header[6] = (ushort)GET_INT_LO((int)central_directory.size());
+ header[7] = (ushort)GET_INT_HI((int)central_directory.size());
+ // Offset of central directory within disk.
+ header[8] = (ushort)GET_INT_LO(output_file_offset);
+ header[9] = (ushort)GET_INT_HI(output_file_offset);
+ // zipfile comment length;
+ header[10] = (ushort)SWAP_BYTES((int)mc.len);
+
+ // Write the central directory.
+ write_data(central_directory.b);
+
+ // Write the End of Central Directory structure.
+ write_data(header, (int)sizeof(header));
+
+ // Write the comment.
+ write_data(mc);
+}
+
+// Public API
+
+// Open a Jar file and initialize.
+void jar::openJarFile(const char *fname)
+{
+ if (!jarfp)
+ {
+ jarfp = fopen(fname, "wb");
+ if (!jarfp)
+ {
+ fprintf(stderr, "Error: Could not open jar file: %s\n", fname);
+ exit(3); // Called only from the native standalone unpacker
+ }
+ }
+}
+
+// Add a ZIP entry and copy the file data
+void jar::addJarEntry(const char *fname, bool deflate_hint, int modtime, bytes &head,
+ bytes &tail)
+{
+ int len = (int)(head.len + tail.len);
+ int clen = 0;
+
+ uint32_t crc = get_crc32(0, Z_NULL, 0);
+ if (head.len != 0)
+ crc = get_crc32(crc, (uchar *)head.ptr, (uint32_t)head.len);
+ if (tail.len != 0)
+ crc = get_crc32(crc, (uchar *)tail.ptr, (uint32_t)tail.len);
+
+ bool deflate = (deflate_hint && len > 0);
+
+ if (deflate)
+ {
+ if (deflate_bytes(head, tail) == false)
+ {
+ deflate = false;
+ }
+ }
+ clen = (int)((deflate) ? deflated.size() : len);
+ add_to_jar_directory(fname, !deflate, modtime, len, clen, crc);
+ write_jar_header(fname, !deflate, modtime, len, clen, crc);
+
+ if (deflate)
+ {
+ write_data(deflated.b);
+ }
+ else
+ {
+ write_data(head);
+ write_data(tail);
+ }
+}
+
+// Add a ZIP entry for a directory name no data
+void jar::addDirectoryToJarFile(const char *dir_name)
+{
+ bool store = true;
+ add_to_jar_directory((const char *)dir_name, store, default_modtime, 0, 0, 0);
+ write_jar_header((const char *)dir_name, store, default_modtime, 0, 0, 0);
+}
+
+// Write out the central directory and close the jar file.
+void jar::closeJarFile(bool central)
+{
+ if (jarfp)
+ {
+ fflush(jarfp);
+ if (central)
+ write_central_directory();
+ fflush(jarfp);
+ fclose(jarfp);
+ }
+ reset();
+}
+
+/* Convert the date y/n/d and time h:m:s to a four byte DOS date and
+ * time (date in high two bytes, time in low two bytes allowing magnitude
+ * comparison).
+ */
+inline uint32_t jar::dostime(int y, int n, int d, int h, int m, int s)
+{
+ return y < 1980 ? dostime(1980, 1, 1, 0, 0, 0)
+ : (((uint32_t)y - 1980) << 25) | ((uint32_t)n << 21) | ((uint32_t)d << 16) |
+ ((uint32_t)h << 11) | ((uint32_t)m << 5) | ((uint32_t)s >> 1);
+}
+/*
+#ifdef _REENTRANT // solaris
+extern "C" struct tm *gmtime_r(const time_t *, struct tm *);
+#else
+#define gmtime_r(t, s) gmtime(t)
+#endif
+*/
+/*
+ * Return the Unix time in DOS format
+ */
+uint32_t jar::get_dostime(int modtime)
+{
+ // see defines.h
+ if (modtime != 0 && modtime == modtime_cache)
+ return dostime_cache;
+ if (modtime != 0 && default_modtime == 0)
+ default_modtime = modtime; // catch a reasonable default
+ time_t t = modtime;
+ struct tm sbuf;
+ (void)memset((void *)&sbuf, 0, sizeof(sbuf));
+ struct tm *s = gmtime_r(&t, &sbuf);
+ modtime_cache = modtime;
+ dostime_cache =
+ dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday, s->tm_hour, s->tm_min, s->tm_sec);
+ // printf("modtime %d => %d\n", modtime_cache, dostime_cache);
+ return dostime_cache;
+}
+
+/* Returns true on success, and will set the clen to the compressed
+ length, the caller should verify if true and clen less than the
+ input data
+*/
+bool jar::deflate_bytes(bytes &head, bytes &tail)
+{
+ int len = (int)(head.len + tail.len);
+
+ z_stream zs;
+ BYTES_OF(zs).clear();
+
+ // NOTE: the window size should always be -MAX_WBITS normally -15.
+ // unzip/zipup.c and java/Deflater.c
+
+ int error =
+ deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
+ if (error != Z_OK)
+ {
+ /*
+ switch (error)
+ {
+ case Z_MEM_ERROR:
+ PRINTCR((2, "Error: deflate error : Out of memory \n"));
+ break;
+ case Z_STREAM_ERROR:
+ PRINTCR((2, "Error: deflate error : Invalid compression level \n"));
+ break;
+ case Z_VERSION_ERROR:
+ PRINTCR((2, "Error: deflate error : Invalid version\n"));
+ break;
+ default:
+ PRINTCR((2, "Error: Internal deflate error error = %d\n", error));
+ }
+ */
+ return false;
+ }
+
+ deflated.empty();
+ zs.next_out = (uchar *)deflated.grow(len + (len / 2));
+ zs.avail_out = (int)deflated.size();
+
+ zs.next_in = (uchar *)head.ptr;
+ zs.avail_in = (int)head.len;
+
+ bytes *first = &head;
+ bytes *last = &tail;
+ if (last->len == 0)
+ {
+ first = nullptr;
+ last = &head;
+ }
+ else if (first->len == 0)
+ {
+ first = nullptr;
+ }
+
+ if (first != nullptr && error == Z_OK)
+ {
+ zs.next_in = (uchar *)first->ptr;
+ zs.avail_in = (int)first->len;
+ error = deflate(&zs, Z_NO_FLUSH);
+ }
+ if (error == Z_OK)
+ {
+ zs.next_in = (uchar *)last->ptr;
+ zs.avail_in = (int)last->len;
+ error = deflate(&zs, Z_FINISH);
+ }
+ if (error == Z_STREAM_END)
+ {
+ if (len > (int)zs.total_out)
+ {
+ deflated.b.len = zs.total_out;
+ deflateEnd(&zs);
+ return true;
+ }
+ deflateEnd(&zs);
+ return false;
+ }
+
+ deflateEnd(&zs);
+ return false;
+}
+
+// Callback for fetching data from a GZIP input stream
+static int64_t read_input_via_gzip(unpacker *u, void *buf, int64_t minlen, int64_t maxlen)
+{
+ assert(minlen <= maxlen); // don't talk nonsense
+ int64_t numread = 0;
+ char *bufptr = (char *)buf;
+ char *inbuf = u->gzin->inbuf;
+ size_t inbuflen = sizeof(u->gzin->inbuf);
+ unpacker::read_input_fn_t read_gzin_fn = (unpacker::read_input_fn_t)u->gzin->read_input_fn;
+ z_stream &zs = *(z_stream *)u->gzin->zstream;
+ while (numread < minlen)
+ {
+ int readlen = (1 << 16); // pretty arbitrary
+ if (readlen > (maxlen - numread))
+ readlen = (int)(maxlen - numread);
+ zs.next_out = (uchar *)bufptr;
+ zs.avail_out = readlen;
+ if (zs.avail_in == 0)
+ {
+ zs.avail_in = (int)read_gzin_fn(u, inbuf, 1, inbuflen);
+ zs.next_in = (uchar *)inbuf;
+ }
+ int error = inflate(&zs, Z_NO_FLUSH);
+ if (error != Z_OK && error != Z_STREAM_END)
+ {
+ unpack_abort("error inflating input");
+ break;
+ }
+ int nr = readlen - zs.avail_out;
+ numread += nr;
+ bufptr += nr;
+ assert(numread <= maxlen);
+ if (error == Z_STREAM_END)
+ {
+ enum
+ {
+ TRAILER_LEN = 8
+ };
+ // skip 8-byte trailer
+ if (zs.avail_in >= TRAILER_LEN)
+ {
+ zs.avail_in -= TRAILER_LEN;
+ }
+ else
+ {
+ // Bug: 5023768,we read past the TRAILER_LEN to see if there is
+ // any extraneous data, as we dont support concatenated .gz
+ // files just yet.
+ int extra = (int)read_gzin_fn(u, inbuf, 1, inbuflen);
+ zs.avail_in += extra - TRAILER_LEN;
+ }
+ // %%% should check final CRC and length here
+ // %%% should check for concatenated *.gz files here
+ if (zs.avail_in > 0)
+ unpack_abort("garbage after end of deflated input stream");
+ // pop this filter off:
+ u->gzin->free();
+ break;
+ }
+ }
+
+ // fprintf(u->errstrm, "readInputFn(%d,%d) => %d (gunzip)\n",
+ // (int)minlen, (int)maxlen, (int)numread);
+ return numread;
+}
+
+void gunzip::init(unpacker *u_)
+{
+ BYTES_OF(*this).clear();
+ u = u_;
+ assert(u->gzin == nullptr); // once only, please
+ read_input_fn = (void *)u->read_input_fn;
+ zstream = NEW(z_stream, 1);
+ u->gzin = this;
+ u->read_input_fn = read_input_via_gzip;
+}
+
+void gunzip::start(int magic)
+{
+ assert((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC);
+ int gz_flg = (magic & 0xFF); // keep "flg", discard other 3 bytes
+ enum
+ {
+ FHCRC = (1 << 1),
+ FEXTRA = (1 << 2),
+ FNAME = (1 << 3),
+ FCOMMENT = (1 << 4)
+ };
+ char gz_mtime[4];
+ char gz_xfl[1];
+ char gz_os[1];
+ char gz_extra_len[2];
+ char gz_hcrc[2];
+ char gz_ignore;
+ // do not save extra, name, comment
+ read_fixed_field(gz_mtime, sizeof(gz_mtime));
+ read_fixed_field(gz_xfl, sizeof(gz_xfl));
+ read_fixed_field(gz_os, sizeof(gz_os));
+ if (gz_flg & FEXTRA)
+ {
+ read_fixed_field(gz_extra_len, sizeof(gz_extra_len));
+ int extra_len = gz_extra_len[0] & 0xFF;
+ extra_len += (gz_extra_len[1] & 0xFF) << 8;
+ for (; extra_len > 0; extra_len--)
+ {
+ read_fixed_field(&gz_ignore, 1);
+ }
+ }
+ int null_terms = 0;
+ if (gz_flg & FNAME)
+ null_terms++;
+ if (gz_flg & FCOMMENT)
+ null_terms++;
+ for (; null_terms; null_terms--)
+ {
+ for (;;)
+ {
+ gz_ignore = 0;
+ read_fixed_field(&gz_ignore, 1);
+ if (gz_ignore == 0)
+ break;
+ }
+ }
+ if (gz_flg & FHCRC)
+ read_fixed_field(gz_hcrc, sizeof(gz_hcrc));
+
+ // now the input stream is ready to read into the inflater
+ int error = inflateInit2((z_stream *)zstream, -MAX_WBITS);
+ if (error != Z_OK)
+ {
+ unpack_abort("cannot create input");
+ }
+}
+
+void gunzip::free()
+{
+ assert(u->gzin == this);
+ u->gzin = nullptr;
+ u->read_input_fn = (unpacker::read_input_fn_t) this->read_input_fn;
+ inflateEnd((z_stream *)zstream);
+ ::free(zstream);
+ zstream = nullptr;
+ ::free(this);
+}
+
+void gunzip::read_fixed_field(char *buf, size_t buflen)
+{
+ int64_t nr = ((unpacker::read_input_fn_t)read_input_fn)(u, buf, buflen, buflen);
+ if ((size_t)nr != buflen)
+ unpack_abort("short stream header");
+}
diff --git a/depends/pack200/src/zip.h b/depends/pack200/src/zip.h
new file mode 100644
index 00000000..67ec24da
--- /dev/null
+++ b/depends/pack200/src/zip.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+#include <stdint.h>
+typedef unsigned short ushort;
+typedef unsigned int uint32_t;
+typedef unsigned char uchar;
+
+struct unpacker;
+
+struct jar
+{
+ // JAR file writer
+ FILE *jarfp;
+ int default_modtime;
+
+ // Used by unix2dostime:
+ int modtime_cache;
+ uint32_t dostime_cache;
+
+ // Private members
+ fillbytes central_directory;
+ ushort central_directory_count;
+ uint32_t output_file_offset;
+ fillbytes deflated; // temporary buffer
+
+ // pointer to outer unpacker, for error checks etc.
+ unpacker *u;
+
+ // Public Methods
+ void openJarFile(const char *fname);
+ void addJarEntry(const char *fname, bool deflate_hint, int modtime, bytes &head,
+ bytes &tail);
+ void addDirectoryToJarFile(const char *dir_name);
+ void closeJarFile(bool central);
+
+ void init(unpacker *u_);
+
+ void free()
+ {
+ central_directory.free();
+ deflated.free();
+ }
+
+ void reset()
+ {
+ free();
+ init(u);
+ }
+
+ // Private Methods
+ void write_data(void *ptr, int len);
+ void write_data(bytes &b)
+ {
+ write_data(b.ptr, (int)b.len);
+ }
+ void add_to_jar_directory(const char *fname, bool store, int modtime, int len, int clen,
+ uint32_t crc);
+ void write_jar_header(const char *fname, bool store, int modtime, int len, int clen,
+ unsigned int crc);
+ void write_central_directory();
+ uint32_t dostime(int y, int n, int d, int h, int m, int s);
+ uint32_t get_dostime(int modtime);
+
+ // The definitions of these depend on the NO_ZLIB option:
+ bool deflate_bytes(bytes &head, bytes &tail);
+ static uint32_t get_crc32(uint32_t c, unsigned char *ptr, uint32_t len);
+};
+
+struct gunzip
+{
+ // optional gzip input stream control block
+
+ // pointer to outer unpacker, for error checks etc.
+ unpacker *u;
+
+ void *read_input_fn; // underlying \bchar\b stream
+ void *zstream; // inflater state
+ char inbuf[1 << 14]; // input buffer
+
+ void init(unpacker *u_); // pushes new value on u->read_input_fn
+
+ void free();
+
+ void start(int magic);
+
+ // private stuff
+ void read_fixed_field(char *buf, size_t buflen);
+};
diff --git a/depends/quazip/CMakeLists.txt b/depends/quazip/CMakeLists.txt
new file mode 100644
index 00000000..76da0a59
--- /dev/null
+++ b/depends/quazip/CMakeLists.txt
@@ -0,0 +1,42 @@
+project(quazip)
+
+# Find ZLIB for quazip
+# Use system zlib on unix and Qt ZLIB on Windows
+IF(UNIX)
+ find_package(ZLIB REQUIRED)
+ELSE(UNIX)
+ get_filename_component (ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
+ SET(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
+ SET(ZLIB_LIBRARIES "")
+ IF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
+ MESSAGE("Please specify a valid zlib include dir")
+ ENDIF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
+ENDIF(UNIX)
+
+# set all include directories for in and out of source builds
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${ZLIB_INCLUDE_DIRS}
+)
+
+# include with QT_USE selected library parts
+# INCLUDE(${QT_USE_FILE})
+
+file(GLOB SRCS "*.c" "*.cpp")
+file(GLOB PUBLIC_HEADERS "*.h")
+
+# Static link!
+ADD_DEFINITIONS(-DQUAZIP_STATIC)
+
+#qt5_wrap_cpp(MOC_SRCS ${PUBLIC_HEADERS})
+#set(SRCS ${SRCS} ${MOC_SRCS})
+
+#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+add_library(quazip STATIC ${SRCS})
+QT5_USE_MODULES(quazip Core)
+target_link_libraries(quazip ${ZLIB_LIBRARIES})
+
+#install(FILES ${PUBLIC_HEADERS} DESTINATION include/quazip)
+#install(TARGETS quazip LIBRARY DESTINATION ${LIB_DESTINATION} ARCHIVE DESTINATION ${LIB_DESTINATION} RUNTIME DESTINATION ${LIB_DESTINATION})
diff --git a/depends/quazip/JlCompress.cpp b/depends/quazip/JlCompress.cpp
new file mode 100644
index 00000000..a6cf0eaf
--- /dev/null
+++ b/depends/quazip/JlCompress.cpp
@@ -0,0 +1,522 @@
+#include "JlCompress.h"
+#include <QDebug>
+
+bool JlCompress::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;
+}
+
+/**OK
+ * Comprime il file fileName, nell'oggetto zip, con il nome fileDest.
+ *
+ * La funzione fallisce se:
+ * * zip==NULL;
+ * * l'oggetto zip e stato aperto in una modalita non compatibile con l'aggiunta di file;
+ * * non e possibile aprire il file d'origine;
+ * * non e possibile creare il file all'interno dell'oggetto zip;
+ * * si e rilevato un errore nella copia dei dati;
+ * * non e stato possibile chiudere il file all'interno dell'oggetto zip;
+ */
+bool JlCompress::compressFile(QuaZip* zip, QString fileName, QString fileDest) {
+ // zip: oggetto dove aggiungere il file
+ // fileName: nome del file reale
+ // fileDest: nome del file all'interno del file compresso
+
+ // Controllo l'apertura dello zip
+ if (!zip) return false;
+ if (zip->getMode()!=QuaZip::mdCreate &&
+ zip->getMode()!=QuaZip::mdAppend &&
+ zip->getMode()!=QuaZip::mdAdd) return false;
+
+ // Apro il file originale
+ QFile inFile;
+ inFile.setFileName(fileName);
+ if(!inFile.open(QIODevice::ReadOnly)) return false;
+
+ // Apro il file risulato
+ QuaZipFile outFile(zip);
+ if(!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()))) return false;
+
+ // Copio i dati
+ if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK) {
+ return false;
+ }
+
+ // Chiudo i file
+ outFile.close();
+ if (outFile.getZipError()!=UNZ_OK) return false;
+ inFile.close();
+
+ return true;
+}
+
+/**OK
+ * Comprime la cartella dir nel file fileCompressed, se recursive e true allora
+ * comprime anche le sotto cartelle. I nomi dei file preceduti dal path creato
+ * togliendo il pat della cartella origDir al path della cartella dir.
+ * Se la funzione fallisce restituisce false e cancella il file che si e tentato
+ * di creare.
+ *
+ * La funzione fallisce se:
+ * * zip==NULL;
+ * * l'oggetto zip e stato aperto in una modalita non compatibile con l'aggiunta di file;
+ * * la cartella dir non esiste;
+ * * la compressione di una sotto cartella fallisce (1);
+ * * la compressione di un file fallisce;
+ * (1) La funzione si richiama in maniera ricorsiva per comprimere le sotto cartelle
+ * dunque gli errori di compressione di una sotto cartella sono gli stessi di questa
+ * funzione.
+ */
+bool JlCompress::compressSubDir( QuaZip* parentZip, QString dir, QString parentDir, bool recursive, QSet<QString>& added )
+{
+ // zip: oggetto dove aggiungere il file
+ // dir: cartella reale corrente
+ // origDir: cartella reale originale
+ // (path(dir)-path(origDir)) = path interno all'oggetto zip
+
+ // Controllo l'apertura dello zip
+ if (!parentZip ) return false;
+ if ( parentZip->getMode()!=QuaZip::mdCreate &&
+ parentZip->getMode()!=QuaZip::mdAppend &&
+ parentZip->getMode()!=QuaZip::mdAdd) return false;
+
+ // Controllo la cartella
+ QDir directory(dir);
+ if (!directory.exists()) return false;
+
+ // Se comprimo anche le sotto cartelle
+ if (recursive) {
+ // Per ogni sotto cartella
+ QFileInfoList files = directory.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
+ Q_FOREACH (QFileInfo file, files)
+ {
+ // Comprimo la sotto cartella
+ if(!compressSubDir( parentZip,file.absoluteFilePath(),parentDir,recursive,added)) return false;
+ }
+ }
+
+ // Per ogni file nella cartella
+ QFileInfoList files = directory.entryInfoList(QDir::Files);
+ QDir origDirectory( parentDir );
+ Q_FOREACH (QFileInfo file, files)
+ {
+ // Se non e un file o e il file compresso che sto creando
+ if(!file.isFile()||file.absoluteFilePath()==parentZip->getZipName()) continue;
+
+ // Creo il nome relativo da usare all'interno del file compresso
+ QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
+
+ // Comprimo il file
+ if (!compressFile( parentZip,file.absoluteFilePath(),filename))
+ return false;
+ added.insert(filename);
+ }
+
+ return true;
+}
+
+/**OK
+ * Estrae il file fileName, contenuto nell'oggetto zip, con il nome fileDest.
+ * Se la funzione fallisce restituisce false e cancella il file che si e tentato di estrarre.
+ *
+ * La funzione fallisce se:
+ * * zip==NULL;
+ * * l'oggetto zip e stato aperto in una modalita non compatibile con l'estrazione di file;
+ * * non e possibile aprire il file all'interno dell'oggetto zip;
+ * * non e possibile creare il file estratto;
+ * * si e rilevato un errore nella copia dei dati (1);
+ * * non e stato possibile chiudere il file all'interno dell'oggetto zip (1);
+ *
+ * (1): prima di uscire dalla funzione cancella il file estratto.
+ */
+bool JlCompress::extractFile(QuaZip* zip, QString fileName, QString fileDest) {
+ // zip: oggetto dove aggiungere il file
+ // filename: nome del file reale
+ // fileincompress: nome del file all'interno del file compresso
+
+ // Controllo l'apertura dello zip
+ if (!zip) return false;
+ if (zip->getMode()!=QuaZip::mdUnzip) return false;
+
+ // Apro il file compresso
+ 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 (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) {
+ return false;
+ }
+
+ if (QFileInfo(fileDest).isDir())
+ return true;
+
+ // Apro il file risultato
+ QFile outFile;
+ outFile.setFileName(fileDest);
+ if(!outFile.open(QIODevice::WriteOnly)) return false;
+
+ // Copio i dati
+ if (!copyData(inFile, outFile) || inFile.getZipError()!=UNZ_OK) {
+ outFile.close();
+ removeFile(QStringList(fileDest));
+ return false;
+ }
+ outFile.close();
+
+ // Chiudo i file
+ inFile.close();
+ if (inFile.getZipError()!=UNZ_OK) {
+ removeFile(QStringList(fileDest));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Rimuove i file il cui nome e specificato all'interno di listFile.
+ * Restituisce true se tutti i file sono stati cancellati correttamente, attenzione
+ * perche puo restituire false anche se alcuni file non esistevano e si e tentato
+ * di cancellarli.
+ */
+bool JlCompress::removeFile(QStringList listFile) {
+ bool ret = true;
+ // Per ogni file
+ for (int i=0; i<listFile.count(); i++) {
+ // Lo elimino
+ ret = ret && QFile::remove(listFile.at(i));
+ }
+ return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+/**OK
+ * Comprime il file fileName nel file fileCompressed.
+ * Se la funzione fallisce restituisce false e cancella il file che si e tentato
+ * di creare.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * la compressione del file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+bool JlCompress::compressFile(QString fileCompressed, QString file) {
+ // Creo lo zip
+ QuaZip zip(fileCompressed);
+ QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
+ if(!zip.open(QuaZip::mdCreate)) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ // Aggiungo il file
+ if (!compressFile(&zip,file,QFileInfo(file).fileName())) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ // Chiudo il file zip
+ zip.close();
+ if(zip.getZipError()!=0) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ return true;
+}
+
+/**OK
+ * Comprime i file specificati in files nel file fileCompressed.
+ * Se la funzione fallisce restituisce false e cancella il file che si e tentato
+ * di creare.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * la compressione di un file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+bool JlCompress::compressFiles(QString fileCompressed, QStringList files) {
+ // Creo lo zip
+ QuaZip zip(fileCompressed);
+ QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
+ if(!zip.open(QuaZip::mdCreate)) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ // Comprimo i file
+ QFileInfo info;
+ Q_FOREACH (QString file, files) {
+ info.setFile(file);
+ if (!info.exists() || !compressFile(&zip,file,info.fileName())) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+ }
+
+ // Chiudo il file zip
+ zip.close();
+ if(zip.getZipError()!=0) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ return true;
+}
+
+/**OK
+ * Comprime la cartella dir nel file fileCompressed, se recursive e true allora
+ * comprime anche le sotto cartelle.
+ * Se la funzione fallisce restituisce false e cancella il file che si e tentato
+ * di creare.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * la compressione di un file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+bool JlCompress::compressDir(QString fileCompressed, QString dir, bool recursive) {
+ // Creo lo zip
+ QuaZip zip(fileCompressed);
+ QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
+ if(!zip.open(QuaZip::mdCreate)) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+ QSet<QString> added;
+ // Aggiungo i file e le sotto cartelle
+ if (!compressSubDir(&zip,dir,dir,recursive, added))
+ {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ // Chiudo il file zip
+ zip.close();
+ if(zip.getZipError()!=0) {
+ QFile::remove(fileCompressed);
+ return false;
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+/**OK
+ * Estrae il file fileName, contenuto nel file fileCompressed, con il nome fileDest.
+ * Se fileDest = "" allora il file viene estratto con lo stesso nome con cui e
+ * stato compresso.
+ * Se la funzione fallisce cancella il file che si e tentato di estrarre.
+ * Restituisce il nome assoluto del file estratto.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * l'estrazione del file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+QString JlCompress::extractFile(QString fileCompressed, QString fileName, QString fileDest) {
+ // Apro lo zip
+ QuaZip zip(fileCompressed);
+ if(!zip.open(QuaZip::mdUnzip)) {
+ return QString();
+ }
+
+ // Estraggo il file
+ if (fileDest.isEmpty())
+ fileDest = fileName;
+ if (!extractFile(&zip,fileName,fileDest)) {
+ return QString();
+ }
+
+ // Chiudo il file zip
+ zip.close();
+ if(zip.getZipError()!=0) {
+ removeFile(QStringList(fileDest));
+ return QString();
+ }
+ return QFileInfo(fileDest).absoluteFilePath();
+}
+
+/**OK
+ * Estrae i file specificati in files, contenuti nel file fileCompressed, nella
+ * cartella dir. La struttura a cartelle del file compresso viene rispettata.
+ * Se dir = "" allora il file viene estratto nella cartella corrente.
+ * Se la funzione fallisce cancella i file che si e tentato di estrarre.
+ * Restituisce i nomi assoluti dei file estratti.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * l'estrazione di un file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+QStringList JlCompress::extractFiles(QString fileCompressed, QStringList files, QString dir) {
+ // Creo lo zip
+ QuaZip zip(fileCompressed);
+ if(!zip.open(QuaZip::mdUnzip)) {
+ return QStringList();
+ }
+
+ // Estraggo i file
+ QStringList extracted;
+ for (int i=0; i<files.count(); i++) {
+ QString absPath = QDir(dir).absoluteFilePath(files.at(i));
+ if (!extractFile(&zip, files.at(i), absPath)) {
+ removeFile(extracted);
+ return QStringList();
+ }
+ extracted.append(absPath);
+ }
+
+ // Chiudo il file zip
+ zip.close();
+ if(zip.getZipError()!=0) {
+ removeFile(extracted);
+ return QStringList();
+ }
+
+ return extracted;
+}
+
+QStringList JlCompress::extractWithExceptions(QString fileCompressed, QString dir, QStringList exceptions)
+{
+ QuaZip zip(fileCompressed);
+ if(!zip.open(QuaZip::mdUnzip))
+ {
+ return QStringList();
+ }
+
+ QDir directory(dir);
+ QStringList extracted;
+ if (!zip.goToFirstFile())
+ {
+ return QStringList();
+ }
+ do
+ {
+ QString name = zip.getCurrentFileName();
+ bool ok = true;
+ for(auto str: exceptions)
+ {
+ if(name.startsWith(str))
+ {
+ ok = false;
+ break;
+ }
+ }
+ if(!ok)
+ continue;
+ QString absFilePath = directory.absoluteFilePath(name);
+ if (!JlCompress::extractFile(&zip, "", absFilePath))
+ {
+ JlCompress::removeFile(extracted);
+ return QStringList();
+ }
+ extracted.append(absFilePath);
+ } while (zip.goToNextFile());
+
+ zip.close();
+ if(zip.getZipError()!=0)
+ {
+ JlCompress::removeFile(extracted);
+ return QStringList();
+ }
+
+ return extracted;
+}
+
+/**OK
+ * Estrae il file fileCompressed nella cartella dir.
+ * Se dir = "" allora il file viene estratto nella cartella corrente.
+ * Se la funzione fallisce cancella i file che si e tentato di estrarre.
+ * Restituisce i nomi assoluti dei file estratti.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * la compressione di un file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+QStringList JlCompress::extractDir(QString fileCompressed, QString dir) {
+ // Apro lo zip
+ QuaZip zip(fileCompressed);
+ if(!zip.open(QuaZip::mdUnzip)) {
+ return QStringList();
+ }
+
+ QDir directory(dir);
+ QStringList extracted;
+ if (!zip.goToFirstFile()) {
+ return QStringList();
+ }
+ do {
+ QString name = zip.getCurrentFileName();
+ QString absFilePath = directory.absoluteFilePath(name);
+ if (!extractFile(&zip, "", absFilePath)) {
+ removeFile(extracted);
+ return QStringList();
+ }
+ extracted.append(absFilePath);
+ } while (zip.goToNextFile());
+
+ // Chiudo il file zip
+ zip.close();
+ if(zip.getZipError()!=0) {
+ removeFile(extracted);
+ return QStringList();
+ }
+
+ return extracted;
+}
+
+/**OK
+ * Restituisce la lista dei file resenti nel file compresso fileCompressed.
+ * Se la funzione fallisce, restituisce un elenco vuoto.
+ *
+ * La funzione fallisce se:
+ * * non si riesce ad aprire l'oggetto zip;
+ * * la richiesta di informazioni di un file fallisce;
+ * * non si riesce a chiudere l'oggetto zip;
+ */
+QStringList JlCompress::getFileList(QString fileCompressed) {
+ // Apro lo zip
+ QuaZip* zip = new QuaZip(QFileInfo(fileCompressed).absoluteFilePath());
+ if(!zip->open(QuaZip::mdUnzip)) {
+ delete zip;
+ return QStringList();
+ }
+
+ // Estraggo i nomi dei file
+ QStringList lst;
+ QuaZipFileInfo info;
+ for(bool more=zip->goToFirstFile(); more; more=zip->goToNextFile()) {
+ if(!zip->getCurrentFileInfo(&info)) {
+ delete zip;
+ return QStringList();
+ }
+ lst << info.name;
+ //info.name.toLocal8Bit().constData()
+ }
+
+ // Chiudo il file zip
+ zip->close();
+ if(zip->getZipError()!=0) {
+ delete zip;
+ return QStringList();
+ }
+ delete zip;
+
+ return lst;
+}
+
diff --git a/depends/quazip/JlCompress.h b/depends/quazip/JlCompress.h
new file mode 100644
index 00000000..3ee8c25a
--- /dev/null
+++ b/depends/quazip/JlCompress.h
@@ -0,0 +1,125 @@
+#ifndef JLCOMPRESSFOLDER_H_
+#define JLCOMPRESSFOLDER_H_
+
+#include "quazip.h"
+#include "quazipfile.h"
+#include "quazipfileinfo.h"
+#include <QString>
+#include <QDir>
+#include <QFileInfo>
+#include <QFile>
+
+/// Utility class for typical operations.
+/**
+ This class contains a number of useful static functions to perform
+ simple operations, such as mass ZIP packing or extraction.
+ */
+class QUAZIP_EXPORT JlCompress {
+private:
+ /// Remove some files.
+ /**
+ \param listFile The list of files to remove.
+ \return true if success, false otherwise.
+ */
+ static bool removeFile(QStringList listFile);
+public:
+ /// Compress a single file.
+ /**
+ \param zip Opened zip to compress the file to.
+ \param fileName The full path to the source file.
+ \param fileDest The full name of the file inside the archive.
+ \return true if success, false otherwise.
+ */
+ static bool compressFile(QuaZip* zip, QString fileName, QString fileDest);
+ /// 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.
+ */
+ static bool compressSubDir( QuaZip* parentZip, QString dir, QString parentDir, bool recursive, QSet< QString >& added );
+ /// Extract a single file.
+ /**
+ \param zip The opened zip archive to extract from.
+ \param fileName The full name of the file to extract.
+ \param fileDest The full path to the destination file.
+ \return true if success, false otherwise.
+ */
+ static bool extractFile(QuaZip* zip, QString fileName, QString fileDest);
+
+ /// copy data from inFile to outFile
+ static bool copyData(QIODevice &inFile, QIODevice &outFile);
+ /// Compress a single file.
+ /**
+ \param fileCompressed The name of the archive.
+ \param file The file to compress.
+ \return true if success, false otherwise.
+ */
+ static bool compressFile(QString fileCompressed, QString file);
+ /// Compress a list of files.
+ /**
+ \param fileCompressed The name of the archive.
+ \param files The file list to compress.
+ \return true if success, false otherwise.
+ */
+ static bool compressFiles(QString fileCompressed, QStringList files);
+ /// 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.
+ */
+ static bool compressDir(QString fileCompressed, QString dir = QString(), bool recursive = true);
+
+public:
+ /// Extract a single file.
+ /**
+ \param fileCompressed The name of the archive.
+ \param fileName The file to extract.
+ \param fileDest The destination file, assumed to be identical to
+ \a file if left empty.
+ \return The list of the full paths of the files extracted, empty on failure.
+ */
+ static QString extractFile(QString fileCompressed, QString fileName, QString fileDest = QString());
+ /// Extract a list of files.
+ /**
+ \param fileCompressed The name of the archive.
+ \param files The file list to extract.
+ \param dir The directory to put the files to, the current
+ directory if left empty.
+ \return The list of the full paths of the files extracted, empty on failure.
+ */
+ static QStringList extractFiles(QString fileCompressed, QStringList files, QString dir = QString());
+ /// 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.
+ */
+ static QStringList extractDir(QString fileCompressed, QString dir = QString());
+ /// Extract a whole archive, with a list of exceptions (prefixes to ignore).
+ /**
+ \param fileCompressed The name of the archive.
+ \param dir The directory to extract to, the current directory if
+ left empty.
+ \param exceptions The list of exception prefixes
+ \return The list of the full paths of the files extracted, empty on failure.
+ */
+ static QStringList extractWithExceptions(QString fileCompressed, QString dir, QStringList exceptions);
+ /// Get the file list.
+ /**
+ \return The list of the files in the archive, or, more precisely, the
+ list of the entries, including both files and directories if they
+ are present separately.
+ */
+ static QStringList getFileList(QString fileCompressed);
+};
+
+#endif /* JLCOMPRESSFOLDER_H_ */
diff --git a/depends/quazip/crypt.h b/depends/quazip/crypt.h
new file mode 100644
index 00000000..1d6da628
--- /dev/null
+++ b/depends/quazip/crypt.h
@@ -0,0 +1,135 @@
+/* crypt.h -- base code for crypt/uncrypt ZIPfile
+
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This code is a modified version of crypting code in Infozip distribution
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+
+ If you don't need crypting in your application, just define symbols
+ NOCRYPT and NOUNCRYPT.
+
+ This code support the "Traditional PKWARE Encryption".
+
+ The new AES encryption added on Zip format by Winzip (see the page
+ http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
+ Encryption is not supported.
+*/
+
+#include "quazip_global.h"
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+/***********************************************************************
+ * Return the next byte in the pseudo-random sequence
+ */
+static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab UNUSED)
+{
+ //(void) pcrc_32_tab; /* avoid "unused parameter" warning */
+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
+ * unpredictable manner on 16-bit systems; not a problem
+ * with any known compiler so far, though */
+
+ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+/***********************************************************************
+ * Update the encryption keys with the next byte of plain text
+ */
+static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
+{
+ (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
+ (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+ (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+ {
+ register int keyshift = (int)((*(pkeys+1)) >> 24);
+ (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
+ }
+ return c;
+}
+
+
+/***********************************************************************
+ * Initialize the encryption keys and the random header according to
+ * the given password.
+ */
+static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
+{
+ *(pkeys+0) = 305419896L;
+ *(pkeys+1) = 591751049L;
+ *(pkeys+2) = 878082192L;
+ while (*passwd != '\0') {
+ update_keys(pkeys,pcrc_32_tab,(int)*passwd);
+ passwd++;
+ }
+}
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+
+#define RAND_HEAD_LEN 12
+ /* "last resort" source for second part of crypt seed pattern */
+# ifndef ZCR_SEED2
+# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
+# endif
+
+static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting)
+ const char *passwd; /* password string */
+ unsigned char *buf; /* where to write header */
+ int bufSize;
+ unsigned long* pkeys;
+ const unsigned long* pcrc_32_tab;
+ unsigned long crcForCrypting;
+{
+ int n; /* index in random header */
+ int t; /* temporary */
+ int c; /* random byte */
+ unsigned char header[RAND_HEAD_LEN-2]; /* random header */
+ static unsigned calls = 0; /* ensure different random header each time */
+
+ if (bufSize<RAND_HEAD_LEN)
+ return 0;
+
+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
+ * output of rand() to get less predictability, since rand() is
+ * often poorly implemented.
+ */
+ if (++calls == 1)
+ {
+ srand((unsigned)(time(NULL) ^ ZCR_SEED2));
+ }
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ c = (rand() >> 7) & 0xff;
+ header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
+ }
+ /* Encrypt random header (last two bytes is high word of crc) */
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
+ }
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
+ return n;
+}
+
+#endif
diff --git a/depends/quazip/ioapi.h b/depends/quazip/ioapi.h
new file mode 100644
index 00000000..f4c21809
--- /dev/null
+++ b/depends/quazip/ioapi.h
@@ -0,0 +1,77 @@
+/* ioapi.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Modified by Sergey A. Tachenov to integrate with Qt.
+*/
+
+#ifndef _ZLIBIOAPI_H
+#define _ZLIBIOAPI_H
+
+
+#define ZLIB_FILEFUNC_SEEK_CUR (1)
+#define ZLIB_FILEFUNC_SEEK_END (2)
+#define ZLIB_FILEFUNC_SEEK_SET (0)
+
+#define ZLIB_FILEFUNC_MODE_READ (1)
+#define ZLIB_FILEFUNC_MODE_WRITE (2)
+#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
+
+#define ZLIB_FILEFUNC_MODE_EXISTING (4)
+#define ZLIB_FILEFUNC_MODE_CREATE (8)
+
+
+#ifndef ZCALLBACK
+
+#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+#define ZCALLBACK CALLBACK
+#else
+#define ZCALLBACK
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, voidpf file, int mode));
+typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
+typedef uLong (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
+typedef int (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
+typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
+typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
+
+typedef struct zlib_filefunc_def_s
+{
+ open_file_func zopen_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell_file_func ztell_file;
+ seek_file_func zseek_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+ voidpf opaque;
+} zlib_filefunc_def;
+
+
+
+void fill_qiodevice_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
+#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
+#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
+#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
+#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
+#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/depends/quazip/qioapi.cpp b/depends/quazip/qioapi.cpp
new file mode 100644
index 00000000..f254c34d
--- /dev/null
+++ b/depends/quazip/qioapi.cpp
@@ -0,0 +1,146 @@
+/* ioapi.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Modified by Sergey A. Tachenov to integrate with Qt.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+#include "quazip_global.h"
+#include <QIODevice>
+
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+voidpf ZCALLBACK qiodevice_open_file_func (
+ voidpf opaque UNUSED,
+ voidpf file,
+ int mode)
+{
+ QIODevice *iodevice = reinterpret_cast<QIODevice*>(file);
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ iodevice->open(QIODevice::ReadOnly);
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ iodevice->open(QIODevice::ReadWrite);
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ iodevice->open(QIODevice::WriteOnly);
+
+ if (iodevice->isOpen()) {
+ if (iodevice->isSequential()) {
+ iodevice->close();
+ return NULL;
+ } else {
+ return iodevice;
+ }
+ } else
+ return NULL;
+}
+
+
+uLong ZCALLBACK qiodevice_read_file_func (
+ voidpf opaque UNUSED,
+ voidpf stream,
+ void* buf,
+ uLong size)
+{
+ uLong ret;
+ ret = (uLong)((QIODevice*)stream)->read((char*)buf,size);
+ return ret;
+}
+
+
+uLong ZCALLBACK qiodevice_write_file_func (
+ voidpf opaque UNUSED,
+ voidpf stream,
+ const void* buf,
+ uLong size)
+{
+ uLong ret;
+ ret = (uLong)((QIODevice*)stream)->write((char*)buf,size);
+ return ret;
+}
+
+uLong ZCALLBACK qiodevice_tell_file_func (
+ voidpf opaque UNUSED,
+ voidpf stream)
+{
+ uLong ret;
+ ret = ((QIODevice*)stream)->pos();
+ return ret;
+}
+
+int ZCALLBACK qiodevice_seek_file_func (
+ voidpf opaque UNUSED,
+ voidpf stream,
+ uLong offset,
+ int origin)
+{
+ uLong qiodevice_seek_result=0;
+ int ret;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ qiodevice_seek_result = ((QIODevice*)stream)->pos() + offset;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ qiodevice_seek_result = ((QIODevice*)stream)->size() - offset;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ qiodevice_seek_result = offset;
+ break;
+ default: return -1;
+ }
+ ret = !((QIODevice*)stream)->seek(qiodevice_seek_result);
+ return ret;
+}
+
+int ZCALLBACK qiodevice_close_file_func (
+ voidpf opaque UNUSED,
+ voidpf stream)
+{
+ ((QIODevice*)stream)->close();
+ return 0;
+}
+
+int ZCALLBACK qiodevice_error_file_func (
+ voidpf opaque UNUSED,
+ voidpf stream)
+{
+ // can't check for error due to the QIODevice API limitation
+ return 0;
+}
+
+void fill_qiodevice_filefunc (
+ zlib_filefunc_def* pzlib_filefunc_def)
+{
+ pzlib_filefunc_def->zopen_file = qiodevice_open_file_func;
+ pzlib_filefunc_def->zread_file = qiodevice_read_file_func;
+ pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func;
+ pzlib_filefunc_def->ztell_file = qiodevice_tell_file_func;
+ pzlib_filefunc_def->zseek_file = qiodevice_seek_file_func;
+ pzlib_filefunc_def->zclose_file = qiodevice_close_file_func;
+ pzlib_filefunc_def->zerror_file = qiodevice_error_file_func;
+ pzlib_filefunc_def->opaque = NULL;
+}
diff --git a/depends/quazip/quaadler32.cpp b/depends/quazip/quaadler32.cpp
new file mode 100644
index 00000000..097899f6
--- /dev/null
+++ b/depends/quazip/quaadler32.cpp
@@ -0,0 +1,28 @@
+#include "quaadler32.h"
+
+#include "zlib.h"
+
+QuaAdler32::QuaAdler32()
+{
+ reset();
+}
+
+quint32 QuaAdler32::calculate(const QByteArray &data)
+{
+ return adler32( adler32(0L, Z_NULL, 0), (const Bytef*)data.data(), data.size() );
+}
+
+void QuaAdler32::reset()
+{
+ checksum = adler32(0L, Z_NULL, 0);
+}
+
+void QuaAdler32::update(const QByteArray &buf)
+{
+ checksum = adler32( checksum, (const Bytef*)buf.data(), buf.size() );
+}
+
+quint32 QuaAdler32::value()
+{
+ return checksum;
+}
diff --git a/depends/quazip/quaadler32.h b/depends/quazip/quaadler32.h
new file mode 100644
index 00000000..c5ac0532
--- /dev/null
+++ b/depends/quazip/quaadler32.h
@@ -0,0 +1,29 @@
+#ifndef QUAADLER32_H
+#define QUAADLER32_H
+
+#include <QtCore/QByteArray>
+
+#include "quachecksum32.h"
+
+/// Adler32 checksum
+/** \class QuaAdler32 quaadler32.h <quazip/quaadler32.h>
+ * This class wrappers the adler32 function with the QuaChecksum32 interface.
+ * See QuaChecksum32 for more info.
+ */
+class QUAZIP_EXPORT QuaAdler32 : public QuaChecksum32
+{
+
+public:
+ QuaAdler32();
+
+ quint32 calculate(const QByteArray &data);
+
+ void reset();
+ void update(const QByteArray &buf);
+ quint32 value();
+
+private:
+ quint32 checksum;
+};
+
+#endif //QUAADLER32_H
diff --git a/depends/quazip/quachecksum32.h b/depends/quazip/quachecksum32.h
new file mode 100644
index 00000000..773ec2a4
--- /dev/null
+++ b/depends/quazip/quachecksum32.h
@@ -0,0 +1,54 @@
+#ifndef QUACHECKSUM32_H
+#define QUACHECKSUM32_H
+
+#include <QtCore/QByteArray>
+#include "quazip_global.h"
+
+/// Checksum interface.
+/** \class QuaChecksum32 quachecksum32.h <quazip/quachecksum32.h>
+ * This is an interface for 32 bit checksums.
+ * Classes implementing this interface can calcunate a certin
+ * checksum in a single step:
+ * \code
+ * QChecksum32 *crc32 = new QuaCrc32();
+ * rasoult = crc32->calculate(data);
+ * \endcode
+ * or by streaming the data:
+ * \code
+ * QChecksum32 *crc32 = new QuaCrc32();
+ * while(!fileA.atEnd())
+ * crc32->update(fileA.read(bufSize));
+ * resoultA = crc32->value();
+ * crc32->reset();
+ * while(!fileB.atEnd())
+ * crc32->update(fileB.read(bufSize));
+ * resoultB = crc32->value();
+ * \endcode
+ */
+class QUAZIP_EXPORT QuaChecksum32
+{
+
+public:
+ ///Calculates the checksum for data.
+ /** \a data source data
+ * \return data checksum
+ *
+ * This function has no efect on the value returned by value().
+ */
+ virtual quint32 calculate(const QByteArray &data) = 0;
+
+ ///Resets the calculation on a checksun for a stream.
+ virtual void reset() = 0;
+
+ ///Updates the calculated checksum for the stream
+ /** \a buf next portion of data from the stream
+ */
+ virtual void update(const QByteArray &buf) = 0;
+
+ ///Value of the checksum calculated for the stream passed throw update().
+ /** \return checksum
+ */
+ virtual quint32 value() = 0;
+};
+
+#endif //QUACHECKSUM32_H
diff --git a/depends/quazip/quacrc32.cpp b/depends/quazip/quacrc32.cpp
new file mode 100644
index 00000000..9381f24c
--- /dev/null
+++ b/depends/quazip/quacrc32.cpp
@@ -0,0 +1,28 @@
+#include "quacrc32.h"
+
+#include "zlib.h"
+
+QuaCrc32::QuaCrc32()
+{
+ reset();
+}
+
+quint32 QuaCrc32::calculate(const QByteArray &data)
+{
+ return crc32( crc32(0L, Z_NULL, 0), (const Bytef*)data.data(), data.size() );
+}
+
+void QuaCrc32::reset()
+{
+ checksum = crc32(0L, Z_NULL, 0);
+}
+
+void QuaCrc32::update(const QByteArray &buf)
+{
+ checksum = crc32( checksum, (const Bytef*)buf.data(), buf.size() );
+}
+
+quint32 QuaCrc32::value()
+{
+ return checksum;
+}
diff --git a/depends/quazip/quacrc32.h b/depends/quazip/quacrc32.h
new file mode 100644
index 00000000..4c86d566
--- /dev/null
+++ b/depends/quazip/quacrc32.h
@@ -0,0 +1,26 @@
+#ifndef QUACRC32_H
+#define QUACRC32_H
+
+#include "quachecksum32.h"
+
+///CRC32 checksum
+/** \class QuaCrc32 quacrc32.h <quazip/quacrc32.h>
+* This class wrappers the crc32 function with the QuaChecksum32 interface.
+* See QuaChecksum32 for more info.
+*/
+class QUAZIP_EXPORT QuaCrc32 : public QuaChecksum32 {
+
+public:
+ QuaCrc32();
+
+ quint32 calculate(const QByteArray &data);
+
+ void reset();
+ void update(const QByteArray &buf);
+ quint32 value();
+
+private:
+ quint32 checksum;
+};
+
+#endif //QUACRC32_H
diff --git a/depends/quazip/quagzipfile.cpp b/depends/quazip/quagzipfile.cpp
new file mode 100644
index 00000000..c1c70aad
--- /dev/null
+++ b/depends/quazip/quagzipfile.cpp
@@ -0,0 +1,141 @@
+#include <QFile>
+
+#include "quagzipfile.h"
+
+class QuaGzipFilePrivate {
+ friend class QuaGzipFile;
+ QString fileName;
+ gzFile gzd;
+ inline QuaGzipFilePrivate(): gzd(NULL) {}
+ inline QuaGzipFilePrivate(const QString &fileName):
+ fileName(fileName), gzd(NULL) {}
+ template<typename FileId> bool open(FileId id,
+ QIODevice::OpenMode mode, QString &error);
+ gzFile open(int fd, const char *modeString);
+ gzFile open(const QString &name, const char *modeString);
+};
+
+gzFile QuaGzipFilePrivate::open(const QString &name, const char *modeString)
+{
+ return gzopen(QFile::encodeName(name).constData(), modeString);
+}
+
+gzFile QuaGzipFilePrivate::open(int fd, const char *modeString)
+{
+ return gzdopen(fd, modeString);
+}
+
+template<typename FileId>
+bool QuaGzipFilePrivate::open(FileId id, QIODevice::OpenMode mode,
+ QString &error)
+{
+ char modeString[2];
+ modeString[0] = modeString[1] = '\0';
+ if ((mode & QIODevice::ReadOnly) != 0
+ && (mode & QIODevice::WriteOnly) != 0) {
+ error = QuaGzipFile::trUtf8("Opening gzip for both reading"
+ " and writing is not supported");
+ return false;
+ } else if ((mode & QIODevice::ReadOnly) != 0) {
+ modeString[0] = 'r';
+ } else if ((mode & QIODevice::WriteOnly) != 0) {
+ modeString[0] = 'w';
+ } else {
+ error = QuaGzipFile::trUtf8("You can open a gzip either for reading"
+ " or for writing. Which is it?");
+ return false;
+ }
+ gzd = open(id, modeString);
+ if (gzd == NULL) {
+ error = QuaGzipFile::trUtf8("Could not gzopen() file");
+ return false;
+ }
+ return true;
+}
+
+QuaGzipFile::QuaGzipFile():
+d(new QuaGzipFilePrivate())
+{
+}
+
+QuaGzipFile::QuaGzipFile(QObject *parent):
+QIODevice(parent),
+d(new QuaGzipFilePrivate())
+{
+}
+
+QuaGzipFile::QuaGzipFile(const QString &fileName, QObject *parent):
+ QIODevice(parent),
+d(new QuaGzipFilePrivate(fileName))
+{
+}
+
+QuaGzipFile::~QuaGzipFile()
+{
+ if (isOpen()) {
+ close();
+ }
+ delete d;
+}
+
+void QuaGzipFile::setFileName(const QString& fileName)
+{
+ d->fileName = fileName;
+}
+
+QString QuaGzipFile::getFileName() const
+{
+ return d->fileName;
+}
+
+bool QuaGzipFile::isSequential() const
+{
+ return true;
+}
+
+bool QuaGzipFile::open(QIODevice::OpenMode mode)
+{
+ QString error;
+ if (!d->open(d->fileName, mode, error)) {
+ setErrorString(error);
+ return false;
+ }
+ return QIODevice::open(mode);
+}
+
+bool QuaGzipFile::open(int fd, QIODevice::OpenMode mode)
+{
+ QString error;
+ if (!d->open(fd, mode, error)) {
+ setErrorString(error);
+ return false;
+ }
+ return QIODevice::open(mode);
+}
+
+bool QuaGzipFile::flush()
+{
+ return gzflush(d->gzd, Z_SYNC_FLUSH) == Z_OK;
+}
+
+void QuaGzipFile::close()
+{
+ QIODevice::close();
+ gzclose(d->gzd);
+}
+
+qint64 QuaGzipFile::readData(char *data, qint64 maxSize)
+{
+ return gzread(d->gzd, (voidp)data, (unsigned)maxSize);
+}
+
+qint64 QuaGzipFile::writeData(const char *data, qint64 maxSize)
+{
+ if (maxSize == 0)
+ return 0;
+ int written = gzwrite(d->gzd, (voidp)data, (unsigned)maxSize);
+ if (written == 0)
+ return -1;
+ else
+ return written;
+}
diff --git a/depends/quazip/quagzipfile.h b/depends/quazip/quagzipfile.h
new file mode 100644
index 00000000..211ceadb
--- /dev/null
+++ b/depends/quazip/quagzipfile.h
@@ -0,0 +1,35 @@
+#ifndef QUAZIP_QUAGZIPFILE_H
+#define QUAZIP_QUAGZIPFILE_H
+
+#include <QIODevice>
+#include "quazip_global.h"
+
+#include <zlib.h>
+
+class QuaGzipFilePrivate;
+
+class QUAZIP_EXPORT QuaGzipFile: public QIODevice {
+ Q_OBJECT
+public:
+ QuaGzipFile();
+ QuaGzipFile(QObject *parent);
+ QuaGzipFile(const QString &fileName, QObject *parent = NULL);
+ virtual ~QuaGzipFile();
+ void setFileName(const QString& fileName);
+ QString getFileName() const;
+ virtual bool isSequential() const;
+ virtual bool open(QIODevice::OpenMode mode);
+ virtual bool open(int fd, QIODevice::OpenMode mode);
+ virtual bool flush();
+ virtual void close();
+protected:
+ virtual qint64 readData(char *data, qint64 maxSize);
+ virtual qint64 writeData(const char *data, qint64 maxSize);
+private:
+ // not implemented by design to disable copy
+ QuaGzipFile(const QuaGzipFile &that);
+ QuaGzipFile& operator=(const QuaGzipFile &that);
+ QuaGzipFilePrivate *d;
+};
+
+#endif // QUAZIP_QUAGZIPFILE_H
diff --git a/depends/quazip/quaziodevice.cpp b/depends/quazip/quaziodevice.cpp
new file mode 100644
index 00000000..959ca0e8
--- /dev/null
+++ b/depends/quazip/quaziodevice.cpp
@@ -0,0 +1,283 @@
+#include "quaziodevice.h"
+
+#define QUAZIO_INBUFSIZE 4096
+#define QUAZIO_OUTBUFSIZE 4096
+
+class QuaZIODevicePrivate {
+ friend class QuaZIODevice;
+ QuaZIODevicePrivate(QIODevice *io);
+ ~QuaZIODevicePrivate();
+ QIODevice *io;
+ z_stream zins;
+ z_stream zouts;
+ char *inBuf;
+ int inBufPos;
+ int inBufSize;
+ char *outBuf;
+ int outBufPos;
+ int outBufSize;
+ bool zBufError;
+ int doFlush(QString &error);
+};
+
+QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io):
+ io(io),
+ inBuf(NULL),
+ inBufPos(0),
+ inBufSize(0),
+ outBuf(NULL),
+ outBufPos(0),
+ outBufSize(0),
+ zBufError(false)
+{
+ zins.zalloc = (alloc_func) NULL;
+ zins.zfree = (free_func) NULL;
+ zins.opaque = NULL;
+ zouts.zalloc = (alloc_func) NULL;
+ zouts.zfree = (free_func) NULL;
+ zouts.opaque = NULL;
+ inBuf = new char[QUAZIO_INBUFSIZE];
+ outBuf = new char[QUAZIO_OUTBUFSIZE];
+#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT
+ debug.setFileName("debug.out");
+ debug.open(QIODevice::WriteOnly);
+#endif
+#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT
+ indebug.setFileName("debug.in");
+ indebug.open(QIODevice::WriteOnly);
+#endif
+}
+
+QuaZIODevicePrivate::~QuaZIODevicePrivate()
+{
+#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT
+ debug.close();
+#endif
+#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT
+ indebug.close();
+#endif
+ if (inBuf != NULL)
+ delete[] inBuf;
+ if (outBuf != NULL)
+ delete[] outBuf;
+}
+
+int QuaZIODevicePrivate::doFlush(QString &error)
+{
+ int flushed = 0;
+ while (outBufPos < outBufSize) {
+ int more = io->write(outBuf + outBufPos, outBufSize - outBufPos);
+ if (more == -1) {
+ error = io->errorString();
+ return -1;
+ }
+ if (more == 0)
+ break;
+ outBufPos += more;
+ flushed += more;
+ }
+ if (outBufPos == outBufSize) {
+ outBufPos = outBufSize = 0;
+ }
+ return flushed;
+}
+
+// #define QUAZIP_ZIODEVICE_DEBUG_OUTPUT
+// #define QUAZIP_ZIODEVICE_DEBUG_INPUT
+#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT
+#include <QFile>
+static QFile debug;
+#endif
+#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT
+#include <QFile>
+static QFile indebug;
+#endif
+
+QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent):
+ QIODevice(parent),
+ d(new QuaZIODevicePrivate(io))
+{
+ connect(io, SIGNAL(readyRead()), SIGNAL(readyRead()));
+}
+
+QuaZIODevice::~QuaZIODevice()
+{
+ if (isOpen())
+ close();
+ delete d;
+}
+
+QIODevice *QuaZIODevice::getIoDevice() const
+{
+ return d->io;
+}
+
+bool QuaZIODevice::open(QIODevice::OpenMode mode)
+{
+ if ((mode & QIODevice::ReadOnly) != 0) {
+ if (inflateInit(&d->zins) != Z_OK) {
+ setErrorString(d->zins.msg);
+ return false;
+ }
+ }
+ if ((mode & QIODevice::WriteOnly) != 0) {
+ if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ setErrorString(d->zouts.msg);
+ return false;
+ }
+ }
+ return QIODevice::open(mode);
+}
+
+void QuaZIODevice::close()
+{
+ if ((openMode() & QIODevice::ReadOnly) != 0) {
+ if (inflateEnd(&d->zins) != Z_OK) {
+ setErrorString(d->zins.msg);
+ }
+ }
+ if ((openMode() & QIODevice::WriteOnly) != 0) {
+ flush();
+ if (deflateEnd(&d->zouts) != Z_OK) {
+ setErrorString(d->zouts.msg);
+ }
+ }
+ QIODevice::close();
+}
+
+qint64 QuaZIODevice::readData(char *data, qint64 maxSize)
+{
+ int read = 0;
+ while (read < maxSize) {
+ if (d->inBufPos == d->inBufSize) {
+ d->inBufPos = 0;
+ d->inBufSize = d->io->read(d->inBuf, QUAZIO_INBUFSIZE);
+ if (d->inBufSize == -1) {
+ d->inBufSize = 0;
+ setErrorString(d->io->errorString());
+ return -1;
+ }
+ if (d->inBufSize == 0)
+ break;
+ }
+ while (read < maxSize && d->inBufPos < d->inBufSize) {
+ d->zins.next_in = (Bytef *) (d->inBuf + d->inBufPos);
+ d->zins.avail_in = d->inBufSize - d->inBufPos;
+ d->zins.next_out = (Bytef *) (data + read);
+ d->zins.avail_out = (uInt) (maxSize - read); // hope it's less than 2GB
+ int more = 0;
+ switch (inflate(&d->zins, Z_SYNC_FLUSH)) {
+ case Z_OK:
+ read = (char *) d->zins.next_out - data;
+ d->inBufPos = (char *) d->zins.next_in - d->inBuf;
+ break;
+ case Z_STREAM_END:
+ read = (char *) d->zins.next_out - data;
+ d->inBufPos = (char *) d->zins.next_in - d->inBuf;
+ return read;
+ case Z_BUF_ERROR: // this should never happen, but just in case
+ if (!d->zBufError) {
+ qWarning("Z_BUF_ERROR detected with %d/%d in/out, weird",
+ d->zins.avail_in, d->zins.avail_out);
+ d->zBufError = true;
+ }
+ memmove(d->inBuf, d->inBuf + d->inBufPos, d->inBufSize - d->inBufPos);
+ d->inBufSize -= d->inBufPos;
+ d->inBufPos = 0;
+ more = d->io->read(d->inBuf + d->inBufSize, QUAZIO_INBUFSIZE - d->inBufSize);
+ if (more == -1) {
+ setErrorString(d->io->errorString());
+ return -1;
+ }
+ if (more == 0)
+ return read;
+ d->inBufSize += more;
+ break;
+ default:
+ setErrorString(QString::fromLocal8Bit(d->zins.msg));
+ return -1;
+ }
+ }
+ }
+#ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT
+ indebug.write(data, read);
+#endif
+ return read;
+}
+
+qint64 QuaZIODevice::writeData(const char *data, qint64 maxSize)
+{
+ int written = 0;
+ QString error;
+ if (d->doFlush(error) == -1) {
+ setErrorString(error);
+ return -1;
+ }
+ while (written < maxSize) {
+ // there is some data waiting in the output buffer
+ if (d->outBufPos < d->outBufSize)
+ return written;
+ d->zouts.next_in = (Bytef *) (data + written);
+ d->zouts.avail_in = (uInt) (maxSize - written); // hope it's less than 2GB
+ d->zouts.next_out = (Bytef *) d->outBuf;
+ d->zouts.avail_out = QUAZIO_OUTBUFSIZE;
+ switch (deflate(&d->zouts, Z_NO_FLUSH)) {
+ case Z_OK:
+ written = (char *) d->zouts.next_in - data;
+ d->outBufSize = (char *) d->zouts.next_out - d->outBuf;
+ break;
+ default:
+ setErrorString(QString::fromLocal8Bit(d->zouts.msg));
+ return -1;
+ }
+ if (d->doFlush(error) == -1) {
+ setErrorString(error);
+ return -1;
+ }
+ }
+#ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT
+ debug.write(data, written);
+#endif
+ return written;
+}
+
+bool QuaZIODevice::flush()
+{
+ QString error;
+ if (d->doFlush(error) < 0) {
+ setErrorString(error);
+ return false;
+ }
+ // can't flush buffer, some data is still waiting
+ if (d->outBufPos < d->outBufSize)
+ return true;
+ Bytef c = 0;
+ d->zouts.next_in = &c; // fake input buffer
+ d->zouts.avail_in = 0; // of zero size
+ do {
+ d->zouts.next_out = (Bytef *) d->outBuf;
+ d->zouts.avail_out = QUAZIO_OUTBUFSIZE;
+ switch (deflate(&d->zouts, Z_SYNC_FLUSH)) {
+ case Z_OK:
+ d->outBufSize = (char *) d->zouts.next_out - d->outBuf;
+ if (d->doFlush(error) < 0) {
+ setErrorString(error);
+ return false;
+ }
+ if (d->outBufPos < d->outBufSize)
+ return true;
+ break;
+ case Z_BUF_ERROR: // nothing to write?
+ return true;
+ default:
+ setErrorString(QString::fromLocal8Bit(d->zouts.msg));
+ return false;
+ }
+ } while (d->zouts.avail_out == 0);
+ return true;
+}
+
+bool QuaZIODevice::isSequential() const
+{
+ return true;
+}
diff --git a/depends/quazip/quaziodevice.h b/depends/quazip/quaziodevice.h
new file mode 100644
index 00000000..b061cd16
--- /dev/null
+++ b/depends/quazip/quaziodevice.h
@@ -0,0 +1,27 @@
+#ifndef QUAZIP_QUAZIODEVICE_H
+#define QUAZIP_QUAZIODEVICE_H
+
+#include <QIODevice>
+#include "quazip_global.h"
+
+#include <zlib.h>
+
+class QuaZIODevicePrivate;
+
+class QUAZIP_EXPORT QuaZIODevice: public QIODevice {
+ Q_OBJECT
+public:
+ QuaZIODevice(QIODevice *io, QObject *parent = NULL);
+ ~QuaZIODevice();
+ virtual bool flush();
+ virtual bool open(QIODevice::OpenMode);
+ virtual void close();
+ QIODevice *getIoDevice() const;
+ virtual bool isSequential() const;
+protected:
+ virtual qint64 readData(char *data, qint64 maxSize);
+ virtual qint64 writeData(const char *data, qint64 maxSize);
+private:
+ QuaZIODevicePrivate *d;
+};
+#endif // QUAZIP_QUAZIODEVICE_H
diff --git a/depends/quazip/quazip.cpp b/depends/quazip/quazip.cpp
new file mode 100644
index 00000000..b6fa92f0
--- /dev/null
+++ b/depends/quazip/quazip.cpp
@@ -0,0 +1,554 @@
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ **/
+
+#include <QtCore/QFile>
+#include <QtCore/QFlags>
+
+#include "quazip.h"
+
+/// All the internal stuff for the QuaZip class.
+/**
+ \internal
+
+ This class keeps all the private stuff for the QuaZip class so it can
+ be changed without breaking binary compatibility, according to the
+ Pimpl idiom.
+ */
+class QuaZipPrivate {
+ friend class QuaZip;
+ private:
+ /// The pointer to the corresponding QuaZip instance.
+ QuaZip *q;
+ /// The codec for file names.
+ QTextCodec *fileNameCodec;
+ /// The codec for comments.
+ QTextCodec *commentCodec;
+ /// The archive file name.
+ QString zipName;
+ /// The device to access the archive.
+ QIODevice *ioDevice;
+ /// The global comment.
+ QString comment;
+ /// The open mode.
+ QuaZip::Mode mode;
+ union {
+ /// The internal handle for UNZIP modes.
+ unzFile unzFile_f;
+ /// The internal handle for ZIP modes.
+ zipFile zipFile_f;
+ };
+ /// Whether a current file is set.
+ bool hasCurrentFile_f;
+ /// The last error.
+ int zipError;
+ /// Whether \ref QuaZip::setDataDescriptorWritingEnabled() "the data descriptor writing mode" is enabled.
+ bool dataDescriptorWritingEnabled;
+ /// The constructor for the corresponding QuaZip constructor.
+ inline QuaZipPrivate(QuaZip *q):
+ q(q),
+ fileNameCodec(QTextCodec::codecForLocale()),
+ commentCodec(QTextCodec::codecForLocale()),
+ ioDevice(NULL),
+ mode(QuaZip::mdNotOpen),
+ hasCurrentFile_f(false),
+ zipError(UNZ_OK),
+ dataDescriptorWritingEnabled(true) {}
+ /// The constructor for the corresponding QuaZip constructor.
+ inline QuaZipPrivate(QuaZip *q, const QString &zipName):
+ q(q),
+ fileNameCodec(QTextCodec::codecForLocale()),
+ commentCodec(QTextCodec::codecForLocale()),
+ zipName(zipName),
+ ioDevice(NULL),
+ mode(QuaZip::mdNotOpen),
+ hasCurrentFile_f(false),
+ zipError(UNZ_OK),
+ dataDescriptorWritingEnabled(true) {}
+ /// The constructor for the corresponding QuaZip constructor.
+ inline QuaZipPrivate(QuaZip *q, QIODevice *ioDevice):
+ q(q),
+ fileNameCodec(QTextCodec::codecForLocale()),
+ commentCodec(QTextCodec::codecForLocale()),
+ ioDevice(ioDevice),
+ mode(QuaZip::mdNotOpen),
+ hasCurrentFile_f(false),
+ zipError(UNZ_OK),
+ dataDescriptorWritingEnabled(true) {}
+ /// Returns either a list of file names or a list of QuaZipFileInfo.
+ template<typename TFileInfo>
+ bool getFileInfoList(QList<TFileInfo> *result) const;
+};
+
+QuaZip::QuaZip():
+ p(new QuaZipPrivate(this))
+{
+}
+
+QuaZip::QuaZip(const QString& zipName):
+ p(new QuaZipPrivate(this, zipName))
+{
+}
+
+QuaZip::QuaZip(QIODevice *ioDevice):
+ p(new QuaZipPrivate(this, ioDevice))
+{
+}
+
+QuaZip::~QuaZip()
+{
+ if(isOpen())
+ close();
+ delete p;
+}
+
+bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi)
+{
+ p->zipError=UNZ_OK;
+ if(isOpen()) {
+ qWarning("QuaZip::open(): ZIP already opened");
+ return false;
+ }
+ QIODevice *ioDevice = p->ioDevice;
+ if (ioDevice == NULL) {
+ if (p->zipName.isEmpty()) {
+ qWarning("QuaZip::open(): set either ZIP file name or IO device first");
+ return false;
+ } else {
+ ioDevice = new QFile(p->zipName);
+ }
+ }
+ switch(mode) {
+ case mdUnzip:
+ p->unzFile_f=unzOpen2(ioDevice, ioApi);
+ if(p->unzFile_f!=NULL) {
+ p->mode=mode;
+ p->ioDevice = ioDevice;
+ return true;
+ } else {
+ p->zipError=UNZ_OPENERROR;
+ if (!p->zipName.isEmpty())
+ delete ioDevice;
+ return false;
+ }
+ case mdCreate:
+ case mdAppend:
+ case mdAdd:
+ p->zipFile_f=zipOpen2(ioDevice,
+ mode==mdCreate?APPEND_STATUS_CREATE:
+ mode==mdAppend?APPEND_STATUS_CREATEAFTER:
+ APPEND_STATUS_ADDINZIP,
+ NULL,
+ ioApi);
+ if(p->zipFile_f!=NULL) {
+ p->mode=mode;
+ p->ioDevice = ioDevice;
+ return true;
+ } else {
+ p->zipError=UNZ_OPENERROR;
+ if (!p->zipName.isEmpty())
+ delete ioDevice;
+ return false;
+ }
+ default:
+ qWarning("QuaZip::open(): unknown mode: %d", (int)mode);
+ if (!p->zipName.isEmpty())
+ delete ioDevice;
+ return false;
+ break;
+ }
+}
+
+void QuaZip::close()
+{
+ p->zipError=UNZ_OK;
+ switch(p->mode) {
+ case mdNotOpen:
+ qWarning("QuaZip::close(): ZIP is not open");
+ return;
+ case mdUnzip:
+ p->zipError=unzClose(p->unzFile_f);
+ break;
+ case mdCreate:
+ case mdAppend:
+ case mdAdd:
+ p->zipError=zipClose(p->zipFile_f,
+ p->comment.isNull() ? NULL
+ : p->commentCodec->fromUnicode(p->comment).constData());
+ break;
+ default:
+ qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode);
+ return;
+ }
+ // opened by name, need to delete the internal IO device
+ if (!p->zipName.isEmpty()) {
+ delete p->ioDevice;
+ p->ioDevice = NULL;
+ }
+ if(p->zipError==UNZ_OK)
+ p->mode=mdNotOpen;
+}
+
+void QuaZip::setZipName(const QString& zipName)
+{
+ if(isOpen()) {
+ qWarning("QuaZip::setZipName(): ZIP is already open!");
+ return;
+ }
+ p->zipName=zipName;
+ p->ioDevice = NULL;
+}
+
+void QuaZip::setIoDevice(QIODevice *ioDevice)
+{
+ if(isOpen()) {
+ qWarning("QuaZip::setIoDevice(): ZIP is already open!");
+ return;
+ }
+ p->ioDevice = ioDevice;
+ p->zipName = QString();
+}
+
+int QuaZip::getEntriesCount()const
+{
+ QuaZip *fakeThis=(QuaZip*)this; // non-const
+ fakeThis->p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode");
+ return -1;
+ }
+ unz_global_info globalInfo;
+ if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK)
+ return p->zipError;
+ return (int)globalInfo.number_entry;
+}
+
+QString QuaZip::getComment()const
+{
+ QuaZip *fakeThis=(QuaZip*)this; // non-const
+ fakeThis->p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode");
+ return QString();
+ }
+ unz_global_info globalInfo;
+ QByteArray comment;
+ if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK)
+ return QString();
+ comment.resize(globalInfo.size_comment);
+ if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0)
+ return QString();
+ fakeThis->p->zipError = UNZ_OK;
+ return p->commentCodec->toUnicode(comment);
+}
+
+bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs)
+{
+ p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode");
+ return false;
+ }
+ if(fileName.isEmpty()) {
+ p->hasCurrentFile_f=false;
+ return true;
+ }
+ // Unicode-aware reimplementation of the unzLocateFile function
+ if(p->unzFile_f==NULL) {
+ p->zipError=UNZ_PARAMERROR;
+ return false;
+ }
+ if(fileName.length()>MAX_FILE_NAME_LENGTH) {
+ p->zipError=UNZ_PARAMERROR;
+ return false;
+ }
+ bool sens = convertCaseSensitivity(cs) == Qt::CaseSensitive;
+ QString lower, current;
+ if(!sens) lower=fileName.toLower();
+ p->hasCurrentFile_f=false;
+ for(bool more=goToFirstFile(); more; more=goToNextFile()) {
+ current=getCurrentFileName();
+ if(current.isEmpty()) return false;
+ if(sens) {
+ if(current==fileName) break;
+ } else {
+ if(current.toLower()==lower) break;
+ }
+ }
+ return p->hasCurrentFile_f;
+}
+
+bool QuaZip::goToFirstFile()
+{
+ p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode");
+ return false;
+ }
+ p->zipError=unzGoToFirstFile(p->unzFile_f);
+ p->hasCurrentFile_f=p->zipError==UNZ_OK;
+ return p->hasCurrentFile_f;
+}
+
+bool QuaZip::goToNextFile()
+{
+ p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode");
+ return false;
+ }
+ p->zipError=unzGoToNextFile(p->unzFile_f);
+ p->hasCurrentFile_f=p->zipError==UNZ_OK;
+ if(p->zipError==UNZ_END_OF_LIST_OF_FILE)
+ p->zipError=UNZ_OK;
+ return p->hasCurrentFile_f;
+}
+
+bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const
+{
+ QuaZip *fakeThis=(QuaZip*)this; // non-const
+ fakeThis->p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode");
+ return false;
+ }
+ unz_file_info info_z;
+ QByteArray fileName;
+ QByteArray extra;
+ QByteArray comment;
+ if(info==NULL) return false;
+ if(!isOpen()||!hasCurrentFile()) return false;
+ if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK)
+ return false;
+ fileName.resize(info_z.size_filename);
+ extra.resize(info_z.size_file_extra);
+ comment.resize(info_z.size_file_comment);
+ if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL,
+ fileName.data(), fileName.size(),
+ extra.data(), extra.size(),
+ comment.data(), comment.size()))!=UNZ_OK)
+ return false;
+ info->versionCreated=info_z.version;
+ info->versionNeeded=info_z.version_needed;
+ info->flags=info_z.flag;
+ info->method=info_z.compression_method;
+ info->crc=info_z.crc;
+ info->compressedSize=info_z.compressed_size;
+ info->uncompressedSize=info_z.uncompressed_size;
+ info->diskNumberStart=info_z.disk_num_start;
+ info->internalAttr=info_z.internal_fa;
+ info->externalAttr=info_z.external_fa;
+ info->name=p->fileNameCodec->toUnicode(fileName);
+ info->comment=p->commentCodec->toUnicode(comment);
+ info->extra=extra;
+ info->dateTime=QDateTime(
+ QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday),
+ QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec));
+ return true;
+}
+
+QString QuaZip::getCurrentFileName()const
+{
+ QuaZip *fakeThis=(QuaZip*)this; // non-const
+ fakeThis->p->zipError=UNZ_OK;
+ if(p->mode!=mdUnzip) {
+ qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode");
+ return QString();
+ }
+ if(!isOpen()||!hasCurrentFile()) return QString();
+ QByteArray fileName(MAX_FILE_NAME_LENGTH, 0);
+ if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL, fileName.data(), fileName.size(),
+ NULL, 0, NULL, 0))!=UNZ_OK)
+ return QString();
+ return p->fileNameCodec->toUnicode(fileName.constData());
+}
+
+void QuaZip::setFileNameCodec(QTextCodec *fileNameCodec)
+{
+ p->fileNameCodec=fileNameCodec;
+}
+
+void QuaZip::setFileNameCodec(const char *fileNameCodecName)
+{
+ p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName);
+}
+
+QTextCodec *QuaZip::getFileNameCodec()const
+{
+ return p->fileNameCodec;
+}
+
+void QuaZip::setCommentCodec(QTextCodec *commentCodec)
+{
+ p->commentCodec=commentCodec;
+}
+
+void QuaZip::setCommentCodec(const char *commentCodecName)
+{
+ p->commentCodec=QTextCodec::codecForName(commentCodecName);
+}
+
+QTextCodec *QuaZip::getCommentCodec()const
+{
+ return p->commentCodec;
+}
+
+QString QuaZip::getZipName() const
+{
+ return p->zipName;
+}
+
+QIODevice *QuaZip::getIoDevice() const
+{
+ if (!p->zipName.isEmpty()) // opened by name, using an internal QIODevice
+ return NULL;
+ return p->ioDevice;
+}
+
+QuaZip::Mode QuaZip::getMode()const
+{
+ return p->mode;
+}
+
+bool QuaZip::isOpen()const
+{
+ return p->mode!=mdNotOpen;
+}
+
+int QuaZip::getZipError() const
+{
+ return p->zipError;
+}
+
+void QuaZip::setComment(const QString& comment)
+{
+ p->comment=comment;
+}
+
+bool QuaZip::hasCurrentFile()const
+{
+ return p->hasCurrentFile_f;
+}
+
+unzFile QuaZip::getUnzFile()
+{
+ return p->unzFile_f;
+}
+
+zipFile QuaZip::getZipFile()
+{
+ return p->zipFile_f;
+}
+
+void QuaZip::setDataDescriptorWritingEnabled(bool enabled)
+{
+ p->dataDescriptorWritingEnabled = enabled;
+}
+
+bool QuaZip::isDataDescriptorWritingEnabled() const
+{
+ return p->dataDescriptorWritingEnabled;
+}
+
+template<typename TFileInfo>
+TFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok);
+
+template<>
+QuaZipFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok)
+{
+ QuaZipFileInfo info;
+ *ok = zip->getCurrentFileInfo(&info);
+ return info;
+}
+
+template<>
+QString QuaZip_getFileInfo(QuaZip *zip, bool *ok)
+{
+ QString name = zip->getCurrentFileName();
+ *ok = !name.isEmpty();
+ return name;
+}
+
+template<typename TFileInfo>
+bool QuaZipPrivate::getFileInfoList(QList<TFileInfo> *result) const
+{
+ QuaZipPrivate *fakeThis=const_cast<QuaZipPrivate*>(this);
+ fakeThis->zipError=UNZ_OK;
+ if (mode!=QuaZip::mdUnzip) {
+ qWarning("QuaZip::getFileNameList/getFileInfoList(): "
+ "ZIP is not open in mdUnzip mode");
+ return false;
+ }
+ QString currentFile;
+ if (q->hasCurrentFile()) {
+ currentFile = q->getCurrentFileName();
+ }
+ if (q->goToFirstFile()) {
+ do {
+ bool ok;
+ result->append(QuaZip_getFileInfo<TFileInfo>(q, &ok));
+ if (!ok)
+ return false;
+ } while (q->goToNextFile());
+ }
+ if (zipError != UNZ_OK)
+ return false;
+ if (currentFile.isEmpty()) {
+ if (!q->goToFirstFile())
+ return false;
+ } else {
+ if (!q->setCurrentFile(currentFile))
+ return false;
+ }
+ return true;
+}
+
+QStringList QuaZip::getFileNameList() const
+{
+ QStringList list;
+ if (p->getFileInfoList(&list))
+ return list;
+ else
+ return QStringList();
+}
+
+QList<QuaZipFileInfo> QuaZip::getFileInfoList() const
+{
+ QList<QuaZipFileInfo> list;
+ if (p->getFileInfoList(&list))
+ return list;
+ else
+ return QList<QuaZipFileInfo>();
+}
+
+Qt::CaseSensitivity QuaZip::convertCaseSensitivity(QuaZip::CaseSensitivity cs)
+{
+ if (cs == csDefault) {
+#ifdef Q_WS_WIN
+ return Qt::CaseInsensitive;
+#else
+ return Qt::CaseSensitive;
+#endif
+ } else {
+ return cs == csSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ }
+}
diff --git a/depends/quazip/quazip.h b/depends/quazip/quazip.h
new file mode 100644
index 00000000..a3ab8e52
--- /dev/null
+++ b/depends/quazip/quazip.h
@@ -0,0 +1,411 @@
+#ifndef QUA_ZIP_H
+#define QUA_ZIP_H
+
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ **/
+
+#include <QString>
+#include <QStringList>
+#include <QTextCodec>
+
+#include "zip.h"
+#include "unzip.h"
+
+#include "quazip_global.h"
+#include "quazipfileinfo.h"
+
+// just in case it will be defined in the later versions of the ZIP/UNZIP
+#ifndef UNZ_OPENERROR
+// define additional error code
+#define UNZ_OPENERROR -1000
+#endif
+
+class QuaZipPrivate;
+
+/// ZIP archive.
+/** \class QuaZip quazip.h <quazip/quazip.h>
+ * This class implements basic interface to the ZIP archive. It can be
+ * used to read table contents of the ZIP archive and retreiving
+ * information about the files inside it.
+ *
+ * You can also use this class to open files inside archive by passing
+ * pointer to the instance of this class to the constructor of the
+ * QuaZipFile class. But see QuaZipFile::QuaZipFile(QuaZip*, QObject*)
+ * for the possible pitfalls.
+ *
+ * This class is indended to provide interface to the ZIP subpackage of
+ * the ZIP/UNZIP package as well as to the UNZIP subpackage. But
+ * currently it supports only UNZIP.
+ *
+ * The use of this class is simple - just create instance using
+ * constructor, then set ZIP archive file name using setFile() function
+ * (if you did not passed the name to the constructor), then open() and
+ * then use different functions to work with it! Well, if you are
+ * paranoid, you may also wish to call close before destructing the
+ * instance, to check for errors on close.
+ *
+ * You may also use getUnzFile() and getZipFile() functions to get the
+ * ZIP archive handle and use it with ZIP/UNZIP package API directly.
+ *
+ * This class supports localized file names inside ZIP archive, but you
+ * have to set up proper codec with setCodec() function. By default,
+ * locale codec will be used, which is probably ok for UNIX systems, but
+ * will almost certainly fail with ZIP archives created in Windows. This
+ * is because Windows ZIP programs have strange habit of using DOS
+ * encoding for file names in ZIP archives. For example, ZIP archive
+ * with cyrillic names created in Windows will have file names in \c
+ * IBM866 encoding instead of \c WINDOWS-1251. I think that calling one
+ * function is not much trouble, but for true platform independency it
+ * would be nice to have some mechanism for file name encoding auto
+ * detection using locale information. Does anyone know a good way to do
+ * it?
+ **/
+class QUAZIP_EXPORT QuaZip {
+ friend class QuaZipPrivate;
+ public:
+ /// Useful constants.
+ enum Constants {
+ MAX_FILE_NAME_LENGTH=256 /**< Maximum file name length. Taken from
+ \c UNZ_MAXFILENAMEINZIP constant in
+ unzip.c. */
+ };
+ /// Open mode of the ZIP file.
+ enum Mode {
+ mdNotOpen, ///< ZIP file is not open. This is the initial mode.
+ mdUnzip, ///< ZIP file is open for reading files inside it.
+ mdCreate, ///< ZIP file was created with open() call.
+ mdAppend, /**< ZIP file was opened in append mode. This refers to
+ * \c APPEND_STATUS_CREATEAFTER mode in ZIP/UNZIP package
+ * and means that zip is appended to some existing file
+ * what is useful when that file contains
+ * self-extractor code. This is obviously \em not what
+ * you whant to use to add files to the existing ZIP
+ * archive.
+ **/
+ mdAdd ///< ZIP file was opened for adding files in the archive.
+ };
+ /// Case sensitivity for the file names.
+ /** This is what you specify when accessing files in the archive.
+ * Works perfectly fine with any characters thanks to Qt's great
+ * unicode support. This is different from ZIP/UNZIP API, where
+ * only US-ASCII characters was supported.
+ **/
+ enum CaseSensitivity {
+ csDefault=0, ///< Default for platform. Case sensitive for UNIX, not for Windows.
+ csSensitive=1, ///< Case sensitive.
+ csInsensitive=2 ///< Case insensitive.
+ };
+ static Qt::CaseSensitivity convertCaseSensitivity(CaseSensitivity);
+ private:
+ QuaZipPrivate *p;
+ // not (and will not be) implemented
+ QuaZip(const QuaZip& that);
+ // not (and will not be) implemented
+ QuaZip& operator=(const QuaZip& that);
+ public:
+ /// Constructs QuaZip object.
+ /** Call setName() before opening constructed object. */
+ QuaZip();
+ /// Constructs QuaZip object associated with ZIP file \a zipName.
+ QuaZip(const QString& zipName);
+ /// Constructs QuaZip object associated with ZIP file represented by \a ioDevice.
+ /** The IO device must be seekable, otherwise an error will occur when opening. */
+ QuaZip(QIODevice *ioDevice);
+ /// Destroys QuaZip object.
+ /** Calls close() if necessary. */
+ ~QuaZip();
+ /// Opens ZIP file.
+ /**
+ * Argument \a mode specifies open mode of the ZIP archive. See Mode
+ * for details. Note that there is zipOpen2() function in the
+ * ZIP/UNZIP API which accepts \a globalcomment argument, but it
+ * does not use it anywhere, so this open() function does not have this
+ * argument. See setComment() if you need to set global comment.
+ *
+ * If the ZIP file is accessed via explicitly set QIODevice, then
+ * this device is opened in the necessary mode. If the device was
+ * already opened by some other means, then the behaviour is defined by
+ * the device implementation, but generally it is not a very good
+ * idea. For example, QFile will at least issue a warning.
+ *
+ * \return \c true if successful, \c false otherwise.
+ *
+ * \note ZIP/UNZIP API open calls do not return error code - they
+ * just return \c NULL indicating an error. But to make things
+ * easier, quazip.h header defines additional error code \c
+ * UNZ_ERROROPEN and getZipError() will return it if the open call
+ * of the ZIP/UNZIP API returns \c NULL.
+ *
+ * Argument \a ioApi specifies IO function set for ZIP/UNZIP
+ * package to use. See unzip.h, zip.h and ioapi.h for details. Note
+ * that IO API for QuaZip is different from the original package.
+ * The file path argument was changed to be of type \c voidpf, and
+ * QuaZip passes a QIODevice pointer there. This QIODevice is either
+ * set explicitly via setIoDevice() or the QuaZip(QIODevice*)
+ * constructor, or it is created internally when opening the archive
+ * by its file name. The default API (qioapi.cpp) just delegates
+ * everything to the QIODevice API. Not only this allows to use a
+ * QIODevice instead of file name, but also has a nice side effect
+ * of raising the file size limit from 2G to 4G.
+ *
+ * In short: just forget about the \a ioApi argument and you'll be
+ * fine.
+ **/
+ bool open(Mode mode, zlib_filefunc_def *ioApi =NULL);
+ /// Closes ZIP file.
+ /** Call getZipError() to determine if the close was successful. The
+ * underlying QIODevice is also closed, regardless of whether it was
+ * set explicitly or not. */
+ void close();
+ /// Sets the codec used to encode/decode file names inside archive.
+ /** This is necessary to access files in the ZIP archive created
+ * under Windows with non-latin characters in file names. For
+ * example, file names with cyrillic letters will be in \c IBM866
+ * encoding.
+ **/
+ void setFileNameCodec(QTextCodec *fileNameCodec);
+ /// Sets the codec used to encode/decode file names inside archive.
+ /** \overload
+ * Equivalent to calling setFileNameCodec(QTextCodec::codecForName(codecName));
+ **/
+ void setFileNameCodec(const char *fileNameCodecName);
+ /// Returns the codec used to encode/decode comments inside archive.
+ QTextCodec* getFileNameCodec() const;
+ /// Sets the codec used to encode/decode comments inside archive.
+ /** This codec defaults to locale codec, which is probably ok.
+ **/
+ void setCommentCodec(QTextCodec *commentCodec);
+ /// Sets the codec used to encode/decode comments inside archive.
+ /** \overload
+ * Equivalent to calling setCommentCodec(QTextCodec::codecForName(codecName));
+ **/
+ void setCommentCodec(const char *commentCodecName);
+ /// Returns the codec used to encode/decode comments inside archive.
+ QTextCodec* getCommentCodec() const;
+ /// Returns the name of the ZIP file.
+ /** Returns null string if no ZIP file name has been set, for
+ * example when the QuaZip instance is set up to use a QIODevice
+ * instead.
+ * \sa setZipName(), setIoDevice(), getIoDevice()
+ **/
+ QString getZipName() const;
+ /// Sets the name of the ZIP file.
+ /** Does nothing if the ZIP file is open.
+ *
+ * Does not reset error code returned by getZipError().
+ * \sa setIoDevice(), getIoDevice(), getZipName()
+ **/
+ void setZipName(const QString& zipName);
+ /// Returns the device representing this ZIP file.
+ /** Returns null string if no device has been set explicitly, for
+ * example when opening a ZIP file by name.
+ * \sa setIoDevice(), getZipName(), setZipName()
+ **/
+ QIODevice *getIoDevice() const;
+ /// Sets the device representing the ZIP file.
+ /** Does nothing if the ZIP file is open.
+ *
+ * Does not reset error code returned by getZipError().
+ * \sa getIoDevice(), getZipName(), setZipName()
+ **/
+ void setIoDevice(QIODevice *ioDevice);
+ /// Returns the mode in which ZIP file was opened.
+ Mode getMode() const;
+ /// Returns \c true if ZIP file is open, \c false otherwise.
+ bool isOpen() const;
+ /// Returns the error code of the last operation.
+ /** Returns \c UNZ_OK if the last operation was successful.
+ *
+ * Error code resets to \c UNZ_OK every time you call any function
+ * that accesses something inside ZIP archive, even if it is \c
+ * const (like getEntriesCount()). open() and close() calls reset
+ * error code too. See documentation for the specific functions for
+ * details on error detection.
+ **/
+ int getZipError() const;
+ /// Returns number of the entries in the ZIP central directory.
+ /** Returns negative error code in the case of error. The same error
+ * code will be returned by subsequent getZipError() call.
+ **/
+ int getEntriesCount() const;
+ /// Returns global comment in the ZIP file.
+ QString getComment() const;
+ /// Sets the global comment in the ZIP file.
+ /** The comment will be written to the archive on close operation.
+ * QuaZip makes a distinction between a null QByteArray() comment
+ * and an empty &quot;&quot; comment in the QuaZip::mdAdd mode.
+ * A null comment is the default and it means &quot;don't change
+ * the comment&quot;. An empty comment removes the original comment.
+ *
+ * \sa open()
+ **/
+ void setComment(const QString& comment);
+ /// Sets the current file to the first file in the archive.
+ /** Returns \c true on success, \c false otherwise. Call
+ * getZipError() to get the error code.
+ **/
+ bool goToFirstFile();
+ /// Sets the current file to the next file in the archive.
+ /** Returns \c true on success, \c false otherwise. Call
+ * getZipError() to determine if there was an error.
+ *
+ * Should be used only in QuaZip::mdUnzip mode.
+ *
+ * \note If the end of file was reached, getZipError() will return
+ * \c UNZ_OK instead of \c UNZ_END_OF_LIST_OF_FILE. This is to make
+ * things like this easier:
+ * \code
+ * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) {
+ * // do something
+ * }
+ * if(zip.getZipError()==UNZ_OK) {
+ * // ok, there was no error
+ * }
+ * \endcode
+ **/
+ bool goToNextFile();
+ /// Sets current file by its name.
+ /** Returns \c true if successful, \c false otherwise. Argument \a
+ * cs specifies case sensitivity of the file name. Call
+ * getZipError() in the case of a failure to get error code.
+ *
+ * This is not a wrapper to unzLocateFile() function. That is
+ * because I had to implement locale-specific case-insensitive
+ * comparison.
+ *
+ * Here are the differences from the original implementation:
+ *
+ * - If the file was not found, error code is \c UNZ_OK, not \c
+ * UNZ_END_OF_LIST_OF_FILE (see also goToNextFile()).
+ * - If this function fails, it unsets the current file rather than
+ * resetting it back to what it was before the call.
+ *
+ * If \a fileName is null string then this function unsets the
+ * current file and return \c true. Note that you should close the
+ * file first if it is open! See
+ * QuaZipFile::QuaZipFile(QuaZip*,QObject*) for the details.
+ *
+ * Should be used only in QuaZip::mdUnzip mode.
+ *
+ * \sa setFileNameCodec(), CaseSensitivity
+ **/
+ bool setCurrentFile(const QString& fileName, CaseSensitivity cs =csDefault);
+ /// Returns \c true if the current file has been set.
+ bool hasCurrentFile() const;
+ /// Retrieves information about the current file.
+ /** Fills the structure pointed by \a info. Returns \c true on
+ * success, \c false otherwise. In the latter case structure pointed
+ * by \a info remains untouched. If there was an error,
+ * getZipError() returns error code.
+ *
+ * Should be used only in QuaZip::mdUnzip mode.
+ *
+ * Does nothing and returns \c false in any of the following cases.
+ * - ZIP is not open;
+ * - ZIP does not have current file;
+ * - \a info is \c NULL;
+ *
+ * In all these cases getZipError() returns \c UNZ_OK since there
+ * is no ZIP/UNZIP API call.
+ **/
+ bool getCurrentFileInfo(QuaZipFileInfo* info)const;
+ /// Returns the current file name.
+ /** Equivalent to calling getCurrentFileInfo() and then getting \c
+ * name field of the QuaZipFileInfo structure, but faster and more
+ * convenient.
+ *
+ * Should be used only in QuaZip::mdUnzip mode.
+ **/
+ QString getCurrentFileName()const;
+ /// Returns \c unzFile handle.
+ /** You can use this handle to directly call UNZIP part of the
+ * ZIP/UNZIP package functions (see unzip.h).
+ *
+ * \warning When using the handle returned by this function, please
+ * keep in mind that QuaZip class is unable to detect any changes
+ * you make in the ZIP file state (e. g. changing current file, or
+ * closing the handle). So please do not do anything with this
+ * handle that is possible to do with the functions of this class.
+ * Or at least return the handle in the original state before
+ * calling some another function of this class (including implicit
+ * destructor calls and calls from the QuaZipFile objects that refer
+ * to this QuaZip instance!). So if you have changed the current
+ * file in the ZIP archive - then change it back or you may
+ * experience some strange behavior or even crashes.
+ **/
+ unzFile getUnzFile();
+ /// Returns \c zipFile handle.
+ /** You can use this handle to directly call ZIP part of the
+ * ZIP/UNZIP package functions (see zip.h). Warnings about the
+ * getUnzFile() function also apply to this function.
+ **/
+ zipFile getZipFile();
+ /// Changes the data descriptor writing mode.
+ /**
+ According to the ZIP format specification, a file inside archive
+ may have a data descriptor immediately following the file
+ data. This is reflected by a special flag in the local file header
+ and in the central directory. By default, QuaZIP sets this flag
+ and writes the data descriptor unless both method and level were
+ set to 0, in which case it operates in 1.0-compatible mode and
+ never writes data descriptors.
+
+ By setting this flag to false, it is possible to disable data
+ descriptor writing, thus increasing compatibility with archive
+ readers that don't understand this feature of the ZIP file format.
+
+ Setting this flag affects all the QuaZipFile instances that are
+ opened after this flag is set.
+
+ The data descriptor writing mode is enabled by default.
+
+ \param enabled If \c true, enable local descriptor writing,
+ disable it otherwise.
+
+ \sa QuaZipFile::setDataDescriptorWritingEnabled()
+ */
+ void setDataDescriptorWritingEnabled(bool enabled);
+ /// Returns the data descriptor default writing mode.
+ /**
+ \sa setDataDescriptorWritingEnabled()
+ */
+ bool isDataDescriptorWritingEnabled() const;
+ /// Returns a list of files inside the archive.
+ /**
+ \return A list of file names or an empty list if there
+ was an error or if the archive is empty (call getZipError() to
+ figure out which).
+ \sa getFileInfoList()
+ */
+ QStringList getFileNameList() const;
+ /// Returns information list about all files inside the archive.
+ /**
+ \return A list of QuaZipFileInfo objects or an empty list if there
+ was an error or if the archive is empty (call getZipError() to
+ figure out which).
+ \sa getFileNameList()
+ */
+ QList<QuaZipFileInfo> getFileInfoList() const;
+};
+
+#endif
diff --git a/depends/quazip/quazip_global.h b/depends/quazip/quazip_global.h
new file mode 100644
index 00000000..d9d09ade
--- /dev/null
+++ b/depends/quazip/quazip_global.h
@@ -0,0 +1,55 @@
+/**
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ */
+
+#ifndef QUAZIP_GLOBAL_H
+#define QUAZIP_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+/**
+ This is automatically defined when building a static library, but when
+ including QuaZip sources directly into a project, QUAZIP_STATIC should
+ be defined explicitly to avoid possible troubles with unnecessary
+ importing/exporting.
+ */
+#ifdef QUAZIP_STATIC
+#define QUAZIP_EXPORT
+#else
+/**
+ * When building a DLL with MSVC, QUAZIP_BUILD must be defined.
+ * qglobal.h takes care of defining Q_DECL_* correctly for msvc/gcc.
+ */
+#if defined(QUAZIP_BUILD)
+ #define QUAZIP_EXPORT Q_DECL_EXPORT
+#else
+ #define QUAZIP_EXPORT Q_DECL_IMPORT
+#endif
+#endif // QUAZIP_STATIC
+
+#ifdef __GNUC__
+#define UNUSED __attribute__((__unused__))
+#else
+#define UNUSED
+#endif
+
+#endif // QUAZIP_GLOBAL_H
diff --git a/depends/quazip/quazipdir.cpp b/depends/quazip/quazipdir.cpp
new file mode 100644
index 00000000..02208894
--- /dev/null
+++ b/depends/quazip/quazipdir.cpp
@@ -0,0 +1,507 @@
+#include "quazipdir.h"
+
+#include <QSet>
+#include <QSharedData>
+
+class QuaZipDirPrivate: public QSharedData {
+ friend class QuaZipDir;
+private:
+ QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()):
+ zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault),
+ filter(QDir::NoFilter), sorting(QDir::NoSort) {}
+ QuaZip *zip;
+ QString dir;
+ QuaZip::CaseSensitivity caseSensitivity;
+ QDir::Filters filter;
+ QStringList nameFilters;
+ QDir::SortFlags sorting;
+ template<typename TFileInfoList>
+ bool entryInfoList(QStringList nameFilters, QDir::Filters filter,
+ QDir::SortFlags sort, TFileInfoList &result) const;
+ inline QString simplePath() const {return QDir::cleanPath(dir);}
+};
+
+QuaZipDir::QuaZipDir(const QuaZipDir &that):
+ d(that.d)
+{
+}
+
+QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir):
+ d(new QuaZipDirPrivate(zip, dir))
+{
+ if (d->dir.startsWith('/'))
+ d->dir = d->dir.mid(1);
+}
+
+QuaZipDir::~QuaZipDir()
+{
+}
+
+bool QuaZipDir::operator==(const QuaZipDir &that)
+{
+ return d->zip == that.d->zip && d->dir == that.d->dir;
+}
+
+QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that)
+{
+ this->d = that.d;
+ return *this;
+}
+
+QString QuaZipDir::operator[](int pos) const
+{
+ return entryList().at(pos);
+}
+
+QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const
+{
+ return d->caseSensitivity;
+}
+
+bool QuaZipDir::cd(const QString &directoryName)
+{
+ if (directoryName == "/") {
+ d->dir = "";
+ return true;
+ }
+ QString dirName = directoryName;
+ if (dirName.endsWith('/'))
+ dirName.chop(1);
+ if (dirName.contains('/')) {
+ QuaZipDir dir(*this);
+ if (dirName.startsWith('/')) {
+#ifdef QUAZIP_QUAZIPDIR_DEBUG
+ qDebug("QuaZipDir::cd(%s): going to /",
+ dirName.toUtf8().constData());
+#endif
+ if (!dir.cd("/"))
+ return false;
+ }
+ QStringList path = dirName.split('/', QString::SkipEmptyParts);
+ for (QStringList::const_iterator i = path.constBegin();
+ i != path.end();
+ ++i) {
+ const QString &step = *i;
+#ifdef QUAZIP_QUAZIPDIR_DEBUG
+ qDebug("QuaZipDir::cd(%s): going to %s",
+ dirName.toUtf8().constData(),
+ step.toUtf8().constData());
+#endif
+ if (!dir.cd(step))
+ return false;
+ }
+ d->dir = dir.path();
+ return true;
+ } else { // no '/'
+ if (dirName == ".") {
+ return true;
+ } else if (dirName == "..") {
+ if (isRoot()) {
+ return false;
+ } else {
+ int slashPos = d->dir.lastIndexOf('/');
+ if (slashPos == -1) {
+ d->dir = "";
+ } else {
+ d->dir = d->dir.left(slashPos);
+ }
+ return true;
+ }
+ } else { // a simple subdirectory
+ if (exists(dirName)) {
+ if (isRoot())
+ d->dir = dirName;
+ else
+ d->dir += "/" + dirName;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
+
+bool QuaZipDir::cdUp()
+{
+ return cd("..");
+}
+
+uint QuaZipDir::count() const
+{
+ return entryList().count();
+}
+
+QString QuaZipDir::dirName() const
+{
+ return QDir(d->dir).dirName();
+}
+
+QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
+ const QString &relativeName,
+ bool isReal)
+{
+ QuaZipFileInfo info;
+ if (isReal) {
+ *ok = zip->getCurrentFileInfo(&info);
+ } else {
+ *ok = true;
+ info.compressedSize = 0;
+ info.crc = 0;
+ info.diskNumberStart = 0;
+ info.externalAttr = 0;
+ info.flags = 0;
+ info.internalAttr = 0;
+ info.method = 0;
+ info.uncompressedSize = 0;
+ info.versionCreated = info.versionNeeded = 0;
+ }
+ info.name = relativeName;
+ return info;
+}
+
+template<typename TFileInfoList>
+void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, TFileInfoList &to);
+
+template<>
+void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QList<QuaZipFileInfo> &to)
+{
+ to = from;
+}
+
+template<>
+void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QStringList &to)
+{
+ to.clear();
+ for (QList<QuaZipFileInfo>::const_iterator i = from.constBegin();
+ i != from.constEnd();
+ ++i) {
+ to.append(i->name);
+ }
+}
+
+// utility class to restore the current file
+class QuaZipDirRestoreCurrent {
+public:
+ inline QuaZipDirRestoreCurrent(QuaZip *zip):
+ zip(zip), currentFile(zip->getCurrentFileName()) {}
+ inline ~QuaZipDirRestoreCurrent()
+ {
+ zip->setCurrentFile(currentFile);
+ }
+private:
+ QuaZip *zip;
+ QString currentFile;
+};
+
+class QuaZipDirComparator
+{
+ private:
+ QDir::SortFlags sort;
+ static QString getExtension(const QString &name);
+ int compareStrings(const QString &string1, const QString &string2);
+ public:
+ inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {}
+ bool operator()(const QuaZipFileInfo &info1, const QuaZipFileInfo &info2);
+};
+
+QString QuaZipDirComparator::getExtension(const QString &name)
+{
+ if (name.endsWith('.') || name.indexOf('.', 1) == -1) {
+ return "";
+ } else {
+ return name.mid(name.lastIndexOf('.') + 1);
+ }
+
+}
+
+int QuaZipDirComparator::compareStrings(const QString &string1,
+ const QString &string2)
+{
+ if (sort & QDir::LocaleAware) {
+ if (sort & QDir::IgnoreCase) {
+ return string1.toLower().localeAwareCompare(string2.toLower());
+ } else {
+ return string1.localeAwareCompare(string2);
+ }
+ } else {
+ return string1.compare(string2, (sort & QDir::IgnoreCase)
+ ? Qt::CaseInsensitive : Qt::CaseSensitive);
+ }
+}
+
+bool QuaZipDirComparator::operator()(const QuaZipFileInfo &info1,
+ const QuaZipFileInfo &info2)
+{
+ QDir::SortFlags order = sort
+ & (QDir::Name | QDir::Time | QDir::Size | QDir::Type);
+ if ((sort & QDir::DirsFirst) == QDir::DirsFirst
+ || (sort & QDir::DirsLast) == QDir::DirsLast) {
+ if (info1.name.endsWith('/') && !info2.name.endsWith('/'))
+ return (sort & QDir::DirsFirst) == QDir::DirsFirst;
+ else if (!info1.name.endsWith('/') && info2.name.endsWith('/'))
+ return (sort & QDir::DirsLast) == QDir::DirsLast;
+ }
+ bool result;
+ int extDiff;
+ switch (order) {
+ case QDir::Name:
+ result = compareStrings(info1.name, info2.name) < 0;
+ break;
+ case QDir::Type:
+ extDiff = compareStrings(getExtension(info1.name),
+ getExtension(info2.name));
+ if (extDiff == 0) {
+ result = compareStrings(info1.name, info2.name) < 0;
+ } else {
+ result = extDiff < 0;
+ }
+ break;
+ case QDir::Size:
+ if (info1.uncompressedSize == info2.uncompressedSize) {
+ result = compareStrings(info1.name, info2.name) < 0;
+ } else {
+ result = info1.uncompressedSize < info2.uncompressedSize;
+ }
+ break;
+ case QDir::Time:
+ if (info1.dateTime == info2.dateTime) {
+ result = compareStrings(info1.name, info2.name) < 0;
+ } else {
+ result = info1.dateTime < info2.dateTime;
+ }
+ break;
+ default:
+ qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X",
+ static_cast<unsigned>(sort));
+ return false;
+ }
+ return (sort & QDir::Reversed) ? !result : result;
+}
+
+template<typename TFileInfoList>
+bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters,
+ QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const
+{
+ QString basePath = simplePath();
+ if (!basePath.isEmpty())
+ basePath += "/";
+ int baseLength = basePath.length();
+ result.clear();
+ QuaZipDirRestoreCurrent saveCurrent(zip);
+ if (!zip->goToFirstFile()) {
+ return zip->getZipError() == UNZ_OK;
+ }
+ QDir::Filters fltr = filter;
+ if (fltr == QDir::NoFilter)
+ fltr = this->filter;
+ if (fltr == QDir::NoFilter)
+ fltr = QDir::AllEntries;
+ QStringList nmfltr = nameFilters;
+ if (nmfltr.isEmpty())
+ nmfltr = this->nameFilters;
+ QSet<QString> dirsFound;
+ QList<QuaZipFileInfo> list;
+ do {
+ QString name = zip->getCurrentFileName();
+ if (!name.startsWith(basePath))
+ continue;
+ QString relativeName = name.mid(baseLength);
+ bool isDir = false;
+ bool isReal = true;
+ if (relativeName.contains('/')) {
+ int indexOfSlash = relativeName.indexOf('/');
+ // something like "subdir/"
+ isReal = indexOfSlash == relativeName.length() - 1;
+ relativeName = relativeName.left(indexOfSlash + 1);
+ if (dirsFound.contains(relativeName))
+ continue;
+ isDir = true;
+ }
+ dirsFound.insert(relativeName);
+ if ((fltr & QDir::Dirs) == 0 && isDir)
+ continue;
+ if ((fltr & QDir::Files) == 0 && !isDir)
+ continue;
+ if (!nmfltr.isEmpty() && QDir::match(nmfltr, relativeName))
+ continue;
+ bool ok;
+ QuaZipFileInfo info = QuaZipDir_getFileInfo(zip, &ok, relativeName,
+ isReal);
+ if (!ok) {
+ return false;
+ }
+ list.append(info);
+ } while (zip->goToNextFile());
+ QDir::SortFlags srt = sort;
+ if (srt == QDir::NoSort)
+ srt = sorting;
+#ifdef QUAZIP_QUAZIPDIR_DEBUG
+ qDebug("QuaZipDirPrivate::entryInfoList(): before sort:");
+ foreach (QuaZipFileInfo info, list) {
+ qDebug("%s\t%s", info.name.toUtf8().constData(),
+ info.dateTime.toString(Qt::ISODate).toUtf8().constData());
+ }
+#endif
+ if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) {
+ if (QuaZip::convertCaseSensitivity(caseSensitivity)
+ == Qt::CaseInsensitive)
+ srt |= QDir::IgnoreCase;
+ QuaZipDirComparator lessThan(srt);
+ qSort(list.begin(), list.end(), lessThan);
+ }
+ QuaZipDir_convertInfoList(list, result);
+ return true;
+}
+
+QList<QuaZipFileInfo> QuaZipDir::entryInfoList(const QStringList &nameFilters,
+ QDir::Filters filters, QDir::SortFlags sort) const
+{
+ QList<QuaZipFileInfo> result;
+ if (d->entryInfoList(nameFilters, filters, sort, result))
+ return result;
+ else
+ return QList<QuaZipFileInfo>();
+}
+
+QList<QuaZipFileInfo> QuaZipDir::entryInfoList(QDir::Filters filters,
+ QDir::SortFlags sort) const
+{
+ return entryInfoList(QStringList(), filters, sort);
+}
+
+QStringList QuaZipDir::entryList(const QStringList &nameFilters,
+ QDir::Filters filters, QDir::SortFlags sort) const
+{
+ QStringList result;
+ if (d->entryInfoList(nameFilters, filters, sort, result))
+ return result;
+ else
+ return QStringList();
+}
+
+QStringList QuaZipDir::entryList(QDir::Filters filters,
+ QDir::SortFlags sort) const
+{
+ return entryList(QStringList(), filters, sort);
+}
+
+bool QuaZipDir::exists(const QString &filePath) const
+{
+ if (filePath == "/")
+ return true;
+ QString fileName = filePath;
+ if (fileName.endsWith('/'))
+ fileName.chop(1);
+ if (fileName.contains('/')) {
+ QFileInfo fileInfo(fileName);
+#ifdef QUAZIP_QUAZIPDIR_DEBUG
+ qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, "
+ "fileInfo.path()=%s", fileName.toUtf8().constData(),
+ fileInfo.fileName().toUtf8().constData(),
+ fileInfo.path().toUtf8().constData());
+#endif
+ QuaZipDir dir(*this);
+ return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName());
+ } else {
+ if (fileName == "..") {
+ return !isRoot();
+ } else if (fileName == ".") {
+ return true;
+ } else {
+ QStringList entries = entryList(QDir::AllEntries, QDir::NoSort);
+#ifdef QUAZIP_QUAZIPDIR_DEBUG
+ qDebug("QuaZipDir::exists(): looking for %s",
+ fileName.toUtf8().constData());
+ for (QStringList::const_iterator i = entries.constBegin();
+ i != entries.constEnd();
+ ++i) {
+ qDebug("QuaZipDir::exists(): entry: %s",
+ i->toUtf8().constData());
+ }
+#endif
+ Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity(
+ d->caseSensitivity);
+ if (filePath.endsWith('/')) {
+ return entries.contains(filePath, cs);
+ } else {
+ return entries.contains(fileName, cs)
+ || entries.contains(fileName + "/", cs);
+ }
+ }
+ }
+}
+
+bool QuaZipDir::exists() const
+{
+ QDir thisDir(d->dir);
+ return QuaZipDir(d->zip, thisDir.filePath("..")).exists(thisDir.dirName());
+}
+
+QString QuaZipDir::filePath(const QString &fileName) const
+{
+ return QDir(d->dir).filePath(fileName);
+}
+
+QDir::Filters QuaZipDir::filter()
+{
+ return d->filter;
+}
+
+bool QuaZipDir::isRoot() const
+{
+ return d->simplePath().isEmpty();
+}
+
+QStringList QuaZipDir::nameFilters() const
+{
+ return d->nameFilters;
+}
+
+QString QuaZipDir::path() const
+{
+ return d->dir;
+}
+
+QString QuaZipDir::relativeFilePath(const QString &fileName) const
+{
+ return QDir(d->dir).relativeFilePath(fileName);
+}
+
+void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity)
+{
+ d->caseSensitivity = caseSensitivity;
+}
+
+void QuaZipDir::setFilter(QDir::Filters filters)
+{
+ d->filter = filters;
+}
+
+void QuaZipDir::setNameFilters(const QStringList &nameFilters)
+{
+ d->nameFilters = nameFilters;
+}
+
+void QuaZipDir::setPath(const QString &path)
+{
+ QString newDir = path;
+ if (newDir == "/") {
+ d->dir = "";
+ } else {
+ if (newDir.endsWith('/'))
+ newDir.chop(1);
+ if (newDir.startsWith('/'))
+ newDir = newDir.mid(1);
+ d->dir = newDir;
+ }
+}
+
+void QuaZipDir::setSorting(QDir::SortFlags sort)
+{
+ d->sorting = sort;
+}
+
+QDir::SortFlags QuaZipDir::sorting() const
+{
+ return d->sorting;
+}
diff --git a/depends/quazip/quazipdir.h b/depends/quazip/quazipdir.h
new file mode 100644
index 00000000..e2d70bc8
--- /dev/null
+++ b/depends/quazip/quazipdir.h
@@ -0,0 +1,171 @@
+#ifndef QUAZIP_QUAZIPDIR_H
+#define QUAZIP_QUAZIPDIR_H
+
+class QuaZipDirPrivate;
+
+#include "quazip.h"
+#include "quazipfileinfo.h"
+#include <QDir>
+#include <QList>
+#include <QSharedDataPointer>
+
+/// Provides ZIP archive navigation.
+/**
+* This class is modelled after QDir, and is designed to provide similar
+* features for ZIP archives.
+*
+* The only significant difference from QDir is that the root path is not
+* '/', but an empty string since that's how the file paths are stored in
+* the archive. However, QuaZipDir understands the paths starting with
+* '/'. It is important in a few places:
+*
+* - In the cd() function.
+* - In the constructor.
+* - In the exists() function.
+*
+* Note that since ZIP uses '/' on all platforms, the '\' separator is
+* not supported.
+*/
+class QUAZIP_EXPORT QuaZipDir {
+private:
+ QSharedDataPointer<QuaZipDirPrivate> d;
+public:
+ /// The copy constructor.
+ QuaZipDir(const QuaZipDir &that);
+ /// Constructs a QuaZipDir instance pointing to the specified directory.
+ /**
+ If \a dir is not specified, points to the root of the archive.
+ The same happens if the \a dir is &quot;/&quot;.
+ */
+ QuaZipDir(QuaZip *zip, const QString &dir = QString());
+ /// Destructor.
+ ~QuaZipDir();
+ /// The assignment operator.
+ bool operator==(const QuaZipDir &that);
+ /// operator!=
+ /**
+ \return \c true if either this and \a that use different QuaZip
+ instances or if they point to different directories.
+ */
+ inline bool operator!=(const QuaZipDir &that) {return !operator==(that);}
+ /// operator==
+ /**
+ \return \c true if both this and \a that use the same QuaZip
+ instance and point to the same directory.
+ */
+ QuaZipDir& operator=(const QuaZipDir &that);
+ /// Returns the name of the entry at the specified position.
+ QString operator[](int pos) const;
+ /// Returns the current case sensitivity mode.
+ QuaZip::CaseSensitivity caseSensitivity() const;
+ /// Changes the 'current' directory.
+ /**
+ * If the path starts with '/', it is interpreted as an absolute
+ * path from the root of the archive. Otherwise, it is interpreted
+ * as a path relative to the current directory as was set by the
+ * previous cd() or the constructor.
+ *
+ * Note that the subsequent path() call will not return a path
+ * starting with '/' in all cases.
+ */
+ bool cd(const QString &dirName);
+ /// Goes up.
+ bool cdUp();
+ /// Returns the number of entries in the directory.
+ uint count() const;
+ /// Returns the current directory name.
+ /**
+ The name doesn't include the path.
+ */
+ QString dirName() const;
+ /// Returns the list of the entries in the directory.
+ /**
+ \param nameFilters The list of file patterns to list, uses the same
+ syntax as QDir.
+ \param filters The entry type filters, only Files and Dirs are
+ accepted.
+ \param sort Sorting mode (not supported yet).
+ */
+ QList<QuaZipFileInfo> entryInfoList(const QStringList &nameFilters,
+ QDir::Filters filters = QDir::NoFilter,
+ QDir::SortFlags sort = QDir::NoSort) const;
+ /// Returns the list of the entries in the directory.
+ /**
+ \overload
+
+ The same as entryInfoList(QStringList(), filters, sort).
+ */
+ QList<QuaZipFileInfo> entryInfoList(QDir::Filters filters = QDir::NoFilter,
+ QDir::SortFlags sort = QDir::NoSort) const;
+ /// Returns the list of the entry names in the directory.
+ /**
+ The same as entryInfoList(nameFilters, filters, sort), but only
+ returns entry names.
+ */
+ QStringList entryList(const QStringList &nameFilters,
+ QDir::Filters filters = QDir::NoFilter,
+ QDir::SortFlags sort = QDir::NoSort) const;
+ /// Returns the list of the entry names in the directory.
+ /**
+ \overload
+
+ The same as entryList(QStringList(), filters, sort).
+ */
+ QStringList entryList(QDir::Filters filters = QDir::NoFilter,
+ QDir::SortFlags sort = QDir::NoSort) const;
+ /// Returns \c true if the entry with the specified name exists.
+ /**
+ The &quot;..&quot; is considered to exist if the current directory
+ is not root. The &quot;.&quot; and &quot;/&quot; are considered to
+ always exist. Paths starting with &quot;/&quot; are relative to
+ the archive root, other paths are relative to the current dir.
+ */
+ bool exists(const QString &fileName) const;
+ /// Return \c true if the directory pointed by this QuaZipDir exists.
+ bool exists() const;
+ /// Returns the full path to the specified file.
+ /**
+ Doesn't check if the file actually exists.
+ */
+ QString filePath(const QString &fileName) const;
+ /// Returns the default filter.
+ QDir::Filters filter();
+ /// Returns if the QuaZipDir points to the root of the archive.
+ /**
+ Not that the root path is the empty string, not '/'.
+ */
+ bool isRoot() const;
+ /// Return the default name filter.
+ QStringList nameFilters() const;
+ /// Returns the path to the current dir.
+ /**
+ The path never starts with '/', and the root path is an empty
+ string.
+ */
+ QString path() const;
+ /// Returns the path to the specified file relative to the current dir.
+ QString relativeFilePath(const QString &fileName) const;
+ /// Sets the default case sensitivity mode.
+ void setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity);
+ /// Sets the default filter.
+ void setFilter(QDir::Filters filters);
+ /// Sets the default name filter.
+ void setNameFilters(const QStringList &nameFilters);
+ /// Goes to the specified path.
+ /**
+ The difference from cd() is that this function never checks if the
+ path actually exists and doesn't use relative paths, so it's
+ possible to go to the root directory with setPath(&quot;&quot;).
+
+ Note that this function still chops the trailing and/or leading
+ '/' and treats a single '/' as the root path (path() will still
+ return an empty string).
+ */
+ void setPath(const QString &path);
+ /// Sets the default sorting mode.
+ void setSorting(QDir::SortFlags sort);
+ /// Returns the default sorting mode.
+ QDir::SortFlags sorting() const;
+};
+
+#endif // QUAZIP_QUAZIPDIR_H
diff --git a/depends/quazip/quazipfile.cpp b/depends/quazip/quazipfile.cpp
new file mode 100644
index 00000000..323f815e
--- /dev/null
+++ b/depends/quazip/quazipfile.cpp
@@ -0,0 +1,488 @@
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ **/
+
+#include "quazipfile.h"
+
+using namespace std;
+
+/// The implementation class for QuaZip.
+/**
+\internal
+
+This class contains all the private stuff for the QuaZipFile class, thus
+allowing to preserve binary compatibility between releases, the
+technique known as the Pimpl (private implementation) idiom.
+*/
+class QuaZipFilePrivate {
+ friend class QuaZipFile;
+ private:
+ /// The pointer to the associated QuaZipFile instance.
+ QuaZipFile *q;
+ /// The QuaZip object to work with.
+ QuaZip *zip;
+ /// The file name.
+ QString fileName;
+ /// Case sensitivity mode.
+ QuaZip::CaseSensitivity caseSensitivity;
+ /// Whether this file is opened in the raw mode.
+ bool raw;
+ /// Write position to keep track of.
+ /**
+ QIODevice::pos() is broken for non-seekable devices, so we need
+ our own position.
+ */
+ qint64 writePos;
+ /// Uncompressed size to write along with a raw file.
+ ulong uncompressedSize;
+ /// CRC to write along with a raw file.
+ quint32 crc;
+ /// Whether \ref zip points to an internal QuaZip instance.
+ /**
+ This is true if the archive was opened by name, rather than by
+ supplying an existing QuaZip instance.
+ */
+ bool internal;
+ /// The last error.
+ int zipError;
+ /// Resets \ref zipError.
+ inline void resetZipError() const {setZipError(UNZ_OK);}
+ /// Sets the zip error.
+ /**
+ This function is marked as const although it changes one field.
+ This allows to call it from const functions that don't change
+ anything by themselves.
+ */
+ void setZipError(int zipError) const;
+ /// The constructor for the corresponding QuaZipFile constructor.
+ inline QuaZipFilePrivate(QuaZipFile *q):
+ q(q), zip(NULL), internal(true), zipError(UNZ_OK) {}
+ /// The constructor for the corresponding QuaZipFile constructor.
+ inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName):
+ q(q), internal(true), zipError(UNZ_OK)
+ {
+ zip=new QuaZip(zipName);
+ }
+ /// The constructor for the corresponding QuaZipFile constructor.
+ inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName,
+ QuaZip::CaseSensitivity cs):
+ q(q), internal(true), zipError(UNZ_OK)
+ {
+ zip=new QuaZip(zipName);
+ this->fileName=fileName;
+ if (this->fileName.startsWith('/'))
+ this->fileName = this->fileName.mid(1);
+ this->caseSensitivity=cs;
+ }
+ /// The constructor for the QuaZipFile constructor accepting a file name.
+ inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip):
+ q(q), zip(zip), internal(false), zipError(UNZ_OK) {}
+ /// The destructor.
+ inline ~QuaZipFilePrivate()
+ {
+ if (internal)
+ delete zip;
+ }
+};
+
+QuaZipFile::QuaZipFile():
+ p(new QuaZipFilePrivate(this))
+{
+}
+
+QuaZipFile::QuaZipFile(QObject *parent):
+ QIODevice(parent),
+ p(new QuaZipFilePrivate(this))
+{
+}
+
+QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent):
+ QIODevice(parent),
+ p(new QuaZipFilePrivate(this, zipName))
+{
+}
+
+QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName,
+ QuaZip::CaseSensitivity cs, QObject *parent):
+ QIODevice(parent),
+ p(new QuaZipFilePrivate(this, zipName, fileName, cs))
+{
+}
+
+QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent):
+ QIODevice(parent),
+ p(new QuaZipFilePrivate(this, zip))
+{
+}
+
+QuaZipFile::~QuaZipFile()
+{
+ if (isOpen())
+ close();
+ delete p;
+}
+
+QString QuaZipFile::getZipName() const
+{
+ return p->zip==NULL ? QString() : p->zip->getZipName();
+}
+
+QuaZip *QuaZipFile::getZip() const
+{
+ return p->internal ? NULL : p->zip;
+}
+
+QString QuaZipFile::getActualFileName()const
+{
+ p->setZipError(UNZ_OK);
+ if (p->zip == NULL || (openMode() & WriteOnly))
+ return QString();
+ QString name=p->zip->getCurrentFileName();
+ if(name.isNull())
+ p->setZipError(p->zip->getZipError());
+ return name;
+}
+
+void QuaZipFile::setZipName(const QString& zipName)
+{
+ if(isOpen()) {
+ qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name");
+ return;
+ }
+ if(p->zip!=NULL && p->internal)
+ delete p->zip;
+ p->zip=new QuaZip(zipName);
+ p->internal=true;
+}
+
+void QuaZipFile::setZip(QuaZip *zip)
+{
+ if(isOpen()) {
+ qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP");
+ return;
+ }
+ if(p->zip!=NULL && p->internal)
+ delete p->zip;
+ p->zip=zip;
+ p->fileName=QString();
+ p->internal=false;
+}
+
+void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs)
+{
+ if(p->zip==NULL) {
+ qWarning("QuaZipFile::setFileName(): call setZipName() first");
+ return;
+ }
+ if(!p->internal) {
+ qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip");
+ return;
+ }
+ if(isOpen()) {
+ qWarning("QuaZipFile::setFileName(): can not set file name for already opened file");
+ return;
+ }
+ p->fileName=fileName;
+ if (p->fileName.startsWith('/'))
+ p->fileName = p->fileName.mid(1);
+ p->caseSensitivity=cs;
+}
+
+void QuaZipFilePrivate::setZipError(int zipError) const
+{
+ QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const
+ fakeThis->zipError=zipError;
+ if(zipError==UNZ_OK)
+ q->setErrorString(QString());
+ else
+ q->setErrorString(q->tr("ZIP/UNZIP API error %1").arg(zipError));
+}
+
+bool QuaZipFile::open(OpenMode mode)
+{
+ return open(mode, NULL);
+}
+
+bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password)
+{
+ p->resetZipError();
+ if(isOpen()) {
+ qWarning("QuaZipFile::open(): already opened");
+ return false;
+ }
+ if(mode&Unbuffered) {
+ qWarning("QuaZipFile::open(): Unbuffered mode is not supported");
+ return false;
+ }
+ if((mode&ReadOnly)&&!(mode&WriteOnly)) {
+ if(p->internal) {
+ if(!p->zip->open(QuaZip::mdUnzip)) {
+ p->setZipError(p->zip->getZipError());
+ return false;
+ }
+ if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) {
+ p->setZipError(p->zip->getZipError());
+ p->zip->close();
+ return false;
+ }
+ } else {
+ if(p->zip==NULL) {
+ qWarning("QuaZipFile::open(): zip is NULL");
+ return false;
+ }
+ if(p->zip->getMode()!=QuaZip::mdUnzip) {
+ qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
+ (int)mode, (int)p->zip->getMode());
+ return false;
+ }
+ if(!p->zip->hasCurrentFile()) {
+ qWarning("QuaZipFile::open(): zip does not have current file");
+ return false;
+ }
+ }
+ p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password));
+ if(p->zipError==UNZ_OK) {
+ setOpenMode(mode);
+ p->raw=raw;
+ return true;
+ } else
+ return false;
+ }
+ qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
+ return false;
+}
+
+bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info,
+ const char *password, quint32 crc,
+ int method, int level, bool raw,
+ int windowBits, int memLevel, int strategy)
+{
+ zip_fileinfo info_z;
+ p->resetZipError();
+ if(isOpen()) {
+ qWarning("QuaZipFile::open(): already opened");
+ return false;
+ }
+ if((mode&WriteOnly)&&!(mode&ReadOnly)) {
+ if(p->internal) {
+ qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach");
+ return false;
+ }
+ if(p->zip==NULL) {
+ qWarning("QuaZipFile::open(): zip is NULL");
+ return false;
+ }
+ if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) {
+ qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
+ (int)mode, (int)p->zip->getMode());
+ return false;
+ }
+ info_z.tmz_date.tm_year=info.dateTime.date().year();
+ info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1;
+ info_z.tmz_date.tm_mday=info.dateTime.date().day();
+ info_z.tmz_date.tm_hour=info.dateTime.time().hour();
+ info_z.tmz_date.tm_min=info.dateTime.time().minute();
+ info_z.tmz_date.tm_sec=info.dateTime.time().second();
+ info_z.dosDate = 0;
+ info_z.internal_fa=(uLong)info.internalAttr;
+ info_z.external_fa=(uLong)info.externalAttr;
+ if (!p->zip->isDataDescriptorWritingEnabled())
+ zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
+ p->setZipError(zipOpenNewFileInZip3(p->zip->getZipFile(),
+ p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z,
+ info.extraLocal.constData(), info.extraLocal.length(),
+ info.extraGlobal.constData(), info.extraGlobal.length(),
+ p->zip->getCommentCodec()->fromUnicode(info.comment).constData(),
+ method, level, (int)raw,
+ windowBits, memLevel, strategy,
+ password, (uLong)crc));
+ if(p->zipError==UNZ_OK) {
+ p->writePos=0;
+ setOpenMode(mode);
+ p->raw=raw;
+ if(raw) {
+ p->crc=crc;
+ p->uncompressedSize=info.uncompressedSize;
+ }
+ return true;
+ } else
+ return false;
+ }
+ qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
+ return false;
+}
+
+bool QuaZipFile::isSequential()const
+{
+ return true;
+}
+
+qint64 QuaZipFile::pos()const
+{
+ if(p->zip==NULL) {
+ qWarning("QuaZipFile::pos(): call setZipName() or setZip() first");
+ return -1;
+ }
+ if(!isOpen()) {
+ qWarning("QuaZipFile::pos(): file is not open");
+ return -1;
+ }
+ if(openMode()&ReadOnly)
+ // QIODevice::pos() is broken for sequential devices,
+ // but thankfully bytesAvailable() returns the number of
+ // bytes buffered, so we know how far ahead we are.
+ return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable();
+ else
+ return p->writePos;
+}
+
+bool QuaZipFile::atEnd()const
+{
+ if(p->zip==NULL) {
+ qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first");
+ return false;
+ }
+ if(!isOpen()) {
+ qWarning("QuaZipFile::atEnd(): file is not open");
+ return false;
+ }
+ if(openMode()&ReadOnly)
+ // the same problem as with pos()
+ return QIODevice::bytesAvailable() == 0
+ && unzeof(p->zip->getUnzFile())==1;
+ else
+ return true;
+}
+
+qint64 QuaZipFile::size()const
+{
+ if(!isOpen()) {
+ qWarning("QuaZipFile::atEnd(): file is not open");
+ return -1;
+ }
+ if(openMode()&ReadOnly)
+ return p->raw?csize():usize();
+ else
+ return p->writePos;
+}
+
+qint64 QuaZipFile::csize()const
+{
+ unz_file_info info_z;
+ p->setZipError(UNZ_OK);
+ if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
+ p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
+ if(p->zipError!=UNZ_OK)
+ return -1;
+ return info_z.compressed_size;
+}
+
+qint64 QuaZipFile::usize()const
+{
+ unz_file_info info_z;
+ p->setZipError(UNZ_OK);
+ if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
+ p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
+ if(p->zipError!=UNZ_OK)
+ return -1;
+ return info_z.uncompressed_size;
+}
+
+bool QuaZipFile::getFileInfo(QuaZipFileInfo *info)
+{
+ if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false;
+ p->zip->getCurrentFileInfo(info);
+ p->setZipError(p->zip->getZipError());
+ return p->zipError==UNZ_OK;
+}
+
+void QuaZipFile::close()
+{
+ p->resetZipError();
+ if(p->zip==NULL||!p->zip->isOpen()) return;
+ if(!isOpen()) {
+ qWarning("QuaZipFile::close(): file isn't open");
+ return;
+ }
+ if(openMode()&ReadOnly)
+ p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile()));
+ else if(openMode()&WriteOnly)
+ if(isRaw()) p->setZipError(zipCloseFileInZipRaw(p->zip->getZipFile(), p->uncompressedSize, p->crc));
+ else p->setZipError(zipCloseFileInZip(p->zip->getZipFile()));
+ else {
+ qWarning("Wrong open mode: %d", (int)openMode());
+ return;
+ }
+ if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen);
+ else return;
+ if(p->internal) {
+ p->zip->close();
+ p->setZipError(p->zip->getZipError());
+ }
+}
+
+qint64 QuaZipFile::readData(char *data, qint64 maxSize)
+{
+ p->setZipError(UNZ_OK);
+ qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize);
+ if (bytesRead < 0) {
+ p->setZipError((int) bytesRead);
+ return -1;
+ }
+ return bytesRead;
+}
+
+qint64 QuaZipFile::writeData(const char* data, qint64 maxSize)
+{
+ p->setZipError(ZIP_OK);
+ p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize));
+ if(p->zipError!=ZIP_OK) return -1;
+ else {
+ p->writePos+=maxSize;
+ return maxSize;
+ }
+}
+
+QString QuaZipFile::getFileName() const
+{
+ return p->fileName;
+}
+
+QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const
+{
+ return p->caseSensitivity;
+}
+
+bool QuaZipFile::isRaw() const
+{
+ return p->raw;
+}
+
+int QuaZipFile::getZipError() const
+{
+ return p->zipError;
+}
+
+qint64 QuaZipFile::bytesAvailable() const
+{
+ return size() - pos();
+}
diff --git a/depends/quazip/quazipfile.h b/depends/quazip/quazipfile.h
new file mode 100644
index 00000000..f6cc41a6
--- /dev/null
+++ b/depends/quazip/quazipfile.h
@@ -0,0 +1,442 @@
+#ifndef QUA_ZIPFILE_H
+#define QUA_ZIPFILE_H
+
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ **/
+
+#include <QIODevice>
+
+#include "quazip_global.h"
+#include "quazip.h"
+#include "quazipnewinfo.h"
+
+class QuaZipFilePrivate;
+
+/// A file inside ZIP archive.
+/** \class QuaZipFile quazipfile.h <quazip/quazipfile.h>
+ * This is the most interesting class. Not only it provides C++
+ * interface to the ZIP/UNZIP package, but also integrates it with Qt by
+ * subclassing QIODevice. This makes possible to access files inside ZIP
+ * archive using QTextStream or QDataStream, for example. Actually, this
+ * is the main purpose of the whole QuaZIP library.
+ *
+ * You can either use existing QuaZip instance to create instance of
+ * this class or pass ZIP archive file name to this class, in which case
+ * it will create internal QuaZip object. See constructors' descriptions
+ * for details. Writing is only possible with the existing instance.
+ *
+ * Note that due to the underlying library's limitation it is not
+ * possible to use multiple QuaZipFile instances to open several files
+ * in the same archive at the same time. If you need to write to
+ * multiple files in parallel, then you should write to temporary files
+ * first, then pack them all at once when you have finished writing. If
+ * you need to read multiple files inside the same archive in parallel,
+ * you should extract them all into a temporary directory first.
+ *
+ * \section quazipfile-sequential Sequential or random-access?
+ *
+ * At the first thought, QuaZipFile has fixed size, the start and the
+ * end and should be therefore considered random-access device. But
+ * there is one major obstacle to making it random-access: ZIP/UNZIP API
+ * does not support seek() operation and the only way to implement it is
+ * through reopening the file and re-reading to the required position,
+ * but this is prohibitively slow.
+ *
+ * Therefore, QuaZipFile is considered to be a sequential device. This
+ * has advantage of availability of the ungetChar() operation (QIODevice
+ * does not implement it properly for non-sequential devices unless they
+ * support seek()). Disadvantage is a somewhat strange behaviour of the
+ * size() and pos() functions. This should be kept in mind while using
+ * this class.
+ *
+ **/
+class QUAZIP_EXPORT QuaZipFile: public QIODevice {
+ friend class QuaZipFilePrivate;
+ Q_OBJECT
+ private:
+ QuaZipFilePrivate *p;
+ // these are not supported nor implemented
+ QuaZipFile(const QuaZipFile& that);
+ QuaZipFile& operator=(const QuaZipFile& that);
+ protected:
+ /// Implementation of the QIODevice::readData().
+ qint64 readData(char *data, qint64 maxSize);
+ /// Implementation of the QIODevice::writeData().
+ qint64 writeData(const char *data, qint64 maxSize);
+ public:
+ /// Constructs a QuaZipFile instance.
+ /** You should use setZipName() and setFileName() or setZip() before
+ * trying to call open() on the constructed object.
+ **/
+ QuaZipFile();
+ /// Constructs a QuaZipFile instance.
+ /** \a parent argument specifies this object's parent object.
+ *
+ * You should use setZipName() and setFileName() or setZip() before
+ * trying to call open() on the constructed object.
+ **/
+ QuaZipFile(QObject *parent);
+ /// Constructs a QuaZipFile instance.
+ /** \a parent argument specifies this object's parent object and \a
+ * zipName specifies ZIP archive file name.
+ *
+ * You should use setFileName() before trying to call open() on the
+ * constructed object.
+ *
+ * QuaZipFile constructed by this constructor can be used for read
+ * only access. Use QuaZipFile(QuaZip*,QObject*) for writing.
+ **/
+ QuaZipFile(const QString& zipName, QObject *parent =NULL);
+ /// Constructs a QuaZipFile instance.
+ /** \a parent argument specifies this object's parent object, \a
+ * zipName specifies ZIP archive file name and \a fileName and \a cs
+ * specify a name of the file to open inside archive.
+ *
+ * QuaZipFile constructed by this constructor can be used for read
+ * only access. Use QuaZipFile(QuaZip*,QObject*) for writing.
+ *
+ * \sa QuaZip::setCurrentFile()
+ **/
+ QuaZipFile(const QString& zipName, const QString& fileName,
+ QuaZip::CaseSensitivity cs =QuaZip::csDefault, QObject *parent =NULL);
+ /// Constructs a QuaZipFile instance.
+ /** \a parent argument specifies this object's parent object.
+ *
+ * \a zip is the pointer to the existing QuaZip object. This
+ * QuaZipFile object then can be used to read current file in the
+ * \a zip or to write to the file inside it.
+ *
+ * \warning Using this constructor for reading current file can be
+ * tricky. Let's take the following example:
+ * \code
+ * QuaZip zip("archive.zip");
+ * zip.open(QuaZip::mdUnzip);
+ * zip.setCurrentFile("file-in-archive");
+ * QuaZipFile file(&zip);
+ * file.open(QIODevice::ReadOnly);
+ * // ok, now we can read from the file
+ * file.read(somewhere, some);
+ * zip.setCurrentFile("another-file-in-archive"); // oops...
+ * QuaZipFile anotherFile(&zip);
+ * anotherFile.open(QIODevice::ReadOnly);
+ * anotherFile.read(somewhere, some); // this is still ok...
+ * file.read(somewhere, some); // and this is NOT
+ * \endcode
+ * So, what exactly happens here? When we change current file in the
+ * \c zip archive, \c file that references it becomes invalid
+ * (actually, as far as I understand ZIP/UNZIP sources, it becomes
+ * closed, but QuaZipFile has no means to detect it).
+ *
+ * Summary: do not close \c zip object or change its current file as
+ * long as QuaZipFile is open. Even better - use another constructors
+ * which create internal QuaZip instances, one per object, and
+ * therefore do not cause unnecessary trouble. This constructor may
+ * be useful, though, if you already have a QuaZip instance and do
+ * not want to access several files at once. Good example:
+ * \code
+ * QuaZip zip("archive.zip");
+ * zip.open(QuaZip::mdUnzip);
+ * // first, we need some information about archive itself
+ * QByteArray comment=zip.getComment();
+ * // and now we are going to access files inside it
+ * QuaZipFile file(&zip);
+ * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) {
+ * file.open(QIODevice::ReadOnly);
+ * // do something cool with file here
+ * file.close(); // do not forget to close!
+ * }
+ * zip.close();
+ * \endcode
+ **/
+ QuaZipFile(QuaZip *zip, QObject *parent =NULL);
+ /// Destroys a QuaZipFile instance.
+ /** Closes file if open, destructs internal QuaZip object (if it
+ * exists and \em is internal, of course).
+ **/
+ virtual ~QuaZipFile();
+ /// Returns the ZIP archive file name.
+ /** If this object was created by passing QuaZip pointer to the
+ * constructor, this function will return that QuaZip's file name
+ * (or null string if that object does not have file name yet).
+ *
+ * Otherwise, returns associated ZIP archive file name or null
+ * string if there are no name set yet.
+ *
+ * \sa setZipName() getFileName()
+ **/
+ QString getZipName()const;
+ /// Returns a pointer to the associated QuaZip object.
+ /** Returns \c NULL if there is no associated QuaZip or it is
+ * internal (so you will not mess with it).
+ **/
+ QuaZip* getZip()const;
+ /// Returns file name.
+ /** This function returns file name you passed to this object either
+ * by using
+ * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*)
+ * or by calling setFileName(). Real name of the file may differ in
+ * case if you used case-insensitivity.
+ *
+ * Returns null string if there is no file name set yet. This is the
+ * case when this QuaZipFile operates on the existing QuaZip object
+ * (constructor QuaZipFile(QuaZip*,QObject*) or setZip() was used).
+ *
+ * \sa getActualFileName
+ **/
+ QString getFileName() const;
+ /// Returns case sensitivity of the file name.
+ /** This function returns case sensitivity argument you passed to
+ * this object either by using
+ * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*)
+ * or by calling setFileName().
+ *
+ * Returns unpredictable value if getFileName() returns null string
+ * (this is the case when you did not used setFileName() or
+ * constructor above).
+ *
+ * \sa getFileName
+ **/
+ QuaZip::CaseSensitivity getCaseSensitivity() const;
+ /// Returns the actual file name in the archive.
+ /** This is \em not a ZIP archive file name, but a name of file inside
+ * archive. It is not necessary the same name that you have passed
+ * to the
+ * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*),
+ * setFileName() or QuaZip::setCurrentFile() - this is the real file
+ * name inside archive, so it may differ in case if the file name
+ * search was case-insensitive.
+ *
+ * Equivalent to calling getCurrentFileName() on the associated
+ * QuaZip object. Returns null string if there is no associated
+ * QuaZip object or if it does not have a current file yet. And this
+ * is the case if you called setFileName() but did not open the
+ * file yet. So this is perfectly fine:
+ * \code
+ * QuaZipFile file("somezip.zip");
+ * file.setFileName("somefile");
+ * QString name=file.getName(); // name=="somefile"
+ * QString actual=file.getActualFileName(); // actual is null string
+ * file.open(QIODevice::ReadOnly);
+ * QString actual=file.getActualFileName(); // actual can be "SoMeFiLe" on Windows
+ * \endcode
+ *
+ * \sa getZipName(), getFileName(), QuaZip::CaseSensitivity
+ **/
+ QString getActualFileName()const;
+ /// Sets the ZIP archive file name.
+ /** Automatically creates internal QuaZip object and destroys
+ * previously created internal QuaZip object, if any.
+ *
+ * Will do nothing if this file is already open. You must close() it
+ * first.
+ **/
+ void setZipName(const QString& zipName);
+ /// Returns \c true if the file was opened in raw mode.
+ /** If the file is not open, the returned value is undefined.
+ *
+ * \sa open(OpenMode,int*,int*,bool,const char*)
+ **/
+ bool isRaw() const;
+ /// Binds to the existing QuaZip instance.
+ /** This function destroys internal QuaZip object, if any, and makes
+ * this QuaZipFile to use current file in the \a zip object for any
+ * further operations. See QuaZipFile(QuaZip*,QObject*) for the
+ * possible pitfalls.
+ *
+ * Will do nothing if the file is currently open. You must close()
+ * it first.
+ **/
+ void setZip(QuaZip *zip);
+ /// Sets the file name.
+ /** Will do nothing if at least one of the following conditions is
+ * met:
+ * - ZIP name has not been set yet (getZipName() returns null
+ * string).
+ * - This QuaZipFile is associated with external QuaZip. In this
+ * case you should call that QuaZip's setCurrentFile() function
+ * instead!
+ * - File is already open so setting the name is meaningless.
+ *
+ * \sa QuaZip::setCurrentFile
+ **/
+ void setFileName(const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault);
+ /// Opens a file for reading.
+ /** Returns \c true on success, \c false otherwise.
+ * Call getZipError() to get error code.
+ *
+ * \note Since ZIP/UNZIP API provides buffered reading only,
+ * QuaZipFile does not support unbuffered reading. So do not pass
+ * QIODevice::Unbuffered flag in \a mode, or open will fail.
+ **/
+ virtual bool open(OpenMode mode);
+ /// Opens a file for reading.
+ /** \overload
+ * Argument \a password specifies a password to decrypt the file. If
+ * it is NULL then this function behaves just like open(OpenMode).
+ **/
+ inline bool open(OpenMode mode, const char *password)
+ {return open(mode, NULL, NULL, false, password);}
+ /// Opens a file for reading.
+ /** \overload
+ * Argument \a password specifies a password to decrypt the file.
+ *
+ * An integers pointed by \a method and \a level will receive codes
+ * of the compression method and level used. See unzip.h.
+ *
+ * If raw is \c true then no decompression is performed.
+ *
+ * \a method should not be \c NULL. \a level can be \c NULL if you
+ * don't want to know the compression level.
+ **/
+ bool open(OpenMode mode, int *method, int *level, bool raw, const char *password =NULL);
+ /// Opens a file for writing.
+ /** \a info argument specifies information about file. It should at
+ * least specify a correct file name. Also, it is a good idea to
+ * specify correct timestamp (by default, current time will be
+ * used). See QuaZipNewInfo.
+ *
+ * The \a password argument specifies the password for crypting. Pass NULL
+ * if you don't need any crypting. The \a crc argument was supposed
+ * to be used for crypting too, but then it turned out that it's
+ * false information, so you need to set it to 0 unless you want to
+ * use the raw mode (see below).
+ *
+ * Arguments \a method and \a level specify compression method and
+ * level. The only method supported is Z_DEFLATED, but you may also
+ * specify 0 for no compression. If all of the files in the archive
+ * use both method 0 and either level 0 is explicitly specified or
+ * data descriptor writing is disabled with
+ * QuaZip::setDataDescriptorWritingEnabled(), then the
+ * resulting archive is supposed to be compatible with the 1.0 ZIP
+ * format version, should you need that. Except for this, \a level
+ * has no other effects with method 0.
+ *
+ * If \a raw is \c true, no compression is performed. In this case,
+ * \a crc and uncompressedSize field of the \a info are required.
+ *
+ * Arguments \a windowBits, \a memLevel, \a strategy provide zlib
+ * algorithms tuning. See deflateInit2() in zlib.
+ **/
+ bool open(OpenMode mode, const QuaZipNewInfo& info,
+ const char *password =NULL, quint32 crc =0,
+ int method =Z_DEFLATED, int level =Z_DEFAULT_COMPRESSION, bool raw =false,
+ int windowBits =-MAX_WBITS, int memLevel =DEF_MEM_LEVEL, int strategy =Z_DEFAULT_STRATEGY);
+ /// Returns \c true, but \ref quazipfile-sequential "beware"!
+ virtual bool isSequential()const;
+ /// Returns current position in the file.
+ /** Implementation of the QIODevice::pos(). When reading, this
+ * function is a wrapper to the ZIP/UNZIP unztell(), therefore it is
+ * unable to keep track of the ungetChar() calls (which is
+ * non-virtual and therefore is dangerous to reimplement). So if you
+ * are using ungetChar() feature of the QIODevice, this function
+ * reports incorrect value until you get back characters which you
+ * ungot.
+ *
+ * When writing, pos() returns number of bytes already written
+ * (uncompressed unless you use raw mode).
+ *
+ * \note Although
+ * \ref quazipfile-sequential "QuaZipFile is a sequential device"
+ * and therefore pos() should always return zero, it does not,
+ * because it would be misguiding. Keep this in mind.
+ *
+ * This function returns -1 if the file or archive is not open.
+ *
+ * Error code returned by getZipError() is not affected by this
+ * function call.
+ **/
+ virtual qint64 pos()const;
+ /// Returns \c true if the end of file was reached.
+ /** This function returns \c false in the case of error. This means
+ * that you called this function on either not open file, or a file
+ * in the not open archive or even on a QuaZipFile instance that
+ * does not even have QuaZip instance associated. Do not do that
+ * because there is no means to determine whether \c false is
+ * returned because of error or because end of file was reached.
+ * Well, on the other side you may interpret \c false return value
+ * as "there is no file open to check for end of file and there is
+ * no end of file therefore".
+ *
+ * When writing, this function always returns \c true (because you
+ * are always writing to the end of file).
+ *
+ * Error code returned by getZipError() is not affected by this
+ * function call.
+ **/
+ virtual bool atEnd()const;
+ /// Returns file size.
+ /** This function returns csize() if the file is open for reading in
+ * raw mode, usize() if it is open for reading in normal mode and
+ * pos() if it is open for writing.
+ *
+ * Returns -1 on error, call getZipError() to get error code.
+ *
+ * \note This function returns file size despite that
+ * \ref quazipfile-sequential "QuaZipFile is considered to be sequential device",
+ * for which size() should return bytesAvailable() instead. But its
+ * name would be very misguiding otherwise, so just keep in mind
+ * this inconsistence.
+ **/
+ virtual qint64 size()const;
+ /// Returns compressed file size.
+ /** Equivalent to calling getFileInfo() and then getting
+ * compressedSize field, but more convenient and faster.
+ *
+ * File must be open for reading before calling this function.
+ *
+ * Returns -1 on error, call getZipError() to get error code.
+ **/
+ qint64 csize()const;
+ /// Returns uncompressed file size.
+ /** Equivalent to calling getFileInfo() and then getting
+ * uncompressedSize field, but more convenient and faster. See
+ * getFileInfo() for a warning.
+ *
+ * File must be open for reading before calling this function.
+ *
+ * Returns -1 on error, call getZipError() to get error code.
+ **/
+ qint64 usize()const;
+ /// Gets information about current file.
+ /** This function does the same thing as calling
+ * QuaZip::getCurrentFileInfo() on the associated QuaZip object,
+ * but you can not call getCurrentFileInfo() if the associated
+ * QuaZip is internal (because you do not have access to it), while
+ * you still can call this function in that case.
+ *
+ * File must be open for reading before calling this function.
+ *
+ * Returns \c false in the case of an error.
+ **/
+ bool getFileInfo(QuaZipFileInfo *info);
+ /// Closes the file.
+ /** Call getZipError() to determine if the close was successful.
+ **/
+ virtual void close();
+ /// Returns the error code returned by the last ZIP/UNZIP API call.
+ int getZipError() const;
+ /// Returns the number of bytes available for reading.
+ virtual qint64 bytesAvailable() const;
+};
+
+#endif
diff --git a/depends/quazip/quazipfileinfo.h b/depends/quazip/quazipfileinfo.h
new file mode 100644
index 00000000..99540229
--- /dev/null
+++ b/depends/quazip/quazipfileinfo.h
@@ -0,0 +1,66 @@
+#ifndef QUA_ZIPFILEINFO_H
+#define QUA_ZIPFILEINFO_H
+
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ **/
+
+#include <QByteArray>
+#include <QDateTime>
+
+#include "quazip_global.h"
+
+/// Information about a file inside archive.
+/** Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to
+ * fill this structure. */
+struct QUAZIP_EXPORT QuaZipFileInfo {
+ /// File name.
+ QString name;
+ /// Version created by.
+ quint16 versionCreated;
+ /// Version needed to extract.
+ quint16 versionNeeded;
+ /// General purpose flags.
+ quint16 flags;
+ /// Compression method.
+ quint16 method;
+ /// Last modification date and time.
+ QDateTime dateTime;
+ /// CRC.
+ quint32 crc;
+ /// Compressed file size.
+ quint32 compressedSize;
+ /// Uncompressed file size.
+ quint32 uncompressedSize;
+ /// Disk number start.
+ quint16 diskNumberStart;
+ /// Internal file attributes.
+ quint16 internalAttr;
+ /// External file attributes.
+ quint32 externalAttr;
+ /// Comment.
+ QString comment;
+ /// Extra field.
+ QByteArray extra;
+};
+
+#endif
diff --git a/depends/quazip/quazipnewinfo.cpp b/depends/quazip/quazipnewinfo.cpp
new file mode 100644
index 00000000..ed57e09f
--- /dev/null
+++ b/depends/quazip/quazipnewinfo.cpp
@@ -0,0 +1,51 @@
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+*/
+
+#include <QFileInfo>
+
+#include "quazipnewinfo.h"
+
+
+QuaZipNewInfo::QuaZipNewInfo(const QString& name):
+ name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0)
+{
+}
+
+QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file):
+ name(name), internalAttr(0), externalAttr(0)
+{
+ QFileInfo info(file);
+ QDateTime lm = info.lastModified();
+ if (!info.exists())
+ dateTime = QDateTime::currentDateTime();
+ else
+ dateTime = lm;
+}
+
+void QuaZipNewInfo::setFileDateTime(const QString& file)
+{
+ QFileInfo info(file);
+ QDateTime lm = info.lastModified();
+ if (info.exists())
+ dateTime = lm;
+}
diff --git a/depends/quazip/quazipnewinfo.h b/depends/quazip/quazipnewinfo.h
new file mode 100644
index 00000000..62159ea7
--- /dev/null
+++ b/depends/quazip/quazipnewinfo.h
@@ -0,0 +1,102 @@
+#ifndef QUA_ZIPNEWINFO_H
+#define QUA_ZIPNEWINFO_H
+
+/*
+Copyright (C) 2005-2011 Sergey A. Tachenov
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+See COPYING file for the full LGPL text.
+
+Original ZIP package is copyrighted by Gilles Vollant, see
+quazip/(un)zip.h files for details, basically it's zlib license.
+ **/
+
+#include <QDateTime>
+#include <QString>
+
+#include "quazip_global.h"
+
+/// Information about a file to be created.
+/** This structure holds information about a file to be created inside
+ * ZIP archive. At least name should be set to something correct before
+ * passing this structure to
+ * QuaZipFile::open(OpenMode,const QuaZipNewInfo&,int,int,bool).
+ **/
+struct QUAZIP_EXPORT QuaZipNewInfo {
+ /// File name.
+ /** This field holds file name inside archive, including path relative
+ * to archive root.
+ **/
+ QString name;
+ /// File timestamp.
+ /** This is the last file modification date and time. Will be stored
+ * in the archive central directory. It is a good practice to set it
+ * to the source file timestamp instead of archive creating time. Use
+ * setFileDateTime() or QuaZipNewInfo(const QString&, const QString&).
+ **/
+ QDateTime dateTime;
+ /// File internal attributes.
+ quint16 internalAttr;
+ /// File external attributes.
+ quint32 externalAttr;
+ /// File comment.
+ /** Will be encoded using QuaZip::getCommentCodec().
+ **/
+ QString comment;
+ /// File local extra field.
+ QByteArray extraLocal;
+ /// File global extra field.
+ QByteArray extraGlobal;
+ /// Uncompressed file size.
+ /** This is only needed if you are using raw file zipping mode, i. e.
+ * adding precompressed file in the zip archive.
+ **/
+ ulong uncompressedSize;
+ /// Constructs QuaZipNewInfo instance.
+ /** Initializes name with \a name, dateTime with current date and
+ * time. Attributes are initialized with zeros, comment and extra
+ * field with null values.
+ **/
+ QuaZipNewInfo(const QString& name);
+ /// Constructs QuaZipNewInfo instance.
+ /** Initializes name with \a name and dateTime with timestamp of the
+ * file named \a file. If the \a file does not exists or its timestamp
+ * is inaccessible (e. g. you do not have read permission for the
+ * directory file in), uses current date and time. Attributes are
+ * initialized with zeros, comment and extra field with null values.
+ *
+ * \sa setFileDateTime()
+ **/
+ QuaZipNewInfo(const QString& name, const QString& file);
+ /// Sets the file timestamp from the existing file.
+ /** Use this function to set the file timestamp from the existing
+ * file. Use it like this:
+ * \code
+ * QuaZipFile zipFile(&zip);
+ * QFile file("file-to-add");
+ * file.open(QIODevice::ReadOnly);
+ * QuaZipNewInfo info("file-name-in-archive");
+ * info.setFileDateTime("file-to-add"); // take the timestamp from file
+ * zipFile.open(QIODevice::WriteOnly, info);
+ * \endcode
+ *
+ * This function does not change dateTime if some error occured (e. g.
+ * file is inaccessible).
+ **/
+ void setFileDateTime(const QString& file);
+};
+
+#endif
diff --git a/depends/quazip/unzip.c b/depends/quazip/unzip.c
new file mode 100644
index 00000000..52bc081f
--- /dev/null
+++ b/depends/quazip/unzip.c
@@ -0,0 +1,1603 @@
+/* unzip.c -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read unzip.h for more info
+
+ Modified by Sergey A. Tachenov to integrate with Qt.
+*/
+
+/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+compatibility with older software. The following is from the original crypt.c. Code
+woven in by Terry Thorsen 1/2003.
+*/
+/*
+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2000-Apr-09 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, all these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h]
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+ */
+
+/*
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+#include "unzip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+
+#ifndef CASESENSITIVITYDEFAULT_NO
+# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+# define CASESENSITIVITYDEFAULT_NO
+# endif
+#endif
+
+
+#ifndef UNZ_BUFSIZE
+#define UNZ_BUFSIZE (16384)
+#endif
+
+#ifndef UNZ_MAXFILENAMEINZIP
+#define UNZ_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+
+
+
+
+const char unz_copyright[] =
+ " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+/* unz_file_info_interntal contain internal info about a file in zipfile*/
+typedef struct unz_file_info_internal_s
+{
+ uLong offset_curfile;/* relative offset of local header 4 bytes */
+} unz_file_info_internal;
+
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile,
+ when reading and decompress it */
+typedef struct
+{
+ char *read_buffer; /* internal buffer for compressed data */
+ z_stream stream; /* zLib stream structure for inflate */
+
+ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
+ uLong stream_initialised; /* flag set if stream structure is initialised*/
+
+ uLong offset_local_extrafield;/* offset of the local extra field */
+ uInt size_local_extrafield;/* size of the local extra field */
+ uLong pos_local_extrafield; /* position in the local extra field in read*/
+
+ uLong crc32; /* crc32 of all data uncompressed */
+ uLong crc32_wait; /* crc32 we must obtain after decompress all */
+ uLong rest_read_compressed; /* number of byte to be decompressed */
+ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ uLong compression_method; /* compression method (0==store) */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ int raw;
+} file_in_zip_read_info_s;
+
+
+/* unz_s contain internal information about the zipfile
+*/
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ unz_global_info gi; /* public global information */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ uLong num_file; /* number of the current file in the zipfile*/
+ uLong pos_in_central_dir; /* pos of the current file in the central dir*/
+ uLong current_file_ok; /* flag about the usability of the current file*/
+ uLong central_pos; /* position of the beginning of the central dir*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory with
+ respect to the starting disk number */
+
+ unz_file_info cur_file_info; /* public info about the current file in zip*/
+ unz_file_info_internal cur_file_info_internal; /* private info about it*/
+ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
+ file if we are decompressing it */
+ int encrypted;
+# ifndef NOUNCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+# endif
+} unz_s;
+
+
+#ifndef NOUNCRYPT
+#include "crypt.h"
+#endif
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+
+
+local int unzlocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return UNZ_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return UNZ_ERRNO;
+ else
+ return UNZ_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int unzlocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int unzlocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+
+/* My own strcmpi / strcasecmp */
+local int strcmpcasenosensitive_internal (fileName1,fileName2)
+ const char* fileName1;
+ const char* fileName2;
+{
+ for (;;)
+ {
+ char c1=*(fileName1++);
+ char c2=*(fileName2++);
+ if ((c1>='a') && (c1<='z'))
+ c1 -= 0x20;
+ if ((c2>='a') && (c2<='z'))
+ c2 -= 0x20;
+ if (c1=='\0')
+ return ((c2=='\0') ? 0 : -1);
+ if (c2=='\0')
+ return 1;
+ if (c1<c2)
+ return -1;
+ if (c1>c2)
+ return 1;
+ }
+}
+
+
+#ifdef CASESENSITIVITYDEFAULT_NO
+#define CASESENSITIVITYDEFAULTVALUE 2
+#else
+#define CASESENSITIVITYDEFAULTVALUE 1
+#endif
+
+#ifndef STRCMPCASENOSENTIVEFUNCTION
+#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
+#endif
+
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+
+*/
+extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
+ const char* fileName1;
+ const char* fileName2;
+ int iCaseSensitivity;
+{
+ if (iCaseSensitivity==0)
+ iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
+
+ if (iCaseSensitivity==1)
+ return strcmp(fileName1,fileName2);
+
+ return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong unzlocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
+ "zlib/zlib114.zip".
+ If the zipfile cannot be opened (file doesn't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+extern unzFile ZEXPORT unzOpen2 (file, pzlib_filefunc_def)
+ voidpf file;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ unz_s us;
+ unz_s *s;
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+
+ int err=UNZ_OK;
+
+ if (unz_copyright[0]!=' ')
+ return NULL;
+
+ if (pzlib_filefunc_def==NULL)
+ fill_qiodevice_filefunc(&us.z_filefunc);
+ else
+ us.z_filefunc = *pzlib_filefunc_def;
+
+ us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,
+ file,
+ ZLIB_FILEFUNC_MODE_READ |
+ ZLIB_FILEFUNC_MODE_EXISTING);
+ if (us.filestream==NULL)
+ return NULL;
+
+ central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream);
+ if (central_pos==0)
+ err=UNZ_ERRNO;
+
+ if (ZSEEK(us.z_filefunc, us.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+ /* the signature, already checked */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((number_entry_CD!=us.gi.number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=UNZ_BADZIPFILE;
+
+ /* size of the central directory */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* zipfile comment length */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
+ (err==UNZ_OK))
+ err=UNZ_BADZIPFILE;
+
+ if (err!=UNZ_OK)
+ {
+ ZCLOSE(us.z_filefunc, us.filestream);
+ return NULL;
+ }
+
+ us.byte_before_the_zipfile = central_pos -
+ (us.offset_central_dir+us.size_central_dir);
+ us.central_pos = central_pos;
+ us.pfile_in_zip_read = NULL;
+ us.encrypted = 0;
+
+
+ s=(unz_s*)ALLOC(sizeof(unz_s));
+ *s=us;
+ unzGoToFirstFile((unzFile)s);
+ return (unzFile)s;
+}
+
+
+extern unzFile ZEXPORT unzOpen (file)
+ voidpf file;
+{
+ return unzOpen2(file, NULL);
+}
+
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzClose (file)
+ unzFile file;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ if (s->pfile_in_zip_read!=NULL)
+ unzCloseCurrentFile(file);
+
+ ZCLOSE(s->z_filefunc, s->filestream);
+ TRYFREE(s);
+ return UNZ_OK;
+}
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
+ unzFile file;
+ unz_global_info *pglobal_info;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ *pglobal_info=s->gi;
+ return UNZ_OK;
+}
+
+
+/*
+ Translate date/time from Dos format to tm_unz (readable more easilty)
+*/
+local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
+ uLong ulDosDate;
+ tm_unz* ptm;
+{
+ uLong uDate;
+ uDate = (uLong)(ulDosDate>>16);
+ ptm->tm_mday = (uInt)(uDate&0x1f) ;
+ ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ;
+ ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
+
+ ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
+ ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ;
+ ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ;
+}
+
+/*
+ Get Info about the current file in the zipfile, with internal only info
+*/
+local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
+ unz_file_info *pfile_info,
+ unz_file_info_internal
+ *pfile_info_internal,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+
+local int unzlocal_GetCurrentFileInfoInternal (file,
+ pfile_info,
+ pfile_info_internal,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ unz_file_info_internal *pfile_info_internal;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ unz_s* s;
+ unz_file_info file_info;
+ unz_file_info_internal file_info_internal;
+ int err=UNZ_OK;
+ uLong uMagic;
+ uLong uSeek=0;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pos_in_central_dir+s->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+
+ /* we check the magic */
+ if (err==UNZ_OK) {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x02014b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ uSeek+=file_info.size_filename;
+ if ((err==UNZ_OK) && (szFileName!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_filename<fileNameBufferSize)
+ {
+ *(szFileName+file_info.size_filename)='\0';
+ uSizeRead = file_info.size_filename;
+ }
+ else
+ uSizeRead = fileNameBufferSize;
+
+ if ((file_info.size_filename>0) && (fileNameBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ uSeek -= uSizeRead;
+ }
+
+
+ if ((err==UNZ_OK) && (extraField!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_extra<extraFieldBufferSize)
+ uSizeRead = file_info.size_file_extra;
+ else
+ uSizeRead = extraFieldBufferSize;
+
+ if (uSeek!=0) {
+ if (ZSEEK(s->z_filefunc, s->filestream,uSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ uSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ uSeek += file_info.size_file_extra - uSizeRead;
+ }
+ else
+ uSeek+=file_info.size_file_extra;
+
+
+ if ((err==UNZ_OK) && (szComment!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_comment<commentBufferSize)
+ {
+ *(szComment+file_info.size_file_comment)='\0';
+ uSizeRead = file_info.size_file_comment;
+ }
+ else
+ uSizeRead = commentBufferSize;
+
+ if (uSeek!=0) {
+ if (ZSEEK(s->z_filefunc, s->filestream,uSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ uSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_comment>0) && (commentBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ uSeek+=file_info.size_file_comment - uSizeRead;
+ }
+ else
+ uSeek+=file_info.size_file_comment;
+
+ if ((err==UNZ_OK) && (pfile_info!=NULL))
+ *pfile_info=file_info;
+
+ if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
+ *pfile_info_internal=file_info_internal;
+
+ return err;
+}
+
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem.
+*/
+extern int ZEXPORT unzGetCurrentFileInfo (file,
+ pfile_info,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
+ szFileName,fileNameBufferSize,
+ extraField,extraFieldBufferSize,
+ szComment,commentBufferSize);
+}
+
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+extern int ZEXPORT unzGoToFirstFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ s->pos_in_central_dir=s->offset_central_dir;
+ s->num_file=0;
+ err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+extern int ZEXPORT unzGoToNextFile (file)
+ unzFile file;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */
+ if (s->num_file+1==s->gi.number_entry)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+ s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
+ s->num_file++;
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzipStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
+ unzFile file;
+ const char *szFileName;
+ int iCaseSensitivity;
+{
+ unz_s* s;
+ int err;
+
+ /* We remember the 'current' position in the file so that we can jump
+ * back there if we fail.
+ */
+ unz_file_info cur_file_infoSaved;
+ unz_file_info_internal cur_file_info_internalSaved;
+ uLong num_fileSaved;
+ uLong pos_in_central_dirSaved;
+
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+
+ if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
+ return UNZ_PARAMERROR;
+
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ /* Save the current state */
+ num_fileSaved = s->num_file;
+ pos_in_central_dirSaved = s->pos_in_central_dir;
+ cur_file_infoSaved = s->cur_file_info;
+ cur_file_info_internalSaved = s->cur_file_info_internal;
+
+ err = unzGoToFirstFile(file);
+
+ while (err == UNZ_OK)
+ {
+ char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
+ err = unzGetCurrentFileInfo(file,NULL,
+ szCurrentFileName,sizeof(szCurrentFileName)-1,
+ NULL,0,NULL,0);
+ if (err == UNZ_OK)
+ {
+ if (unzStringFileNameCompare(szCurrentFileName,
+ szFileName,iCaseSensitivity)==0)
+ return UNZ_OK;
+ err = unzGoToNextFile(file);
+ }
+ }
+
+ /* We failed, so restore the state of the 'current file' to where we
+ * were.
+ */
+ s->num_file = num_fileSaved ;
+ s->pos_in_central_dir = pos_in_central_dirSaved ;
+ s->cur_file_info = cur_file_infoSaved;
+ s->cur_file_info_internal = cur_file_info_internalSaved;
+ return err;
+}
+
+
+/*
+///////////////////////////////////////////
+// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+// I need random access
+//
+// Further optimization could be realized by adding an ability
+// to cache the directory in memory. The goal being a single
+// comprehensive file read to put the file I need in a memory.
+*/
+
+/*
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; // offset in file
+ uLong num_of_file; // # of file
+} unz_file_pos;
+*/
+
+extern int ZEXPORT unzGetFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ file_pos->pos_in_zip_directory = s->pos_in_central_dir;
+ file_pos->num_of_file = s->num_file;
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzGoToFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ /* jump to the right spot */
+ s->pos_in_central_dir = file_pos->pos_in_zip_directory;
+ s->num_file = file_pos->num_of_file;
+
+ /* set the current file */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ /* return results */
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+// Unzip Helper Functions - should be here?
+///////////////////////////////////////////
+*/
+
+/*
+ Read the local header of the current zipfile
+ Check the coherency of the local header and info in the end of central
+ directory about this file
+ store in *piSizeVar the size of extra info in local header
+ (filename and size of extra field data)
+*/
+local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
+ poffset_local_extrafield,
+ psize_local_extrafield)
+ unz_s* s;
+ uInt* piSizeVar;
+ uLong *poffset_local_extrafield;
+ uInt *psize_local_extrafield;
+{
+ uLong uMagic,uData,uFlags;
+ uLong size_filename;
+ uLong size_extra_field;
+ int err=UNZ_OK;
+
+ *piSizeVar = 0;
+ *poffset_local_extrafield = 0;
+ *psize_local_extrafield = 0;
+
+ if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile +
+ s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+
+ if (err==UNZ_OK) {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x04034b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+/*
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
+ err=UNZ_BADZIPFILE;
+*/
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
+ err=UNZ_BADZIPFILE;
+
+ if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
+ err=UNZ_BADZIPFILE;
+
+ *piSizeVar += (uInt)size_filename;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK)
+ err=UNZ_ERRNO;
+ *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
+ SIZEZIPLOCALHEADER + size_filename;
+ *psize_local_extrafield = (uInt)size_extra_field;
+
+ *piSizeVar += (uInt)size_extra_field;
+
+ return err;
+}
+
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+ const char* password;
+{
+ int err=UNZ_OK;
+ uInt iSizeVar;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uLong offset_local_extrafield; /* offset of the local extra field */
+ uInt size_local_extrafield; /* size of the local extra field */
+# ifndef NOUNCRYPT
+ char source[12];
+# else
+ if (password != NULL)
+ return UNZ_PARAMERROR;
+# endif
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_PARAMERROR;
+
+ if (s->pfile_in_zip_read != NULL)
+ unzCloseCurrentFile(file);
+
+ if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
+ &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
+ return UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info = (file_in_zip_read_info_s*)
+ ALLOC(sizeof(file_in_zip_read_info_s));
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_INTERNALERROR;
+
+ pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
+ pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+ pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+ pfile_in_zip_read_info->pos_local_extrafield=0;
+ pfile_in_zip_read_info->raw=raw;
+
+ if (pfile_in_zip_read_info->read_buffer==NULL)
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return UNZ_INTERNALERROR;
+ }
+
+ pfile_in_zip_read_info->stream_initialised=0;
+
+ if (method!=NULL)
+ *method = (int)s->cur_file_info.compression_method;
+
+ if (level!=NULL)
+ {
+ *level = 6;
+ switch (s->cur_file_info.flag & 0x06)
+ {
+ case 6 : *level = 1; break;
+ case 4 : *level = 2; break;
+ case 2 : *level = 9; break;
+ }
+ }
+
+ if ((s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
+ pfile_in_zip_read_info->crc32=0;
+ pfile_in_zip_read_info->compression_method =
+ s->cur_file_info.compression_method;
+ pfile_in_zip_read_info->filestream=s->filestream;
+ pfile_in_zip_read_info->z_filefunc=s->z_filefunc;
+ pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
+
+ pfile_in_zip_read_info->stream.total_out = 0;
+
+ if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
+ (!raw))
+ {
+ pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+ pfile_in_zip_read_info->stream.zfree = (free_func)0;
+ pfile_in_zip_read_info->stream.opaque = (voidpf)0;
+ pfile_in_zip_read_info->stream.next_in = (voidpf)0;
+ pfile_in_zip_read_info->stream.avail_in = 0;
+
+ err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+ if (err == Z_OK)
+ pfile_in_zip_read_info->stream_initialised=1;
+ else
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return err;
+ }
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END.
+ * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+ * size of both compressed and uncompressed data
+ */
+ }
+ pfile_in_zip_read_info->rest_read_compressed =
+ s->cur_file_info.compressed_size ;
+ pfile_in_zip_read_info->rest_read_uncompressed =
+ s->cur_file_info.uncompressed_size ;
+
+
+ pfile_in_zip_read_info->pos_in_zipfile =
+ s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+ iSizeVar;
+
+ pfile_in_zip_read_info->stream.avail_in = (uInt)0;
+
+ s->pfile_in_zip_read = pfile_in_zip_read_info;
+
+# ifndef NOUNCRYPT
+ if (password != NULL)
+ {
+ int i;
+ s->pcrc_32_tab = (const unsigned long*) get_crc_table();
+ init_keys(password,s->keys,s->pcrc_32_tab);
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pfile_in_zip_read->pos_in_zipfile +
+ s->pfile_in_zip_read->byte_before_the_zipfile,
+ SEEK_SET)!=0)
+ return UNZ_INTERNALERROR;
+ if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12)
+ return UNZ_INTERNALERROR;
+
+ for (i = 0; i<12; i++)
+ zdecode(s->keys,s->pcrc_32_tab,source[i]);
+
+ s->pfile_in_zip_read->pos_in_zipfile+=12;
+ s->encrypted=1;
+ }
+# endif
+
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzOpenCurrentFile (file)
+ unzFile file;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
+}
+
+extern int ZEXPORT unzOpenCurrentFilePassword (file, password)
+ unzFile file;
+ const char* password;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
+}
+
+extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+{
+ return unzOpenCurrentFile3(file, method, level, raw, NULL);
+}
+
+/*
+ Read bytes from the current file.
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+extern int ZEXPORT unzReadCurrentFile (file, buf, len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ int err=UNZ_OK;
+ uInt iRead = 0;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->read_buffer == NULL))
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (len==0)
+ return 0;
+
+ pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
+
+ pfile_in_zip_read_info->stream.avail_out = (uInt)len;
+
+ if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
+ (!(pfile_in_zip_read_info->raw)))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
+
+ if ((len>pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in) &&
+ (pfile_in_zip_read_info->raw))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in;
+
+ while (pfile_in_zip_read_info->stream.avail_out>0)
+ {
+ if ((pfile_in_zip_read_info->stream.avail_in==0) &&
+ (pfile_in_zip_read_info->rest_read_compressed>0))
+ {
+ uInt uReadThis = UNZ_BUFSIZE;
+ if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
+ uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
+ if (uReadThis == 0)
+ return UNZ_EOF;
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->pos_in_zipfile +
+ pfile_in_zip_read_info->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->read_buffer,
+ uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+
+
+# ifndef NOUNCRYPT
+ if(s->encrypted)
+ {
+ uInt i;
+ for(i=0;i<uReadThis;i++)
+ pfile_in_zip_read_info->read_buffer[i] =
+ zdecode(s->keys,s->pcrc_32_tab,
+ pfile_in_zip_read_info->read_buffer[i]);
+ }
+# endif
+
+
+ pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+
+ pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+
+ pfile_in_zip_read_info->stream.next_in =
+ (Bytef*)pfile_in_zip_read_info->read_buffer;
+ pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
+ }
+
+ if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw))
+ {
+ uInt uDoCopy,i ;
+
+ if ((pfile_in_zip_read_info->stream.avail_in == 0) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ return (iRead==0) ? UNZ_EOF : iRead;
+
+ if (pfile_in_zip_read_info->stream.avail_out <
+ pfile_in_zip_read_info->stream.avail_in)
+ uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
+ else
+ uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
+
+ for (i=0;i<uDoCopy;i++)
+ *(pfile_in_zip_read_info->stream.next_out+i) =
+ *(pfile_in_zip_read_info->stream.next_in+i);
+
+ pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
+ pfile_in_zip_read_info->stream.next_out,
+ uDoCopy);
+ pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
+ pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
+ pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
+ pfile_in_zip_read_info->stream.next_out += uDoCopy;
+ pfile_in_zip_read_info->stream.next_in += uDoCopy;
+ pfile_in_zip_read_info->stream.total_out += uDoCopy;
+ iRead += uDoCopy;
+ }
+ else
+ {
+ uLong uTotalOutBefore,uTotalOutAfter;
+ const Bytef *bufBefore;
+ uLong uOutThis;
+ int flush=Z_SYNC_FLUSH;
+
+ uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
+ bufBefore = pfile_in_zip_read_info->stream.next_out;
+
+ /*
+ if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+ pfile_in_zip_read_info->stream.avail_out) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ flush = Z_FINISH;
+ */
+ err=inflate(&pfile_in_zip_read_info->stream,flush);
+
+ if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL))
+ err = Z_DATA_ERROR;
+
+ uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
+ uOutThis = uTotalOutAfter-uTotalOutBefore;
+
+ pfile_in_zip_read_info->crc32 =
+ crc32(pfile_in_zip_read_info->crc32,bufBefore,
+ (uInt)(uOutThis));
+
+ pfile_in_zip_read_info->rest_read_uncompressed -=
+ uOutThis;
+
+ iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
+
+ if (err==Z_STREAM_END)
+ return (iRead==0) ? UNZ_EOF : iRead;
+ if (err!=Z_OK)
+ break;
+ }
+ }
+
+ if (err==Z_OK)
+ return iRead;
+ return err;
+}
+
+
+/*
+ Give the current position in uncompressed data
+*/
+extern z_off_t ZEXPORT unztell (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ return (z_off_t)pfile_in_zip_read_info->stream.total_out;
+}
+
+
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+extern int ZEXPORT unzeof (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field that can be read
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uInt read_now;
+ uLong size_to_read;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
+ pfile_in_zip_read_info->pos_local_extrafield);
+
+ if (buf==NULL)
+ return (int)size_to_read;
+
+ if (len>size_to_read)
+ read_now = (uInt)size_to_read;
+ else
+ read_now = (uInt)len ;
+
+ if (read_now==0)
+ return 0;
+
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->offset_local_extrafield +
+ pfile_in_zip_read_info->pos_local_extrafield,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ buf,read_now)!=read_now)
+ return UNZ_ERRNO;
+
+ return (int)read_now;
+}
+
+/*
+ Close the file in zip opened with unzipOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+extern int ZEXPORT unzCloseCurrentFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+ (!pfile_in_zip_read_info->raw))
+ {
+ if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
+ err=UNZ_CRCERROR;
+ }
+
+
+ TRYFREE(pfile_in_zip_read_info->read_buffer);
+ pfile_in_zip_read_info->read_buffer = NULL;
+ if (pfile_in_zip_read_info->stream_initialised)
+ inflateEnd(&pfile_in_zip_read_info->stream);
+
+ pfile_in_zip_read_info->stream_initialised = 0;
+ TRYFREE(pfile_in_zip_read_info);
+
+ s->pfile_in_zip_read=NULL;
+
+ return err;
+}
+
+
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
+ unzFile file;
+ char *szComment;
+ uLong uSizeBuf;
+{
+ unz_s* s;
+ uLong uReadThis ;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ uReadThis = uSizeBuf;
+ if (uReadThis>s->gi.size_comment)
+ uReadThis = s->gi.size_comment;
+
+ if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (uReadThis>0)
+ {
+ *szComment='\0';
+ if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+ }
+
+ if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
+ *(szComment+s->gi.size_comment)='\0';
+ return (int)uReadThis;
+}
+
+/* Additions by RX '2004 */
+extern uLong ZEXPORT unzGetOffset (file)
+ unzFile file;
+{
+ unz_s* s;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return 0;
+ if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff)
+ if (s->num_file==s->gi.number_entry)
+ return 0;
+ return s->pos_in_central_dir;
+}
+
+extern int ZEXPORT unzSetOffset (file, pos)
+ unzFile file;
+ uLong pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ s->pos_in_central_dir = pos;
+ s->num_file = s->gi.number_entry; /* hack */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
diff --git a/depends/quazip/unzip.h b/depends/quazip/unzip.h
new file mode 100644
index 00000000..33c9dc1a
--- /dev/null
+++ b/depends/quazip/unzip.h
@@ -0,0 +1,356 @@
+/* unzip.h -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Modified by Sergey A. Tachenov to integrate with Qt.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _unz_H
+#define _unz_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unzFile__;
+typedef unzFile__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+
+#define UNZ_OK (0)
+#define UNZ_END_OF_LIST_OF_FILE (-100)
+#define UNZ_ERRNO (Z_ERRNO)
+#define UNZ_EOF (0)
+#define UNZ_PARAMERROR (-102)
+#define UNZ_BADZIPFILE (-103)
+#define UNZ_INTERNALERROR (-104)
+#define UNZ_CRCERROR (-105)
+
+/* tm_unz contain date/time info */
+typedef struct tm_unz_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_unz;
+
+/* unz_global_info structure contain global data about the ZIPfile
+ These data comes from the end of central dir */
+typedef struct unz_global_info_s
+{
+ uLong number_entry; /* total number of entries in
+ the central dir on this disk */
+ uLong size_comment; /* size of the global comment of the zipfile */
+} unz_global_info;
+
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info_s
+{
+ uLong version; /* version made by 2 bytes */
+ uLong version_needed; /* version needed to extract 2 bytes */
+ uLong flag; /* general purpose bit flag 2 bytes */
+ uLong compression_method; /* compression method 2 bytes */
+ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
+ uLong crc; /* crc-32 4 bytes */
+ uLong compressed_size; /* compressed size 4 bytes */
+ uLong uncompressed_size; /* uncompressed size 4 bytes */
+ uLong size_filename; /* filename length 2 bytes */
+ uLong size_file_extra; /* extra field length 2 bytes */
+ uLong size_file_comment; /* file comment length 2 bytes */
+
+ uLong disk_num_start; /* disk number start 2 bytes */
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+
+ tm_unz tmu_date;
+} unz_file_info;
+
+extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
+ const char* fileName2,
+ int iCaseSensitivity));
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+*/
+
+
+extern unzFile ZEXPORT unzOpen OF((voidpf file));
+/*
+ Open a Zip file. path contain whatever zopen_file from the IO API
+ accepts. For Qt implementation it is a pointer to QIODevice, for
+ fopen() implementation it's a file name.
+ If the zipfile cannot be opened (file don't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+
+extern unzFile ZEXPORT unzOpen2 OF((voidpf file,
+ zlib_filefunc_def* pzlib_filefunc_def));
+/*
+ Open a Zip file, like unzOpen, but provide a set of file low level API
+ for read/write the zip file (see ioapi.h)
+*/
+
+extern int ZEXPORT unzClose OF((unzFile file));
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+
+extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
+ unz_global_info *pglobal_info));
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+
+
+extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+ char *szComment,
+ uLong uSizeBuf));
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+
+
+/***************************************************************************/
+/* Unzip package allow you browse the directory of the zipfile */
+
+extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+
+extern int ZEXPORT unzGoToNextFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+
+extern int ZEXPORT unzLocateFile OF((unzFile file,
+ const char *szFileName,
+ int iCaseSensitivity));
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+
+
+/* ****************************************** */
+/* Ryan supplied functions */
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; /* offset in zip file directory */
+ uLong num_of_file; /* # of file */
+} unz_file_pos;
+
+extern int ZEXPORT unzGetFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+extern int ZEXPORT unzGoToFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+/* ****************************************** */
+
+extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
+ unz_file_info *pfile_info,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+/*
+ Get Info about the current file
+ if pfile_info!=NULL, the *pfile_info structure will contain somes info about
+ the current file
+ if szFileName!=NULL, the filemane string will be copied in szFileName
+ (fileNameBufferSize is the size of the buffer)
+ if extraField!=NULL, the extra field information will be copied in extraField
+ (extraFieldBufferSize is the size of the buffer).
+ This is the Central-header version of the extra field
+ if szComment!=NULL, the comment string of the file will be copied in szComment
+ (commentBufferSize is the size of the buffer)
+*/
+
+/***************************************************************************/
+/* for reading the content of the current zipfile, you can open it, read data
+ from it, and close it (you can close it before reading all the file)
+ */
+
+extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
+ const char* password));
+/*
+ Open for reading data the current file in the zipfile.
+ password is a crypting password
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw,
+ const char* password));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+
+extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+/*
+ Close the file in zip opened with unzOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+
+extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read bytes from the current file (opened by unzOpenCurrentFile)
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+
+extern z_off_t ZEXPORT unztell OF((unzFile file));
+/*
+ Give the current position in uncompressed data
+*/
+
+extern int ZEXPORT unzeof OF((unzFile file));
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+
+extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+
+/***************************************************************************/
+
+/* Get the current file offset */
+extern uLong ZEXPORT unzGetOffset (unzFile file);
+
+/* Set the current file offset */
+extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unz_H */
diff --git a/depends/quazip/zip.c b/depends/quazip/zip.c
new file mode 100644
index 00000000..a36a20a1
--- /dev/null
+++ b/depends/quazip/zip.c
@@ -0,0 +1,1281 @@
+/* zip.c -- IO on .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ 27 Dec 2004 Rolf Kalbermatter
+ Modification to zipOpen2 to support globalComment retrieval.
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read zip.h for more info
+
+ Modified by Sergey A. Tachenov to integrate with Qt.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "zlib.h"
+#include "zip.h"
+#include "quazip_global.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#ifndef VERSIONMADEBY
+# define VERSIONMADEBY (0x031e) /* best for standard pkware crypt */
+#endif
+
+#ifndef Z_BUFSIZE
+#define Z_BUFSIZE (16384)
+#endif
+
+#ifndef Z_MAXFILENAMEINZIP
+#define Z_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+/*
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+*/
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef DEF_MEM_LEVEL
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+#endif
+const char zip_copyright[] =
+ " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+
+#define SIZEDATA_INDATABLOCK (4096-(4*4))
+
+#define LOCALHEADERMAGIC (0x04034b50)
+#define DESCRIPTORHEADERMAGIC (0x08074b50)
+#define CENTRALHEADERMAGIC (0x02014b50)
+#define ENDHEADERMAGIC (0x06054b50)
+
+#define FLAG_LOCALHEADER_OFFSET (0x06)
+#define CRC_LOCALHEADER_OFFSET (0x0e)
+
+#define SIZECENTRALHEADER (0x2e) /* 46 */
+
+typedef struct linkedlist_datablock_internal_s
+{
+ struct linkedlist_datablock_internal_s* next_datablock;
+ uLong avail_in_this_block;
+ uLong filled_in_this_block;
+ uLong unused; /* for future use and alignement */
+ unsigned char data[SIZEDATA_INDATABLOCK];
+} linkedlist_datablock_internal;
+
+typedef struct linkedlist_data_s
+{
+ linkedlist_datablock_internal* first_block;
+ linkedlist_datablock_internal* last_block;
+} linkedlist_data;
+
+
+typedef struct
+{
+ z_stream stream; /* zLib stream structure for inflate */
+ int stream_initialised; /* 1 is stream is initialised */
+ uInt pos_in_buffered_data; /* last written byte in buffered_data */
+
+ uLong pos_local_header; /* offset of the local header of the file
+ currenty writing */
+ char* central_header; /* central header data for the current file */
+ uLong size_centralheader; /* size of the central header for cur file */
+ uLong flag; /* flag of the file currently writing */
+
+ int method; /* compression method of file currenty wr.*/
+ int raw; /* 1 for directly writing raw data */
+ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/
+ uLong dosDate;
+ uLong crc32;
+ int encrypt;
+#ifndef NOCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+ int crypt_header_size;
+#endif
+} curfile_info;
+
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ linkedlist_data central_dir;/* datablock with central dir in construction*/
+ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/
+ curfile_info ci; /* info on the file curretly writing */
+
+ uLong begin_pos; /* position of the beginning of the zipfile */
+ uLong add_position_when_writting_offset;
+ uLong number_entry;
+#ifndef NO_ADDFILEINEXISTINGZIP
+ char *globalcomment;
+#endif
+ unsigned flags;
+} zip_internal;
+
+
+
+#ifndef NOCRYPT
+#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+#include "crypt.h"
+#endif
+
+local linkedlist_datablock_internal* allocate_new_datablock()
+{
+ linkedlist_datablock_internal* ldi;
+ ldi = (linkedlist_datablock_internal*)
+ ALLOC(sizeof(linkedlist_datablock_internal));
+ if (ldi!=NULL)
+ {
+ ldi->next_datablock = NULL ;
+ ldi->filled_in_this_block = 0 ;
+ ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ;
+ }
+ return ldi;
+}
+
+local void free_datablock(ldi)
+ linkedlist_datablock_internal* ldi;
+{
+ while (ldi!=NULL)
+ {
+ linkedlist_datablock_internal* ldinext = ldi->next_datablock;
+ TRYFREE(ldi);
+ ldi = ldinext;
+ }
+}
+
+local void init_linkedlist(ll)
+ linkedlist_data* ll;
+{
+ ll->first_block = ll->last_block = NULL;
+}
+
+#if 0 // unused
+local void free_linkedlist(ll)
+ linkedlist_data* ll;
+{
+ free_datablock(ll->first_block);
+ ll->first_block = ll->last_block = NULL;
+}
+#endif
+
+local int add_data_in_datablock(ll,buf,len)
+ linkedlist_data* ll;
+ const void* buf;
+ uLong len;
+{
+ linkedlist_datablock_internal* ldi;
+ const unsigned char* from_copy;
+
+ if (ll==NULL)
+ return ZIP_INTERNALERROR;
+
+ if (ll->last_block == NULL)
+ {
+ ll->first_block = ll->last_block = allocate_new_datablock();
+ if (ll->first_block == NULL)
+ return ZIP_INTERNALERROR;
+ }
+
+ ldi = ll->last_block;
+ from_copy = (unsigned char*)buf;
+
+ while (len>0)
+ {
+ uInt copy_this;
+ uInt i;
+ unsigned char* to_copy;
+
+ if (ldi->avail_in_this_block==0)
+ {
+ ldi->next_datablock = allocate_new_datablock();
+ if (ldi->next_datablock == NULL)
+ return ZIP_INTERNALERROR;
+ ldi = ldi->next_datablock ;
+ ll->last_block = ldi;
+ }
+
+ if (ldi->avail_in_this_block < len)
+ copy_this = (uInt)ldi->avail_in_this_block;
+ else
+ copy_this = (uInt)len;
+
+ to_copy = &(ldi->data[ldi->filled_in_this_block]);
+
+ for (i=0;i<copy_this;i++)
+ *(to_copy+i)=*(from_copy+i);
+
+ ldi->filled_in_this_block += copy_this;
+ ldi->avail_in_this_block -= copy_this;
+ from_copy += copy_this ;
+ len -= copy_this;
+ }
+ return ZIP_OK;
+}
+
+
+
+/****************************************************************************/
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+/* ===========================================================================
+ Inputs a long in LSB order to the given file
+ nbByte == 1, 2 or 4 (byte, short or long)
+*/
+
+local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream, uLong x, int nbByte));
+local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong x;
+ int nbByte;
+{
+ unsigned char buf[4];
+ int n;
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = (unsigned char)(x & 0xff);
+ x >>= 8;
+ }
+ if (x != 0)
+ { /* data overflow - hack for ZIP64 (X Roche) */
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = 0xff;
+ }
+ }
+
+ if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte)
+ return ZIP_ERRNO;
+ else
+ return ZIP_OK;
+}
+
+local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte));
+local void ziplocal_putValue_inmemory (dest, x, nbByte)
+ void* dest;
+ uLong x;
+ int nbByte;
+{
+ unsigned char* buf=(unsigned char*)dest;
+ int n;
+ for (n = 0; n < nbByte; n++) {
+ buf[n] = (unsigned char)(x & 0xff);
+ x >>= 8;
+ }
+
+ if (x != 0)
+ { /* data overflow - hack for ZIP64 */
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = 0xff;
+ }
+ }
+}
+
+/****************************************************************************/
+
+
+local uLong ziplocal_TmzDateToDosDate(ptm,dosDate)
+ const tm_zip* ptm;
+ uLong dosDate UNUSED;
+{
+ uLong year = (uLong)ptm->tm_year;
+ if (year>1980)
+ year-=1980;
+ else if (year>80)
+ year-=80;
+ return
+ (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
+ ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
+}
+
+
+/****************************************************************************/
+
+local int ziplocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return ZIP_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return ZIP_ERRNO;
+ else
+ return ZIP_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int ziplocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==ZIP_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int ziplocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x ;
+ int i;
+ int err;
+
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==ZIP_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong ziplocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+#endif /* !NO_ADDFILEINEXISTINGZIP*/
+
+/************************************************************/
+extern zipFile ZEXPORT zipOpen2 (file, append, globalcomment, pzlib_filefunc_def)
+ voidpf file;
+ int append;
+ zipcharpc* globalcomment;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ zip_internal ziinit;
+ zip_internal* zi;
+ int err=ZIP_OK;
+
+
+ if (pzlib_filefunc_def==NULL)
+ fill_qiodevice_filefunc(&ziinit.z_filefunc);
+ else
+ ziinit.z_filefunc = *pzlib_filefunc_def;
+
+ ziinit.filestream = (*(ziinit.z_filefunc.zopen_file))
+ (ziinit.z_filefunc.opaque,
+ file,
+ (append == APPEND_STATUS_CREATE) ?
+ (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) :
+ (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING));
+
+ if (ziinit.filestream == NULL)
+ return NULL;
+ ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream);
+ ziinit.in_opened_file_inzip = 0;
+ ziinit.ci.stream_initialised = 0;
+ ziinit.number_entry = 0;
+ ziinit.add_position_when_writting_offset = 0;
+ ziinit.flags = ZIP_WRITE_DATA_DESCRIPTOR;
+ init_linkedlist(&(ziinit.central_dir));
+
+
+ zi = (zip_internal*)ALLOC(sizeof(zip_internal));
+ if (zi==NULL)
+ {
+ ZCLOSE(ziinit.z_filefunc,ziinit.filestream);
+ return NULL;
+ }
+
+ /* now we add file in a zipfile */
+# ifndef NO_ADDFILEINEXISTINGZIP
+ ziinit.globalcomment = NULL;
+ if (append == APPEND_STATUS_ADDINZIP)
+ {
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory */
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry;
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+ uLong size_comment;
+
+ central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream);
+ if (central_pos==0)
+ err=ZIP_ERRNO;
+
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=ZIP_ERRNO;
+
+ /* the signature, already checked */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* number of this disk */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ if ((number_entry_CD!=number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=ZIP_BADZIPFILE;
+
+ /* size of the central directory */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* zipfile global comment length */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ if ((central_pos<offset_central_dir+size_central_dir) &&
+ (err==ZIP_OK))
+ err=ZIP_BADZIPFILE;
+
+ if (err!=ZIP_OK)
+ {
+ ZCLOSE(ziinit.z_filefunc, ziinit.filestream);
+ TRYFREE(zi);
+ return NULL;
+ }
+
+ if (size_comment>0)
+ {
+ ziinit.globalcomment = ALLOC(size_comment+1);
+ if (ziinit.globalcomment)
+ {
+ size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment);
+ ziinit.globalcomment[size_comment]=0;
+ }
+ }
+
+ byte_before_the_zipfile = central_pos -
+ (offset_central_dir+size_central_dir);
+ ziinit.add_position_when_writting_offset = byte_before_the_zipfile;
+
+ {
+ uLong size_central_dir_to_read = size_central_dir;
+ size_t buf_size = SIZEDATA_INDATABLOCK;
+ void* buf_read = (void*)ALLOC(buf_size);
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ offset_central_dir + byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET) != 0)
+ err=ZIP_ERRNO;
+
+ while ((size_central_dir_to_read>0) && (err==ZIP_OK))
+ {
+ uLong read_this = SIZEDATA_INDATABLOCK;
+ if (read_this > size_central_dir_to_read)
+ read_this = size_central_dir_to_read;
+ if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this)
+ err=ZIP_ERRNO;
+
+ if (err==ZIP_OK)
+ err = add_data_in_datablock(&ziinit.central_dir,buf_read,
+ (uLong)read_this);
+ size_central_dir_to_read-=read_this;
+ }
+ TRYFREE(buf_read);
+ }
+ ziinit.begin_pos = byte_before_the_zipfile;
+ ziinit.number_entry = number_entry_CD;
+
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=ZIP_ERRNO;
+ }
+
+ if (globalcomment)
+ {
+ *globalcomment = ziinit.globalcomment;
+ }
+# endif /* !NO_ADDFILEINEXISTINGZIP*/
+
+ if (err != ZIP_OK)
+ {
+# ifndef NO_ADDFILEINEXISTINGZIP
+ TRYFREE(ziinit.globalcomment);
+# endif /* !NO_ADDFILEINEXISTINGZIP*/
+ TRYFREE(zi);
+ return NULL;
+ }
+ else
+ {
+ *zi = ziinit;
+ return (zipFile)zi;
+ }
+}
+
+extern zipFile ZEXPORT zipOpen (file, append)
+ voidpf file;
+ int append;
+{
+ return zipOpen2(file,append,NULL,NULL);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw,
+ windowBits, memLevel, strategy,
+ password, crcForCrypting)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+ int raw;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char* password;
+ uLong crcForCrypting;
+{
+ zip_internal* zi;
+ uInt size_filename;
+ uInt size_comment;
+ uInt i;
+ int err = ZIP_OK;
+ uLong version_to_extract;
+
+# ifdef NOCRYPT
+ if (password != NULL)
+ return ZIP_PARAMERROR;
+# endif
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ if ((method!=0) && (method!=Z_DEFLATED))
+ return ZIP_PARAMERROR;
+
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 1)
+ {
+ err = zipCloseFileInZip (file);
+ if (err != ZIP_OK)
+ return err;
+ }
+
+ if (method == 0
+ && (level == 0 || (zi->flags & ZIP_WRITE_DATA_DESCRIPTOR) == 0))
+ {
+ version_to_extract = 10;
+ }
+ else
+ {
+ version_to_extract = 20;
+ }
+
+
+ if (filename==NULL)
+ filename="-";
+
+ if (comment==NULL)
+ size_comment = 0;
+ else
+ size_comment = (uInt)strlen(comment);
+
+ size_filename = (uInt)strlen(filename);
+
+ if (zipfi == NULL)
+ zi->ci.dosDate = 0;
+ else
+ {
+ if (zipfi->dosDate != 0)
+ zi->ci.dosDate = zipfi->dosDate;
+ else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate);
+ }
+
+ zi->ci.flag = 0;
+ if ((level==8) || (level==9))
+ zi->ci.flag |= 2;
+ if ((level==2))
+ zi->ci.flag |= 4;
+ if ((level==1))
+ zi->ci.flag |= 6;
+ if (password != NULL)
+ {
+ zi->ci.flag |= 1;
+ }
+ if (version_to_extract >= 20
+ && (zi->flags & ZIP_WRITE_DATA_DESCRIPTOR) != 0)
+ zi->ci.flag |= 8;
+ zi->ci.crc32 = 0;
+ zi->ci.method = method;
+ zi->ci.encrypt = 0;
+ zi->ci.stream_initialised = 0;
+ zi->ci.pos_in_buffered_data = 0;
+ zi->ci.raw = raw;
+ zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ;
+ zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename +
+ size_extrafield_global + size_comment;
+ zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader);
+
+ ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
+ /* version info */
+ ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)version_to_extract,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4);
+ ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/
+
+ if (zipfi==NULL)
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2);
+ else
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2);
+
+ if (zipfi==NULL)
+ ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4);
+ else
+ ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4);
+
+ ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4);
+
+ for (i=0;i<size_filename;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
+
+ for (i=0;i<size_extrafield_global;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) =
+ *(((const char*)extrafield_global)+i);
+
+ for (i=0;i<size_comment;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+
+ size_extrafield_global+i) = *(comment+i);
+ if (zi->ci.central_header == NULL)
+ return ZIP_INTERNALERROR;
+
+ /* write the local header */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)version_to_extract,2);
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2);
+
+ if ((err==ZIP_OK) && (size_filename>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename)
+ err = ZIP_ERRNO;
+
+ if ((err==ZIP_OK) && (size_extrafield_local>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local)
+ !=size_extrafield_local)
+ err = ZIP_ERRNO;
+
+ zi->ci.stream.avail_in = (uInt)0;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ zi->ci.stream.total_in = 0;
+ zi->ci.stream.total_out = 0;
+
+ if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ zi->ci.stream.zalloc = (alloc_func)0;
+ zi->ci.stream.zfree = (free_func)0;
+ zi->ci.stream.opaque = (voidpf)0;
+
+ if (windowBits>0)
+ windowBits = -windowBits;
+
+ err = deflateInit2(&zi->ci.stream, level,
+ Z_DEFLATED, windowBits, memLevel, strategy);
+
+ if (err==Z_OK)
+ zi->ci.stream_initialised = 1;
+ }
+# ifndef NOCRYPT
+ zi->ci.crypt_header_size = 0;
+ if ((err==Z_OK) && (password != NULL))
+ {
+ unsigned char bufHead[RAND_HEAD_LEN];
+ unsigned int sizeHead;
+ zi->ci.encrypt = 1;
+ zi->ci.pcrc_32_tab = (const unsigned long*) get_crc_table();
+ /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/
+
+ crcForCrypting = (uLong)zi->ci.dosDate << 16; // ATTANTION! Without this row, you don't unpack your password protected archive in other app.
+
+ sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting);
+ zi->ci.crypt_header_size = sizeHead;
+
+ if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead)
+ err = ZIP_ERRNO;
+ }
+# endif
+
+ if (err==Z_OK)
+ zi->in_opened_file_inzip = 1;
+ return err;
+}
+
+extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+ int raw;
+{
+ return zipOpenNewFileInZip3 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw,
+ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+ NULL, 0);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+{
+ return zipOpenNewFileInZip2 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, 0);
+}
+
+local int zipFlushWriteBuffer(zi)
+ zip_internal* zi;
+{
+ int err=ZIP_OK;
+
+ if (zi->ci.encrypt != 0)
+ {
+#ifndef NOCRYPT
+ uInt i;
+ int t;
+ for (i=0;i<zi->ci.pos_in_buffered_data;i++)
+ zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab,
+ zi->ci.buffered_data[i],t);
+#endif
+ }
+ if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data)
+ !=zi->ci.pos_in_buffered_data)
+ err = ZIP_ERRNO;
+ zi->ci.pos_in_buffered_data = 0;
+ return err;
+}
+
+extern int ZEXPORT zipWriteInFileInZip (file, buf, len)
+ zipFile file;
+ const void* buf;
+ unsigned len;
+{
+ zip_internal* zi;
+ int err=ZIP_OK;
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 0)
+ return ZIP_PARAMERROR;
+
+ zi->ci.stream.next_in = (void*)buf;
+ zi->ci.stream.avail_in = len;
+ zi->ci.crc32 = crc32(zi->ci.crc32,buf,len);
+
+ while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0))
+ {
+ if (zi->ci.stream.avail_out == 0)
+ {
+ if (zipFlushWriteBuffer(zi) == ZIP_ERRNO)
+ err = ZIP_ERRNO;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ }
+
+
+ if(err != ZIP_OK)
+ break;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ uLong uTotalOutBefore = zi->ci.stream.total_out;
+ err=deflate(&zi->ci.stream, Z_NO_FLUSH);
+ zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+
+ }
+ else
+ {
+ uInt copy_this,i;
+ if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
+ copy_this = zi->ci.stream.avail_in;
+ else
+ copy_this = zi->ci.stream.avail_out;
+ for (i=0;i<copy_this;i++)
+ *(((char*)zi->ci.stream.next_out)+i) =
+ *(((const char*)zi->ci.stream.next_in)+i);
+ {
+ zi->ci.stream.avail_in -= copy_this;
+ zi->ci.stream.avail_out-= copy_this;
+ zi->ci.stream.next_in+= copy_this;
+ zi->ci.stream.next_out+= copy_this;
+ zi->ci.stream.total_in+= copy_this;
+ zi->ci.stream.total_out+= copy_this;
+ zi->ci.pos_in_buffered_data += copy_this;
+ }
+ }
+ }
+
+ return err;
+}
+
+extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32)
+ zipFile file;
+ uLong uncompressed_size;
+ uLong crc32;
+{
+ zip_internal* zi;
+ uLong compressed_size;
+ int err=ZIP_OK;
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 0)
+ return ZIP_PARAMERROR;
+ zi->ci.stream.avail_in = 0;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ while (err==ZIP_OK)
+ {
+ uLong uTotalOutBefore;
+ if (zi->ci.stream.avail_out == 0)
+ {
+ if (zipFlushWriteBuffer(zi) == ZIP_ERRNO)
+ err = ZIP_ERRNO;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ }
+ uTotalOutBefore = zi->ci.stream.total_out;
+ err=deflate(&zi->ci.stream, Z_FINISH);
+ zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+ }
+
+ if (err==Z_STREAM_END)
+ err=ZIP_OK; /* this is normal */
+
+ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK))
+ if (zipFlushWriteBuffer(zi)==ZIP_ERRNO)
+ err = ZIP_ERRNO;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ err=deflateEnd(&zi->ci.stream);
+ zi->ci.stream_initialised = 0;
+ }
+
+ if (!zi->ci.raw)
+ {
+ crc32 = (uLong)zi->ci.crc32;
+ uncompressed_size = (uLong)zi->ci.stream.total_in;
+ }
+ compressed_size = (uLong)zi->ci.stream.total_out;
+# ifndef NOCRYPT
+ compressed_size += zi->ci.crypt_header_size;
+# endif
+
+ ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+20,
+ compressed_size,4); /*compr size*/
+ if (zi->ci.stream.data_type == Z_ASCII)
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+24,
+ uncompressed_size,4); /*uncompr size*/
+
+ if (err==ZIP_OK)
+ err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header,
+ (uLong)zi->ci.size_centralheader);
+ free(zi->ci.central_header);
+
+ if (err==ZIP_OK)
+ {
+ uLong cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream);
+ if (ZSEEK(zi->z_filefunc,zi->filestream,
+ zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err = ZIP_ERRNO;
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
+
+ if (err==ZIP_OK) /* compressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4);
+
+ if (err==ZIP_OK) /* uncompressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4);
+
+ if (ZSEEK(zi->z_filefunc,zi->filestream,
+ cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err = ZIP_ERRNO;
+
+ if ((zi->ci.flag & 8) != 0) {
+ /* Write local Descriptor after file data */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)DESCRIPTORHEADERMAGIC,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
+
+ if (err==ZIP_OK) /* compressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4);
+
+ if (err==ZIP_OK) /* uncompressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4);
+ }
+
+
+ }
+
+ zi->number_entry ++;
+ zi->in_opened_file_inzip = 0;
+
+ return err;
+}
+
+extern int ZEXPORT zipCloseFileInZip (file)
+ zipFile file;
+{
+ return zipCloseFileInZipRaw (file,0,0);
+}
+
+extern int ZEXPORT zipClose (file, global_comment)
+ zipFile file;
+ const char* global_comment;
+{
+ zip_internal* zi;
+ int err = 0;
+ uLong size_centraldir = 0;
+ uLong centraldir_pos_inzip;
+ uInt size_global_comment;
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 1)
+ {
+ err = zipCloseFileInZip (file);
+ }
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+ if (global_comment==NULL)
+ global_comment = zi->globalcomment;
+#endif
+ if (global_comment==NULL)
+ size_global_comment = 0;
+ else
+ size_global_comment = (uInt)strlen(global_comment);
+
+ centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream);
+ if (err==ZIP_OK)
+ {
+ linkedlist_datablock_internal* ldi = zi->central_dir.first_block ;
+ while (ldi!=NULL)
+ {
+ if ((err==ZIP_OK) && (ldi->filled_in_this_block>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,
+ ldi->data,ldi->filled_in_this_block)
+ !=ldi->filled_in_this_block )
+ err = ZIP_ERRNO;
+
+ size_centraldir += ldi->filled_in_this_block;
+ ldi = ldi->next_datablock;
+ }
+ }
+ free_datablock(zi->central_dir.first_block);
+
+ if (err==ZIP_OK) /* Magic End */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4);
+
+ if (err==ZIP_OK) /* number of this disk */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
+
+ if (err==ZIP_OK) /* number of the disk with the start of the central directory */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
+
+ if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
+
+ if (err==ZIP_OK) /* total number of entries in the central dir */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
+
+ if (err==ZIP_OK) /* size of the central directory */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4);
+
+ if (err==ZIP_OK) /* offset of start of central directory with respect to the
+ starting disk number */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,
+ (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4);
+
+ if (err==ZIP_OK) /* zipfile comment length */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2);
+
+ if ((err==ZIP_OK) && (size_global_comment>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,
+ global_comment,size_global_comment) != size_global_comment)
+ err = ZIP_ERRNO;
+
+ if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0)
+ if (err == ZIP_OK)
+ err = ZIP_ERRNO;
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+ TRYFREE(zi->globalcomment);
+#endif
+ TRYFREE(zi);
+
+ return err;
+}
+
+extern int ZEXPORT zipSetFlags(zipFile file, unsigned flags)
+{
+ zip_internal* zi;
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+ zi->flags |= flags;
+ return ZIP_OK;
+}
+
+extern int ZEXPORT zipClearFlags(zipFile file, unsigned flags)
+{
+ zip_internal* zi;
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+ zi->flags &= ~flags;
+ return ZIP_OK;
+}
diff --git a/depends/quazip/zip.h b/depends/quazip/zip.h
new file mode 100644
index 00000000..269ec2da
--- /dev/null
+++ b/depends/quazip/zip.h
@@ -0,0 +1,245 @@
+/* zip.h -- IO for compress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+ For uncompress .zip file, look at unzip.h
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.html for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Modified by Sergey A. Tachenov to integrate with Qt.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _zip_H
+#define _zip_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagzipFile__ { int unused; } zipFile__;
+typedef zipFile__ *zipFile;
+#else
+typedef voidp zipFile;
+#endif
+
+#define ZIP_OK (0)
+#define ZIP_EOF (0)
+#define ZIP_ERRNO (Z_ERRNO)
+#define ZIP_PARAMERROR (-102)
+#define ZIP_BADZIPFILE (-103)
+#define ZIP_INTERNALERROR (-104)
+
+#define ZIP_WRITE_DATA_DESCRIPTOR 0x8u
+
+#ifndef DEF_MEM_LEVEL
+# if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+# else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+# endif
+#endif
+/* default memLevel */
+
+/* tm_zip contain date/time info */
+typedef struct tm_zip_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_zip;
+
+typedef struct
+{
+ tm_zip tmz_date; /* date in understandable format */
+ uLong dosDate; /* if dos_date == 0, tmu_date is used */
+/* uLong flag; */ /* general purpose bit flag 2 bytes */
+
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+} zip_fileinfo;
+
+typedef const char* zipcharpc;
+
+
+#define APPEND_STATUS_CREATE (0)
+#define APPEND_STATUS_CREATEAFTER (1)
+#define APPEND_STATUS_ADDINZIP (2)
+
+extern zipFile ZEXPORT zipOpen OF((voidpf file, int append));
+/*
+ Create a zipfile.
+ file is whatever the IO API accepts. For Qt IO API it's a pointer to
+ QIODevice. For fopen() IO API it's a file name (const char*).
+ if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
+ will be created at the end of the file.
+ (useful if the file contain a self extractor code)
+ if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
+ add files in existing zip (be sure you don't add file that doesn't exist)
+ If the zipfile cannot be opened, the return value is NULL.
+ Else, the return value is a zipFile Handle, usable with other function
+ of this zip package.
+*/
+
+/* Note : there is no delete function into a zipfile.
+ If you want delete file into a zipfile, you must open a zipfile, and create another
+ Of couse, you can use RAW reading and writing to copy the file you did not want delte
+*/
+
+extern zipFile ZEXPORT zipOpen2 OF((voidpf file,
+ int append,
+ zipcharpc* globalcomment,
+ zlib_filefunc_def* pzlib_filefunc_def));
+
+extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level));
+/*
+ Open a file in the ZIP for writing.
+ filename : the filename in zip (if NULL, '-' without quote will be used
+ *zipfi contain supplemental information
+ if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
+ contains the extrafield data the the local header
+ if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
+ contains the extrafield data the the local header
+ if comment != NULL, comment contain the comment string
+ method contain the compression method (0 for store, Z_DEFLATED for deflate)
+ level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
+*/
+
+
+extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level,
+ int raw));
+
+/*
+ Same than zipOpenNewFileInZip, except if raw=1, we write raw file
+ */
+
+extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level,
+ int raw,
+ int windowBits,
+ int memLevel,
+ int strategy,
+ const char* password,
+ uLong crcForCtypting));
+
+/*
+ Same than zipOpenNewFileInZip2, except
+ windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
+ password : crypting password (NULL for no crypting)
+ crcForCtypting : crc of file to compress (needed for crypting)
+ */
+
+
+extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
+ const void* buf,
+ unsigned len));
+/*
+ Write data in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
+/*
+ Close the current file in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file,
+ uLong uncompressed_size,
+ uLong crc32));
+/*
+ Close the current file in the zipfile, for fiel opened with
+ parameter raw=1 in zipOpenNewFileInZip2
+ uncompressed_size and crc32 are value for the uncompressed size
+*/
+
+extern int ZEXPORT zipClose OF((zipFile file,
+ const char* global_comment));
+/*
+ Close the zipfile
+*/
+
+/*
+ Added by Sergey A. Tachenov to tweak zipping behaviour.
+ */
+extern int ZEXPORT zipSetFlags(zipFile file, unsigned flags);
+extern int ZEXPORT zipClearFlags(zipFile file, unsigned flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _zip_H */
diff --git a/depends/settings/CMakeLists.txt b/depends/settings/CMakeLists.txt
new file mode 100644
index 00000000..da853a73
--- /dev/null
+++ b/depends/settings/CMakeLists.txt
@@ -0,0 +1,47 @@
+project(libSettings)
+
+# Find Qt
+find_package(Qt5Core REQUIRED)
+
+# Include Qt headers.
+include_directories(${Qt5Base_INCLUDE_DIRS})
+
+
+SET(LIBSETTINGS_SOURCES
+libsettings_config.h
+
+inifile.h
+inifile.cpp
+
+settingsobject.h
+settingsobject.cpp
+inisettingsobject.h
+inisettingsobject.cpp
+
+setting.h
+setting.cpp
+overridesetting.h
+overridesetting.cpp
+)
+
+# Set the include dir path.
+SET(LIBSETTINGS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE)
+
+# Static link!
+ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
+
+add_definitions(-DLIBSETTINGS_LIBRARY)
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+IF(MultiMC_CODE_COVERAGE)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O0 --coverage")
+ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
+ENDIF(MultiMC_CODE_COVERAGE)
+
+add_library(libSettings STATIC ${LIBSETTINGS_SOURCES})
+qt5_use_modules(libSettings Core)
+target_link_libraries(libSettings)
diff --git a/depends/settings/inifile.cpp b/depends/settings/inifile.cpp
new file mode 100644
index 00000000..1170f0b1
--- /dev/null
+++ b/depends/settings/inifile.cpp
@@ -0,0 +1,106 @@
+/* Copyright 2013 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 "inifile.h"
+
+#include <QFile>
+#include <QTextStream>
+#include <QStringList>
+
+INIFile::INIFile()
+{
+}
+
+QString INIFile::unescape(QString orig)
+{
+ orig.replace("\\n", "\n");
+ orig.replace("\\t", "\t");
+ orig.replace("\\\\", "\\");
+ return orig;
+}
+QString INIFile::escape(QString orig)
+{
+ orig.replace("\\", "\\\\");
+ orig.replace("\n", "\\n");
+ orig.replace("\t", "\\t");
+ return orig;
+}
+
+bool INIFile::saveFile(QString fileName)
+{
+ // TODO Handle errors.
+ QFile file(fileName);
+ file.open(QIODevice::WriteOnly);
+ QTextStream out(&file);
+ out.setCodec("UTF-8");
+
+ for (Iterator iter = begin(); iter != end(); iter++)
+ {
+ QString value = iter.value().toString();
+ value = escape(value);
+ out << iter.key() << "=" << value << "\n";
+ }
+
+ return true;
+}
+
+bool INIFile::loadFile(QString fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+ bool success = loadFile(file.readAll());
+ file.close();
+ return success;
+}
+bool INIFile::loadFile(QByteArray file)
+{
+ QTextStream in(file);
+ in.setCodec("UTF-8");
+
+ QStringList lines = in.readAll().split('\n');
+ for (int i = 0; i < lines.count(); i++)
+ {
+ QString &lineRaw = lines[i];
+ // Ignore comments.
+ QString line = lineRaw.left(lineRaw.indexOf('#')).trimmed();
+
+ int eqPos = line.indexOf('=');
+ if (eqPos == -1)
+ continue;
+ QString key = line.left(eqPos).trimmed();
+ QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
+
+ valueStr = unescape(valueStr);
+
+ QVariant value(valueStr);
+ this->operator[](key) = value;
+ }
+
+ return true;
+}
+
+QVariant INIFile::get(QString key, QVariant def) const
+{
+ if (!this->contains(key))
+ return def;
+ else
+ return this->operator[](key);
+}
+
+void INIFile::set(QString key, QVariant val)
+{
+ this->operator[](key) = val;
+}
diff --git a/depends/settings/inifile.h b/depends/settings/inifile.h
new file mode 100644
index 00000000..27da7bf0
--- /dev/null
+++ b/depends/settings/inifile.h
@@ -0,0 +1,38 @@
+/* Copyright 2013 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 <QString>
+#include <QVariant>
+#include <QIODevice>
+
+#include "libsettings_config.h"
+
+// Sectionless INI parser (for instance config files)
+class LIBSETTINGS_EXPORT INIFile : public QMap<QString, QVariant>
+{
+public:
+ explicit INIFile();
+
+ bool loadFile(QByteArray file);
+ bool loadFile(QString fileName);
+ bool saveFile(QString fileName);
+
+ QVariant get(QString key, QVariant def) const;
+ void set(QString key, QVariant val);
+ QString unescape(QString orig);
+ QString escape(QString orig);
+};
diff --git a/depends/settings/inisettingsobject.cpp b/depends/settings/inisettingsobject.cpp
new file mode 100644
index 00000000..5e52a56f
--- /dev/null
+++ b/depends/settings/inisettingsobject.cpp
@@ -0,0 +1,76 @@
+/* Copyright 2013 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 "inisettingsobject.h"
+#include "setting.h"
+
+INISettingsObject::INISettingsObject(const QString &path, QObject *parent)
+ : SettingsObject(parent)
+{
+ m_filePath = path;
+ m_ini.loadFile(path);
+}
+
+void INISettingsObject::setFilePath(const QString &filePath)
+{
+ m_filePath = filePath;
+}
+
+void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
+{
+ if (contains(setting.id()))
+ {
+ // valid value -> set the main config, remove all the sysnonyms
+ if (value.isValid())
+ {
+ auto list = setting.configKeys();
+ m_ini.set(list.takeFirst(), value);
+ for(auto iter: list)
+ m_ini.remove(iter);
+ }
+ // invalid -> remove all (just like resetSetting)
+ else
+ {
+ for(auto iter: setting.configKeys())
+ m_ini.remove(iter);
+ }
+ m_ini.saveFile(m_filePath);
+ }
+}
+
+void INISettingsObject::resetSetting(const Setting &setting)
+{
+ // if we have the setting, remove all the synonyms. ALL OF THEM
+ if (contains(setting.id()))
+ {
+ for(auto iter: setting.configKeys())
+ m_ini.remove(iter);
+ m_ini.saveFile(m_filePath);
+ }
+}
+
+QVariant INISettingsObject::retrieveValue(const Setting &setting)
+{
+ // if we have the setting, return value of the first matching synonym
+ if (contains(setting.id()))
+ {
+ for(auto iter: setting.configKeys())
+ {
+ if(m_ini.contains(iter))
+ return m_ini[iter];
+ }
+ }
+ return QVariant();
+}
diff --git a/depends/settings/inisettingsobject.h b/depends/settings/inisettingsobject.h
new file mode 100644
index 00000000..8badc0c6
--- /dev/null
+++ b/depends/settings/inisettingsobject.h
@@ -0,0 +1,61 @@
+/* Copyright 2013 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 <QObject>
+
+#include "inifile.h"
+
+#include "settingsobject.h"
+
+#include "libsettings_config.h"
+
+/*!
+ * \brief A settings object that stores its settings in an INIFile.
+ */
+class LIBSETTINGS_EXPORT INISettingsObject : public SettingsObject
+{
+ Q_OBJECT
+public:
+ explicit INISettingsObject(const QString &path, QObject *parent = 0);
+
+ /*!
+ * \brief Gets the path to the INI file.
+ * \return The path to the INI file.
+ */
+ virtual QString filePath() const
+ {
+ return m_filePath;
+ }
+
+ /*!
+ * \brief Sets the path to the INI file and reloads it.
+ * \param filePath The INI file's new path.
+ */
+ virtual void setFilePath(const QString &filePath);
+
+protected
+slots:
+ virtual void changeSetting(const Setting &setting, QVariant value);
+ virtual void resetSetting(const Setting &setting);
+
+protected:
+ virtual QVariant retrieveValue(const Setting &setting);
+
+ INIFile m_ini;
+
+ QString m_filePath;
+};
diff --git a/depends/settings/libsettings_config.h b/depends/settings/libsettings_config.h
new file mode 100644
index 00000000..e5beed28
--- /dev/null
+++ b/depends/settings/libsettings_config.h
@@ -0,0 +1,29 @@
+/* Copyright 2013 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 <QtCore/QtGlobal>
+
+#ifdef LIBSETTINGS_STATIC
+#define LIBSETTINGS_EXPORT
+#else
+#ifdef LIBSETTINGS_LIBRARY
+#define LIBSETTINGS_EXPORT Q_DECL_EXPORT
+#else
+#define LIBSETTINGS_EXPORT Q_DECL_IMPORT
+#endif
+#endif
+
diff --git a/depends/settings/overridesetting.cpp b/depends/settings/overridesetting.cpp
new file mode 100644
index 00000000..7b5f5418
--- /dev/null
+++ b/depends/settings/overridesetting.cpp
@@ -0,0 +1,30 @@
+/* Copyright 2013 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 "overridesetting.h"
+
+OverrideSetting::OverrideSetting(std::shared_ptr<Setting> other)
+ : Setting(other->configKeys(), QVariant())
+{
+ m_other = other;
+}
+
+QVariant OverrideSetting::defValue() const
+{
+ if (m_other)
+ return m_other->get();
+ else
+ return QVariant();
+}
diff --git a/depends/settings/overridesetting.h b/depends/settings/overridesetting.h
new file mode 100644
index 00000000..5ef901d0
--- /dev/null
+++ b/depends/settings/overridesetting.h
@@ -0,0 +1,41 @@
+/* Copyright 2013 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 <QObject>
+#include <memory>
+
+#include "setting.h"
+
+#include "libsettings_config.h"
+
+/*!
+ * \brief A setting that 'overrides another.'
+ * This means that the setting's default value will be the value of another setting.
+ * The other setting can be (and usually is) a part of a different SettingsObject
+ * than this one.
+ */
+class LIBSETTINGS_EXPORT OverrideSetting : public Setting
+{
+ Q_OBJECT
+public:
+ explicit OverrideSetting(std::shared_ptr<Setting> other);
+
+ virtual QVariant defValue() const;
+
+protected:
+ std::shared_ptr<Setting> m_other;
+};
diff --git a/depends/settings/setting.cpp b/depends/settings/setting.cpp
new file mode 100644
index 00000000..0d685771
--- /dev/null
+++ b/depends/settings/setting.cpp
@@ -0,0 +1,53 @@
+/* Copyright 2013 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 "setting.h"
+#include "settingsobject.h"
+
+Setting::Setting(QStringList synonyms, QVariant defVal)
+ : QObject(), m_synonyms(synonyms), m_defVal(defVal)
+{
+}
+
+QVariant Setting::get() const
+{
+ SettingsObject *sbase = m_storage;
+ if (!sbase)
+ {
+ return defValue();
+ }
+ else
+ {
+ QVariant test = sbase->retrieveValue(*this);
+ if (!test.isValid())
+ return defValue();
+ return test;
+ }
+}
+
+QVariant Setting::defValue() const
+{
+ return m_defVal;
+}
+
+void Setting::set(QVariant value)
+{
+ emit settingChanged(*this, value);
+}
+
+void Setting::reset()
+{
+ emit settingReset(*this);
+}
diff --git a/depends/settings/setting.h b/depends/settings/setting.h
new file mode 100644
index 00000000..a73474d2
--- /dev/null
+++ b/depends/settings/setting.h
@@ -0,0 +1,119 @@
+/* Copyright 2013 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 <QObject>
+#include <QVariant>
+#include <QStringList>
+#include <memory>
+
+#include "libsettings_config.h"
+
+class SettingsObject;
+
+/*!
+ *
+ */
+class LIBSETTINGS_EXPORT Setting : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a Setting
+ *
+ * Synonyms are all the possible names used in the settings object, in order of preference.
+ * First synonym is the ID, which identifies the setting in MultiMC.
+ *
+ * defVal is the default value that will be returned when the settings object
+ * doesn't have any value for this setting.
+ */
+ explicit Setting(QStringList synonyms, QVariant defVal = QVariant());
+
+ /*!
+ * \brief Gets this setting's ID.
+ * This is used to refer to the setting within the application.
+ * \warning Changing the ID while the setting is registered with a SettingsObject results in
+ * undefined behavior.
+ * \return The ID of the setting.
+ */
+ virtual QString id() const
+ {
+ return m_synonyms.first();
+ }
+
+ /*!
+ * \brief Gets this setting's config file key.
+ * This is used to store the setting's value in the config file. It is usually
+ * the same as the setting's ID, but it can be different.
+ * \return The setting's config file key.
+ */
+ virtual QStringList configKeys() const
+ {
+ return m_synonyms;
+ }
+
+ /*!
+ * \brief Gets this setting's value as a QVariant.
+ * This is done by calling the SettingsObject's retrieveValue() function.
+ * If this Setting doesn't have a SettingsObject, this returns an invalid QVariant.
+ * \return QVariant containing this setting's value.
+ * \sa value()
+ */
+ virtual QVariant get() const;
+
+ /*!
+ * \brief Gets this setting's default value.
+ * \return The default value of this setting.
+ */
+ virtual QVariant defValue() const;
+
+signals:
+ /*!
+ * \brief Signal emitted when this Setting object's value changes.
+ * \param setting A reference to the Setting that changed.
+ * \param value This Setting object's new value.
+ */
+ void settingChanged(const Setting &setting, QVariant value);
+
+ /*!
+ * \brief Signal emitted when this Setting object's value resets to default.
+ * \param setting A reference to the Setting that changed.
+ */
+ void settingReset(const Setting &setting);
+
+public
+slots:
+ /*!
+ * \brief Changes the setting's value.
+ * This is done by emitting the settingChanged() signal which will then be
+ * handled by the SettingsObject object and cause the setting to change.
+ * \param value The new value.
+ */
+ virtual void set(QVariant value);
+
+ /*!
+ * \brief Reset the setting to default
+ * This is done by emitting the settingReset() signal which will then be
+ * handled by the SettingsObject object and cause the setting to change.
+ */
+ virtual void reset();
+
+protected:
+ friend class SettingsObject;
+ SettingsObject * m_storage;
+ QStringList m_synonyms;
+ QVariant m_defVal;
+};
diff --git a/depends/settings/settingsobject.cpp b/depends/settings/settingsobject.cpp
new file mode 100644
index 00000000..43fc989a
--- /dev/null
+++ b/depends/settings/settingsobject.cpp
@@ -0,0 +1,138 @@
+/* Copyright 2013 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 "settingsobject.h"
+#include "setting.h"
+#include "overridesetting.h"
+
+#include <QVariant>
+
+SettingsObject::SettingsObject(QObject *parent) : QObject(parent)
+{
+}
+
+SettingsObject::~SettingsObject()
+{
+ m_settings.clear();
+}
+
+std::shared_ptr<Setting> SettingsObject::registerOverride(std::shared_ptr<Setting> original)
+{
+ if (contains(original->id()))
+ {
+ qDebug(QString("Failed to register setting %1. ID already exists.")
+ .arg(original->id())
+ .toUtf8());
+ return nullptr; // Fail
+ }
+ auto override = std::make_shared<OverrideSetting>(original);
+ override->m_storage = this;
+ connectSignals(*override);
+ m_settings.insert(override->id(), override);
+ return override;
+}
+
+std::shared_ptr<Setting> SettingsObject::registerSetting(QStringList synonyms, QVariant defVal)
+{
+ if (synonyms.empty())
+ return nullptr;
+ if (contains(synonyms.first()))
+ {
+ qDebug(QString("Failed to register setting %1. ID already exists.")
+ .arg(synonyms.first())
+ .toUtf8());
+ return nullptr; // Fail
+ }
+ auto setting = std::make_shared<Setting>(synonyms, defVal);
+ setting->m_storage = this;
+ connectSignals(*setting);
+ m_settings.insert(setting->id(), setting);
+ return setting;
+}
+
+/*
+
+bool SettingsObject::registerSetting(Setting *setting)
+{
+ if (contains(setting->id()))
+ {
+ qDebug(QString("Failed to register setting %1. ID already exists.")
+ .arg(setting->id())
+ .toUtf8());
+ return false; // Fail
+ }
+
+ m_settings.insert(setting->id(), setting);
+ setting->setParent(this); // Take ownership.
+
+ // Connect signals.
+ connectSignals(*setting);
+
+ // qDebug(QString("Registered setting %1.").arg(setting->id()).toUtf8());
+ return true;
+}
+*/
+std::shared_ptr<Setting> SettingsObject::getSetting(const QString &id) const
+{
+ // Make sure there is a setting with the given ID.
+ if (!m_settings.contains(id))
+ return NULL;
+
+ return m_settings[id];
+}
+
+QVariant SettingsObject::get(const QString &id) const
+{
+ auto setting = getSetting(id);
+ return (setting ? setting->get() : QVariant());
+}
+
+bool SettingsObject::set(const QString &id, QVariant value)
+{
+ auto setting = getSetting(id);
+ if (!setting)
+ {
+ qDebug(QString("Error changing setting %1. Setting doesn't exist.").arg(id).toUtf8());
+ return false;
+ }
+ else
+ {
+ setting->set(value);
+ return true;
+ }
+}
+
+void SettingsObject::reset(const QString &id) const
+{
+ auto setting = getSetting(id);
+ if (setting)
+ setting->reset();
+}
+
+bool SettingsObject::contains(const QString &id)
+{
+ return m_settings.contains(id);
+}
+
+void SettingsObject::connectSignals(const Setting &setting)
+{
+ connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)),
+ SLOT(changeSetting(const Setting &, QVariant)));
+ connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)),
+ SIGNAL(settingChanged(const Setting &, QVariant)));
+
+ connect(&setting, SIGNAL(settingReset(Setting)), SLOT(resetSetting(const Setting &)));
+ connect(&setting, SIGNAL(settingReset(Setting)), SIGNAL(settingReset(const Setting &)));
+}
diff --git a/depends/settings/settingsobject.h b/depends/settings/settingsobject.h
new file mode 100644
index 00000000..27746f2d
--- /dev/null
+++ b/depends/settings/settingsobject.h
@@ -0,0 +1,173 @@
+/* Copyright 2013 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 <QObject>
+#include <QMap>
+#include <QStringList>
+#include <QVariant>
+#include <memory>
+
+#include "libsettings_config.h"
+
+class Setting;
+
+/*!
+ * \brief The SettingsObject handles communicating settings between the application and a
+ *settings file.
+ * The class keeps a list of Setting objects. Each Setting object represents one
+ * of the application's settings. These Setting objects are registered with
+ * a SettingsObject and can be managed similarly to the way a list works.
+ *
+ * \author Andrew Okin
+ * \date 2/22/2013
+ *
+ * \sa Setting
+ */
+class LIBSETTINGS_EXPORT SettingsObject : public QObject
+{
+ Q_OBJECT
+public:
+ explicit SettingsObject(QObject *parent = 0);
+ virtual ~SettingsObject();
+ /*!
+ * Registers an override setting for the given original setting in this settings object
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerOverride(std::shared_ptr<Setting> original);
+
+ /*!
+ * Registers the given setting with this SettingsObject and connects the necessary signals.
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerSetting(QStringList synonyms,
+ QVariant defVal = QVariant());
+
+ /*!
+ * Registers the given setting with this SettingsObject and connects the necessary signals.
+ *
+ * This will fail if there is already a setting with the same ID as
+ * the one that is being registered.
+ * \return A valid Setting shared pointer if successful.
+ */
+ std::shared_ptr<Setting> registerSetting(QString id, QVariant defVal = QVariant())
+ {
+ return registerSetting(QStringList(id), defVal);
+ }
+
+ /*!
+ * \brief Gets the setting with the given ID.
+ * \param id The ID of the setting to get.
+ * \return A pointer to the setting with the given ID.
+ * Returns null if there is no setting with the given ID.
+ * \sa operator []()
+ */
+ std::shared_ptr<Setting> getSetting(const QString &id) const;
+
+ /*!
+ * \brief Gets the value of the setting with the given ID.
+ * \param id The ID of the setting to get.
+ * \return The setting's value as a QVariant.
+ * If no setting with the given ID exists, returns an invalid QVariant.
+ */
+ QVariant get(const QString &id) const;
+
+ /*!
+ * \brief Sets the value of the setting with the given ID.
+ * If no setting with the given ID exists, returns false and logs to qDebug
+ * \param id The ID of the setting to change.
+ * \param value The new value of the setting.
+ * \return True if successful, false if it failed.
+ */
+ bool set(const QString &id, QVariant value);
+
+ /*!
+ * \brief Reverts the setting with the given ID to default.
+ * \param id The ID of the setting to reset.
+ */
+ void reset(const QString &id) const;
+
+ /*!
+ * \brief Checks if this SettingsObject contains a setting with the given ID.
+ * \param id The ID to check for.
+ * \return True if the SettingsObject has a setting with the given ID.
+ */
+ bool contains(const QString &id);
+
+signals:
+ /*!
+ * \brief Signal emitted when one of this SettingsObject object's settings changes.
+ * This is usually just connected directly to each Setting object's
+ * settingChanged() signals.
+ * \param setting A reference to the Setting object that changed.
+ * \param value The Setting object's new value.
+ */
+ void settingChanged(const Setting &setting, QVariant value);
+
+ /*!
+ * \brief Signal emitted when one of this SettingsObject object's settings resets.
+ * This is usually just connected directly to each Setting object's
+ * settingReset() signals.
+ * \param setting A reference to the Setting object that changed.
+ */
+ void settingReset(const Setting &setting);
+
+protected
+slots:
+ /*!
+ * \brief Changes a setting.
+ * This slot is usually connected to each Setting object's
+ * settingChanged() signal. The signal is emitted, causing this slot
+ * to update the setting's value in the config file.
+ * \param setting A reference to the Setting object that changed.
+ * \param value The setting's new value.
+ */
+ virtual void changeSetting(const Setting &setting, QVariant value) = 0;
+
+ /*!
+ * \brief Resets a setting.
+ * This slot is usually connected to each Setting object's
+ * settingReset() signal. The signal is emitted, causing this slot
+ * to update the setting's value in the config file.
+ * \param setting A reference to the Setting object that changed.
+ */
+ virtual void resetSetting(const Setting &setting) = 0;
+
+protected:
+ /*!
+ * \brief Connects the necessary signals to the given Setting.
+ * \param setting The setting to connect.
+ */
+ void connectSignals(const Setting &setting);
+
+ /*!
+ * \brief Function used by Setting objects to get their values from the SettingsObject.
+ * \param setting The
+ * \return
+ */
+ virtual QVariant retrieveValue(const Setting &setting) = 0;
+
+ friend class Setting;
+
+private:
+ QMap<QString, std::shared_ptr<Setting>> m_settings;
+};
diff --git a/depends/util/CMakeLists.txt b/depends/util/CMakeLists.txt
new file mode 100644
index 00000000..db7d70e6
--- /dev/null
+++ b/depends/util/CMakeLists.txt
@@ -0,0 +1,61 @@
+project(libUtil)
+
+######## Set compiler flags ########
+IF(APPLE)
+ # assume clang 4.1.0+, add C++0x/C++11 stuff
+ message(STATUS "Using APPLE CMAKE_CXX_FLAGS")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ELSEIF(UNIX)
+ # assume GCC, add C++0x/C++11 stuff
+ MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ELSEIF(MINGW)
+ MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
+ENDIF()
+
+
+# Find Qt
+find_package(Qt5Core REQUIRED)
+
+# Include Qt headers.
+include_directories(${Qt5Base_INCLUDE_DIRS})
+# include_directories(${Qt5Network_INCLUDE_DIRS})
+
+SET(LIBUTIL_SOURCES
+include/libutil_config.h
+
+include/pathutils.h
+src/pathutils.cpp
+
+include/osutils.h
+
+include/userutils.h
+src/userutils.cpp
+
+include/cmdutils.h
+src/cmdutils.cpp
+)
+
+# Set the include dir path.
+SET(LIBUTIL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
+
+# Static link!
+ADD_DEFINITIONS(-DLIBUTIL_STATIC)
+
+add_definitions(-DLIBUTIL_LIBRARY)
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+IF(MultiMC_CODE_COVERAGE)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 --coverage")
+ SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O0 --coverage")
+ SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
+ENDIF(MultiMC_CODE_COVERAGE)
+
+add_library(libUtil STATIC ${LIBUTIL_SOURCES})
+# qt5_use_modules(libUtil Core Network)
+qt5_use_modules(libUtil Core)
+target_link_libraries(libUtil)
diff --git a/depends/util/include/cmdutils.h b/depends/util/include/cmdutils.h
new file mode 100644
index 00000000..4705f3ca
--- /dev/null
+++ b/depends/util/include/cmdutils.h
@@ -0,0 +1,255 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <exception>
+#include <stdexcept>
+
+#include <QString>
+#include <QVariant>
+#include <QHash>
+#include <QStringList>
+
+#include "libutil_config.h"
+
+/**
+ * @file libutil/include/cmdutils.h
+ * @brief commandline parsing and processing utilities
+ */
+
+namespace Util
+{
+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
+ */
+LIBUTIL_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 LIBUTIL_EXPORT ParsingError : public std::runtime_error
+{
+public:
+ ParsingError(const QString &what);
+};
+
+/**
+ * @brief The Parser class
+ */
+class LIBUTIL_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<QString, QVariant> 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<QString, OptionDef *> m_options;
+ QHash<QChar, OptionDef *> m_flags;
+ QHash<QString, CommonDef *> m_params;
+ QList<PositionalDef *> m_positionals;
+ QList<OptionDef *> m_optionList;
+
+ void getPrefix(QString &opt, QString &flag);
+};
+}
+}
diff --git a/depends/util/include/libutil_config.h b/depends/util/include/libutil_config.h
new file mode 100644
index 00000000..56b33b74
--- /dev/null
+++ b/depends/util/include/libutil_config.h
@@ -0,0 +1,28 @@
+/* Copyright 2013 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 <QtCore/QtGlobal>
+
+#ifdef LIBUTIL_STATIC
+#define LIBUTIL_EXPORT
+#else
+#ifdef LIBUTIL_LIBRARY
+#define LIBUTIL_EXPORT Q_DECL_EXPORT
+#else
+#define LIBUTIL_EXPORT Q_DECL_IMPORT
+#endif
+#endif
diff --git a/depends/util/include/osutils.h b/depends/util/include/osutils.h
new file mode 100644
index 00000000..d615d31f
--- /dev/null
+++ b/depends/util/include/osutils.h
@@ -0,0 +1,26 @@
+/* Copyright 2013 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 <QString>
+
+#if defined _WIN32 | defined _WIN64
+#define WINDOWS 1
+#elif __APPLE__ &__MACH__
+#define OSX 1
+#elif __linux__
+#define LINUX 1
+#endif
diff --git a/depends/util/include/pathutils.h b/depends/util/include/pathutils.h
new file mode 100644
index 00000000..45c2a6de
--- /dev/null
+++ b/depends/util/include/pathutils.h
@@ -0,0 +1,59 @@
+/* Copyright 2013 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 <QString>
+
+#include "libutil_config.h"
+
+LIBUTIL_EXPORT QString PathCombine(QString path1, QString path2);
+LIBUTIL_EXPORT QString PathCombine(QString path1, QString path2, QString path3);
+
+LIBUTIL_EXPORT QString AbsolutePath(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)
+ */
+QString NormalizePath(QString path);
+
+LIBUTIL_EXPORT QString RemoveInvalidFilenameChars(QString string, QChar replaceWith = '-');
+
+LIBUTIL_EXPORT QString DirNameFromString(QString string, QString inDir = ".");
+
+/**
+ * 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!
+ */
+LIBUTIL_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!
+ */
+LIBUTIL_EXPORT bool ensureFolderPathExists(QString filenamepath);
+
+LIBUTIL_EXPORT bool copyPath(QString src, QString dst);
+
+/// Opens the given file in the default application.
+LIBUTIL_EXPORT void openFileInDefaultProgram(QString filename);
+
+/// Opens the given directory in the default application.
+LIBUTIL_EXPORT void openDirInDefaultProgram(QString dirpath, bool ensureExists = false);
diff --git a/depends/util/include/userutils.h b/depends/util/include/userutils.h
new file mode 100644
index 00000000..6ce08bce
--- /dev/null
+++ b/depends/util/include/userutils.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <QString>
+
+#include "libutil_config.h"
+
+namespace Util
+{
+// Get the Directory representing the User's Desktop
+LIBUTIL_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
+LIBUTIL_EXPORT bool createShortCut(QString location, QString dest, QStringList args,
+ QString name, QString iconLocation);
+}
diff --git a/depends/util/src/cmdutils.cpp b/depends/util/src/cmdutils.cpp
new file mode 100644
index 00000000..b12098dc
--- /dev/null
+++ b/depends/util/src/cmdutils.cpp
@@ -0,0 +1,486 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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/cmdutils.h"
+
+/**
+ * @file libutil/src/cmdutils.cpp
+ */
+
+namespace Util
+{
+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<PositionalDef *> 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<OptionDef *> 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<OptionDef *> 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<PositionalDef *> it2(m_positionals);
+ while (it2.hasNext())
+ {
+ PositionalDef *param = it2.next();
+ usage << " " << (param->required ? "<" : "[");
+ usage << param->metavar;
+ usage << (param->required ? ">" : "]");
+ }
+
+ return usage.join("");
+}
+
+// parsing
+QHash<QString, QVariant> Parser::parse(QStringList argv)
+{
+ QHash<QString, QVariant> map;
+
+ QStringListIterator it(argv);
+ QString programName = it.next();
+
+ QString optionPrefix;
+ QString flagPrefix;
+ QListIterator<PositionalDef *> 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<OptionDef *> 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<OptionDef *> it(m_optionList);
+ while (it.hasNext())
+ {
+ OptionDef *option = it.next();
+ it.remove();
+ delete option;
+ }
+
+ QMutableListIterator<PositionalDef *> 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())
+{
+}
+}
+}
diff --git a/depends/util/src/pathutils.cpp b/depends/util/src/pathutils.cpp
new file mode 100644
index 00000000..20888754
--- /dev/null
+++ b/depends/util/src/pathutils.cpp
@@ -0,0 +1,147 @@
+/* Copyright 2013 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/pathutils.h"
+
+#include <QFileInfo>
+#include <QDir>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QDebug>
+
+QString PathCombine(QString path1, QString path2)
+{
+ 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();
+}
+
+/**
+ * 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 dirName = RemoveInvalidFilenameChars(string, '-');
+ while (QFileInfo(PathCombine(inDir, dirName)).exists())
+ {
+ num++;
+ dirName = RemoveInvalidFilenameChars(dirName, '-') + QString::number(num);
+
+ // If it's over 9000
+ if (num > 9000)
+ return "";
+ }
+ return dirName;
+}
+
+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 copyPath(QString src, QString dst)
+{
+ QDir dir(src);
+ if (!dir.exists())
+ return false;
+ if (!ensureFolderPathExists(dst))
+ return false;
+
+ foreach(QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
+ {
+ QString inner_src = src + QDir::separator() + d;
+ QString inner_dst = dst + QDir::separator() + d;
+ copyPath(inner_src, inner_dst);
+ }
+
+ foreach(QString f, dir.entryList(QDir::Files))
+ {
+ QFile::copy(src + QDir::separator() + f, dst + QDir::separator() + f);
+ }
+ return true;
+}
+
+void openDirInDefaultProgram(QString path, bool ensureExists)
+{
+ QDir parentPath;
+ QDir dir(path);
+ if (!dir.exists())
+ {
+ parentPath.mkpath(dir.absolutePath());
+ }
+ QDesktopServices::openUrl("file:///" + dir.absolutePath());
+}
+
+void openFileInDefaultProgram(QString filename)
+{
+ QDesktopServices::openUrl("file:///" + QFileInfo(filename).absolutePath());
+}
diff --git a/depends/util/src/userutils.cpp b/depends/util/src/userutils.cpp
new file mode 100644
index 00000000..060a58e9
--- /dev/null
+++ b/depends/util/src/userutils.cpp
@@ -0,0 +1,127 @@
+#include "include/userutils.h"
+
+#include <QStandardPaths>
+#include <QFile>
+#include <QTextStream>
+
+#include "include/osutils.h"
+#include "include/pathutils.h"
+
+// Win32 crap
+#if WINDOWS
+
+#include <windows.h>
+#include <winnls.h>
+#include <shobjidl.h>
+#include <objbase.h>
+#include <objidl.h>
+#include <shlguid.h>
+#include <shlobj.h>
+
+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 Util::getDesktopDir()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
+}
+
+// Cross-platform Shortcut creation
+bool Util::createShortCut(QString location, QString dest, QStringList args, QString name,
+ QString icon)
+{
+#if LINUX
+ location = PathCombine(location, name + ".desktop");
+ qDebug("location: %s", qPrintable(location));
+
+ 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 WINDOWS
+ // 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/depends/xz-embedded/CMakeLists.txt b/depends/xz-embedded/CMakeLists.txt
new file mode 100644
index 00000000..d4987f76
--- /dev/null
+++ b/depends/xz-embedded/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 2.6)
+project(xz-embedded)
+
+option(XZ_BUILD_BCJ "Build xz-embedded with BCJ support (native binary optimization)" OFF)
+option(XZ_BUILD_CRC64 "Build xz-embedded with CRC64 checksum support" ON)
+option(XZ_BUILD_MINIDEC "Build a tiny utility that decompresses xz streams" OFF)
+
+set(CMAKE_C_FLAGS "-std=c99")
+
+include_directories(include)
+SET(XZ_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
+
+# See include/xz.h for manual feature configuration
+# tweak this list and xz.h to fit your needs
+
+set(XZ_SOURCES
+include/xz.h
+src/xz_config.h
+src/xz_crc32.c
+src/xz_crc64.c
+src/xz_dec_lzma2.c
+src/xz_dec_stream.c
+src/xz_lzma2.h
+src/xz_private.h
+src/xz_stream.h
+# src/xz_dec_bcj.c
+)
+# TODO: look into what would be needed for plain old lzma
+
+add_library(xz-embedded STATIC ${XZ_SOURCES})
+add_executable(xzminidec xzminidec.c)
+target_link_libraries(xzminidec xz-embedded)
diff --git a/depends/xz-embedded/include/xz.h b/depends/xz-embedded/include/xz.h
new file mode 100644
index 00000000..eef8ef69
--- /dev/null
+++ b/depends/xz-embedded/include/xz.h
@@ -0,0 +1,321 @@
+/*
+ * XZ decompressor
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_H
+#define XZ_H
+
+#ifdef __KERNEL__
+#include <linux/stddef.h>
+#include <linux/types.h>
+#else
+#include <stddef.h>
+#include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Definitions that determine available features */
+#define XZ_DEC_ANY_CHECK 1
+#define XZ_USE_CRC64 1
+
+// native machine code compression stuff
+/*
+#define XZ_DEC_X86
+#define XZ_DEC_POWERPC
+#define XZ_DEC_IA64
+#define XZ_DEC_ARM
+#define XZ_DEC_ARMTHUMB
+#define XZ_DEC_SPARC
+*/
+
+/* In Linux, this is used to make extern functions static when needed. */
+#ifndef XZ_EXTERN
+#define XZ_EXTERN extern
+#endif
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE: Single-call mode. This uses less RAM than
+ * than multi-call modes, because the LZMA2
+ * dictionary doesn't need to be allocated as
+ * part of the decoder state. All required data
+ * structures are allocated at initialization,
+ * so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2
+ * dictionary buffer. All data structures are
+ * allocated at initialization, so xz_dec_run()
+ * cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is
+ * allocated once the required size has been
+ * parsed from the stream headers. If the
+ * allocation fails, xz_dec_run() will return
+ * XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
+ * with support for all operation modes, but the preboot code may
+ * be built with fewer features to minimize code size.
+ */
+enum xz_mode
+{
+ XZ_SINGLE,
+ XZ_PREALLOC,
+ XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK: Everything is OK so far. More input or more
+ * output space is required to continue. This
+ * return code is possible only in multi-call mode
+ * (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END: Operation finished successfully.
+ * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding
+ * is still possible in multi-call mode by simply
+ * calling xz_dec_run() again.
+ * Note that this return value is used only if
+ * XZ_DEC_ANY_CHECK was defined at build time,
+ * which is not used in the kernel. Unsupported
+ * check types return XZ_OPTIONS_ERROR if
+ * XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR: Allocating memory failed. This return code is
+ * possible only if the decoder was initialized
+ * with XZ_DYNALLOC. The amount of memory that was
+ * tried to be allocated was no more than the
+ * dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than
+ * allowed by the dict_max argument given to
+ * xz_dec_init(). This return value is possible
+ * only in multi-call mode (XZ_PREALLOC or
+ * XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ * ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic
+ * bytes).
+ * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested
+ * compression options. In the decoder this means
+ * that the header CRC32 matches, but the header
+ * itself specifies something that we don't support.
+ * @XZ_DATA_ERROR: Compressed data is corrupt.
+ * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly
+ * different between multi-call and single-call
+ * mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * stream that is truncated or otherwise corrupt.
+ *
+ * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
+ * is too small or the compressed input is corrupt in a way that makes the
+ * decoder produce more output than the caller expected. When it is
+ * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
+ * is used instead of XZ_BUF_ERROR.
+ */
+enum xz_ret
+{
+ XZ_OK,
+ XZ_STREAM_END,
+ XZ_UNSUPPORTED_CHECK,
+ XZ_MEM_ERROR,
+ XZ_MEMLIMIT_ERROR,
+ XZ_FORMAT_ERROR,
+ XZ_OPTIONS_ERROR,
+ XZ_DATA_ERROR,
+ XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in: Beginning of the input buffer. This may be NULL if and only
+ * if in_pos is equal to in_size.
+ * @in_pos: Current position in the input buffer. This must not exceed
+ * in_size.
+ * @in_size: Size of the input buffer
+ * @out: Beginning of the output buffer. This may be NULL if and only
+ * if out_pos is equal to out_size.
+ * @out_pos: Current position in the output buffer. This must not exceed
+ * out_size.
+ * @out_size: Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf
+{
+ const uint8_t *in;
+ size_t in_pos;
+ size_t in_size;
+
+ uint8_t *out;
+ size_t out_pos;
+ size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode: Operation mode
+ * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for
+ * multi-call decoding. This is ignored in single-call mode
+ * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ * or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ * in practice), so other values for dict_max don't make sense.
+ * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ * 512 KiB, and 1 MiB are probably the only reasonable values,
+ * except for kernel and initramfs images where a bigger
+ * dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s: Decoder state allocated using xz_dec_init()
+ * @b: Input and output buffers
+ *
+ * The possible return values depend on build options and operation mode.
+ * See enum xz_ret for details.
+ *
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
+ * contents of the output buffer from b->out[b->out_pos] onward are
+ * undefined. This is true even after XZ_BUF_ERROR, because with some filter
+ * chains, there may be a second pass over the output buffer, and this pass
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s: Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s: Decoder state allocated using xz_dec_init(). If s is NULL,
+ * this function does nothing.
+ */
+XZ_EXTERN void xz_dec_end(struct xz_dec *s);
+
+/*
+ * Standalone build (userspace build or in-kernel build for boot time use)
+ * needs a CRC32 implementation. For normal in-kernel use, kernel's own
+ * CRC32 module is used instead, and users of this module don't need to
+ * care about the functions below.
+ */
+#ifndef XZ_INTERNAL_CRC32
+#ifdef __KERNEL__
+#define XZ_INTERNAL_CRC32 0
+#else
+#define XZ_INTERNAL_CRC32 1
+#endif
+#endif
+
+/*
+ * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64
+ * implementation is needed too.
+ */
+#ifndef XZ_USE_CRC64
+#undef XZ_INTERNAL_CRC64
+#define XZ_INTERNAL_CRC64 0
+#endif
+#ifndef XZ_INTERNAL_CRC64
+#ifdef __KERNEL__
+#error Using CRC64 in the kernel has not been implemented.
+#else
+#define XZ_INTERNAL_CRC64 1
+#endif
+#endif
+
+#if XZ_INTERNAL_CRC32
+/*
+ * This must be called before any other xz_* function to initialize
+ * the CRC32 lookup table.
+ */
+XZ_EXTERN void xz_crc32_init(void);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);
+#endif
+
+#if XZ_INTERNAL_CRC64
+/*
+ * This must be called before any other xz_* function (except xz_crc32_init())
+ * to initialize the CRC64 lookup table.
+ */
+XZ_EXTERN void xz_crc64_init(void);
+
+/*
+ * Update CRC64 value using the polynomial from ECMA-182. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/depends/xz-embedded/src/xz_config.h b/depends/xz-embedded/src/xz_config.h
new file mode 100644
index 00000000..40805b75
--- /dev/null
+++ b/depends/xz-embedded/src/xz_config.h
@@ -0,0 +1,119 @@
+/*
+ * Private includes and definitions for userspace use of XZ Embedded
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_CONFIG_H
+#define XZ_CONFIG_H
+
+/* Uncomment to enable CRC64 support. */
+/* #define XZ_USE_CRC64 */
+
+/* Uncomment as needed to enable BCJ filter decoders. */
+/* #define XZ_DEC_X86 */
+/* #define XZ_DEC_POWERPC */
+/* #define XZ_DEC_IA64 */
+/* #define XZ_DEC_ARM */
+/* #define XZ_DEC_ARMTHUMB */
+/* #define XZ_DEC_SPARC */
+
+/*
+ * MSVC doesn't support modern C but XZ Embedded is mostly C89
+ * so these are enough.
+ */
+#ifdef _MSC_VER
+typedef unsigned char bool;
+#define true 1
+#define false 0
+#define inline __inline
+#else
+#include <stdbool.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xz.h"
+
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) free(ptr)
+
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#define memzero(buf, size) memset(buf, 0, size)
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#define min_t(type, x, y) min(x, y)
+
+/*
+ * Some functions have been marked with __always_inline to keep the
+ * performance reasonable even when the compiler is optimizing for
+ * small code size. You may be able to save a few bytes by #defining
+ * __always_inline to plain inline, but don't complain if the code
+ * becomes slow.
+ *
+ * NOTE: System headers on GNU/Linux may #define this macro already,
+ * so if you want to change it, you need to #undef it first.
+ */
+#ifndef __always_inline
+#ifdef __GNUC__
+#define __always_inline inline __attribute__((__always_inline__))
+#else
+#define __always_inline inline
+#endif
+#endif
+
+/* Inline functions to access unaligned unsigned 32-bit integers */
+#ifndef get_unaligned_le32
+static inline uint32_t get_unaligned_le32(const uint8_t *buf)
+{
+ return (uint32_t)buf[0] | ((uint32_t)buf[1] << 8) | ((uint32_t)buf[2] << 16) |
+ ((uint32_t)buf[3] << 24);
+}
+#endif
+
+#ifndef get_unaligned_be32
+static inline uint32_t get_unaligned_be32(const uint8_t *buf)
+{
+ return (uint32_t)(buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) |
+ (uint32_t)buf[3];
+}
+#endif
+
+#ifndef put_unaligned_le32
+static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
+{
+ buf[0] = (uint8_t)val;
+ buf[1] = (uint8_t)(val >> 8);
+ buf[2] = (uint8_t)(val >> 16);
+ buf[3] = (uint8_t)(val >> 24);
+}
+#endif
+
+#ifndef put_unaligned_be32
+static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
+{
+ buf[0] = (uint8_t)(val >> 24);
+ buf[1] = (uint8_t)(val >> 16);
+ buf[2] = (uint8_t)(val >> 8);
+ buf[3] = (uint8_t)val;
+}
+#endif
+
+/*
+ * Use get_unaligned_le32() also for aligned access for simplicity. On
+ * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
+ * could save a few bytes in code size.
+ */
+#ifndef get_le32
+#define get_le32 get_unaligned_le32
+#endif
+
+#endif
diff --git a/depends/xz-embedded/src/xz_crc32.c b/depends/xz-embedded/src/xz_crc32.c
new file mode 100644
index 00000000..c412662b
--- /dev/null
+++ b/depends/xz-embedded/src/xz_crc32.c
@@ -0,0 +1,61 @@
+/*
+ * CRC32 using the polynomial from IEEE-802.3
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is not the fastest implementation, but it is pretty compact.
+ * The fastest versions of xz_crc32() on modern CPUs without hardware
+ * accelerated CRC instruction are 3-5 times as fast as this version,
+ * but they are bigger and use more memory for the lookup table.
+ */
+
+#include "xz_private.h"
+
+/*
+ * STATIC_RW_DATA is used in the pre-boot environment on some architectures.
+ * See <linux/decompress/mm.h> for details.
+ */
+#ifndef STATIC_RW_DATA
+#define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint32_t xz_crc32_table[256];
+
+XZ_EXTERN void xz_crc32_init(void)
+{
+ const uint32_t poly = 0xEDB88320;
+
+ uint32_t i;
+ uint32_t j;
+ uint32_t r;
+
+ for (i = 0; i < 256; ++i)
+ {
+ r = i;
+ for (j = 0; j < 8; ++j)
+ r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+ xz_crc32_table[i] = r;
+ }
+
+ return;
+}
+
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+{
+ crc = ~crc;
+
+ while (size != 0)
+ {
+ crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+ --size;
+ }
+
+ return ~crc;
+}
diff --git a/depends/xz-embedded/src/xz_crc64.c b/depends/xz-embedded/src/xz_crc64.c
new file mode 100644
index 00000000..4794b9d3
--- /dev/null
+++ b/depends/xz-embedded/src/xz_crc64.c
@@ -0,0 +1,52 @@
+/*
+ * CRC64 using the polynomial from ECMA-182
+ *
+ * This file is similar to xz_crc32.c. See the comments there.
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+#ifndef STATIC_RW_DATA
+#define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint64_t xz_crc64_table[256];
+
+XZ_EXTERN void xz_crc64_init(void)
+{
+ const uint64_t poly = 0xC96C5795D7870F42;
+
+ uint32_t i;
+ uint32_t j;
+ uint64_t r;
+
+ for (i = 0; i < 256; ++i)
+ {
+ r = i;
+ for (j = 0; j < 8; ++j)
+ r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+ xz_crc64_table[i] = r;
+ }
+
+ return;
+}
+
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc)
+{
+ crc = ~crc;
+
+ while (size != 0)
+ {
+ crc = xz_crc64_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+ --size;
+ }
+
+ return ~crc;
+}
diff --git a/depends/xz-embedded/src/xz_dec_bcj.c b/depends/xz-embedded/src/xz_dec_bcj.c
new file mode 100644
index 00000000..9ffda3bd
--- /dev/null
+++ b/depends/xz-embedded/src/xz_dec_bcj.c
@@ -0,0 +1,588 @@
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+/*
+ * The rest of the file is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj
+{
+ /* Type of the BCJ filter being used */
+ enum
+ {
+ BCJ_X86 = 4, /* x86 or x86-64 */
+ BCJ_POWERPC = 5, /* Big endian only */
+ BCJ_IA64 = 6, /* Big or little endian */
+ BCJ_ARM = 7, /* Little endian only */
+ BCJ_ARMTHUMB = 8, /* Little endian only */
+ BCJ_SPARC = 9 /* Big or little endian */
+ } type;
+
+ /*
+ * Return value of the next filter in the chain. We need to preserve
+ * this information across calls, because we must not call the next
+ * filter anymore once it has returned XZ_STREAM_END.
+ */
+ enum xz_ret ret;
+
+ /* True if we are operating in single-call mode. */
+ bool single_call;
+
+ /*
+ * Absolute position relative to the beginning of the uncompressed
+ * data (in a single .xz Block). We care only about the lowest 32
+ * bits so this doesn't need to be uint64_t even with big files.
+ */
+ uint32_t pos;
+
+ /* x86 filter state */
+ uint32_t x86_prev_mask;
+
+ /* Temporary space to hold the variables from struct xz_buf */
+ uint8_t *out;
+ size_t out_pos;
+ size_t out_size;
+
+ struct
+ {
+ /* Amount of already filtered data in the beginning of buf */
+ size_t filtered;
+
+ /* Total amount of data currently stored in buf */
+ size_t size;
+
+ /*
+ * Buffer to hold a mix of filtered and unfiltered data. This
+ * needs to be big enough to hold Alignment + 2 * Look-ahead:
+ *
+ * Type Alignment Look-ahead
+ * x86 1 4
+ * PowerPC 4 0
+ * IA-64 16 0
+ * ARM 4 0
+ * ARM-Thumb 2 2
+ * SPARC 4 0
+ */
+ uint8_t buf[16];
+ } temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is used to test the most significant byte of a memory address
+ * in an x86 instruction.
+ */
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+ return b == 0x00 || b == 0xFF;
+}
+
+static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ static const bool mask_to_allowed_status[8] = {true, true, true, false,
+ true, false, false, false};
+
+ static const uint8_t mask_to_bit_num[8] = {0, 1, 2, 2, 3, 3, 3, 3};
+
+ size_t i;
+ size_t prev_pos = (size_t) - 1;
+ uint32_t prev_mask = s->x86_prev_mask;
+ uint32_t src;
+ uint32_t dest;
+ uint32_t j;
+ uint8_t b;
+
+ if (size <= 4)
+ return 0;
+
+ size -= 4;
+ for (i = 0; i < size; ++i)
+ {
+ if ((buf[i] & 0xFE) != 0xE8)
+ continue;
+
+ prev_pos = i - prev_pos;
+ if (prev_pos > 3)
+ {
+ prev_mask = 0;
+ }
+ else
+ {
+ prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+ if (prev_mask != 0)
+ {
+ b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+ if (!mask_to_allowed_status[prev_mask] || bcj_x86_test_msbyte(b))
+ {
+ prev_pos = i;
+ prev_mask = (prev_mask << 1) | 1;
+ continue;
+ }
+ }
+ }
+
+ prev_pos = i;
+
+ if (bcj_x86_test_msbyte(buf[i + 4]))
+ {
+ src = get_unaligned_le32(buf + i + 1);
+ while (true)
+ {
+ dest = src - (s->pos + (uint32_t)i + 5);
+ if (prev_mask == 0)
+ break;
+
+ j = mask_to_bit_num[prev_mask] * 8;
+ b = (uint8_t)(dest >> (24 - j));
+ if (!bcj_x86_test_msbyte(b))
+ break;
+
+ src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+ }
+
+ dest &= 0x01FFFFFF;
+ dest |= (uint32_t)0 - (dest & 0x01000000);
+ put_unaligned_le32(dest, buf + i + 1);
+ i += 4;
+ }
+ else
+ {
+ prev_mask = (prev_mask << 1) | 1;
+ }
+ }
+
+ prev_pos = i - prev_pos;
+ s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+
+ for (i = 0; i + 4 <= size; i += 4)
+ {
+ instr = get_unaligned_be32(buf + i);
+ if ((instr & 0xFC000003) == 0x48000001)
+ {
+ instr &= 0x03FFFFFC;
+ instr -= s->pos + (uint32_t)i;
+ instr &= 0x03FFFFFC;
+ instr |= 0x48000001;
+ put_unaligned_be32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ static const uint8_t branch_table[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0};
+
+ /*
+ * The local variables take a little bit stack space, but it's less
+ * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+ * stack usage here without doing that for the LZMA2 decoder too.
+ */
+
+ /* Loop counters */
+ size_t i;
+ size_t j;
+
+ /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+ uint32_t slot;
+
+ /* Bitwise offset of the instruction indicated by slot */
+ uint32_t bit_pos;
+
+ /* bit_pos split into byte and bit parts */
+ uint32_t byte_pos;
+ uint32_t bit_res;
+
+ /* Address part of an instruction */
+ uint32_t addr;
+
+ /* Mask used to detect which instructions to convert */
+ uint32_t mask;
+
+ /* 41-bit instruction stored somewhere in the lowest 48 bits */
+ uint64_t instr;
+
+ /* Instruction normalized with bit_res for easier manipulation */
+ uint64_t norm;
+
+ for (i = 0; i + 16 <= size; i += 16)
+ {
+ mask = branch_table[buf[i] & 0x1F];
+ for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41)
+ {
+ if (((mask >> slot) & 1) == 0)
+ continue;
+
+ byte_pos = bit_pos >> 3;
+ bit_res = bit_pos & 7;
+ instr = 0;
+ for (j = 0; j < 6; ++j)
+ instr |= (uint64_t)(buf[i + j + byte_pos]) << (8 * j);
+
+ norm = instr >> bit_res;
+
+ if (((norm >> 37) & 0x0F) == 0x05 && ((norm >> 9) & 0x07) == 0)
+ {
+ addr = (norm >> 13) & 0x0FFFFF;
+ addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+ addr <<= 4;
+ addr -= s->pos + (uint32_t)i;
+ addr >>= 4;
+
+ norm &= ~((uint64_t)0x8FFFFF << 13);
+ norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+ norm |= (uint64_t)(addr & 0x100000) << (36 - 20);
+
+ instr &= (1 << bit_res) - 1;
+ instr |= norm << bit_res;
+
+ for (j = 0; j < 6; j++)
+ buf[i + j + byte_pos] = (uint8_t)(instr >> (8 * j));
+ }
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4)
+ {
+ if (buf[i + 3] == 0xEB)
+ {
+ addr =
+ (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) | ((uint32_t)buf[i + 2] << 16);
+ addr <<= 2;
+ addr -= s->pos + (uint32_t)i + 8;
+ addr >>= 2;
+ buf[i] = (uint8_t)addr;
+ buf[i + 1] = (uint8_t)(addr >> 8);
+ buf[i + 2] = (uint8_t)(addr >> 16);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 2)
+ {
+ if ((buf[i + 1] & 0xF8) == 0xF0 && (buf[i + 3] & 0xF8) == 0xF8)
+ {
+ addr = (((uint32_t)buf[i + 1] & 0x07) << 19) | ((uint32_t)buf[i] << 11) |
+ (((uint32_t)buf[i + 3] & 0x07) << 8) | (uint32_t)buf[i + 2];
+ addr <<= 1;
+ addr -= s->pos + (uint32_t)i + 4;
+ addr >>= 1;
+ buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+ buf[i] = (uint8_t)(addr >> 11);
+ buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+ buf[i + 2] = (uint8_t)addr;
+ i += 2;
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+
+ for (i = 0; i + 4 <= size; i += 4)
+ {
+ instr = get_unaligned_be32(buf + i);
+ if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF)
+ {
+ instr <<= 2;
+ instr -= s->pos + (uint32_t)i;
+ instr >>= 2;
+ instr =
+ ((uint32_t)0x40000000 - (instr & 0x400000)) | 0x40000000 | (instr & 0x3FFFFF);
+ put_unaligned_be32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void bcj_apply(struct xz_dec_bcj *s, uint8_t *buf, size_t *pos, size_t size)
+{
+ size_t filtered;
+
+ buf += *pos;
+ size -= *pos;
+
+ switch (s->type)
+ {
+#ifdef XZ_DEC_X86
+ case BCJ_X86:
+ filtered = bcj_x86(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_POWERPC
+ case BCJ_POWERPC:
+ filtered = bcj_powerpc(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_IA64
+ case BCJ_IA64:
+ filtered = bcj_ia64(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_ARM
+ case BCJ_ARM:
+ filtered = bcj_arm(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+ case BCJ_ARMTHUMB:
+ filtered = bcj_armthumb(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_SPARC
+ case BCJ_SPARC:
+ filtered = bcj_sparc(s, buf, size);
+ break;
+#endif
+ default:
+ /* Never reached but silence compiler warnings. */
+ filtered = 0;
+ break;
+ }
+
+ *pos += filtered;
+ s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+ size_t copy_size;
+
+ copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+ memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+ b->out_pos += copy_size;
+
+ s->temp.filtered -= copy_size;
+ s->temp.size -= copy_size;
+ memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b)
+{
+ size_t out_start;
+
+ /*
+ * Flush pending already filtered data to the output buffer. Return
+ * immediatelly if we couldn't flush everything, or if the next
+ * filter in the chain had already returned XZ_STREAM_END.
+ */
+ if (s->temp.filtered > 0)
+ {
+ bcj_flush(s, b);
+ if (s->temp.filtered > 0)
+ return XZ_OK;
+
+ if (s->ret == XZ_STREAM_END)
+ return XZ_STREAM_END;
+ }
+
+ /*
+ * If we have more output space than what is currently pending in
+ * temp, copy the unfiltered data from temp to the output buffer
+ * and try to fill the output buffer by decoding more data from the
+ * next filter in the chain. Apply the BCJ filter on the new data
+ * in the output buffer. If everything cannot be filtered, copy it
+ * to temp and rewind the output buffer position accordingly.
+ *
+ * This needs to be always run when temp.size == 0 to handle a special
+ * case where the output buffer is full and the next filter has no
+ * more output coming but hasn't returned XZ_STREAM_END yet.
+ */
+ if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0)
+ {
+ out_start = b->out_pos;
+ memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+ b->out_pos += s->temp.size;
+
+ s->ret = xz_dec_lzma2_run(lzma2, b);
+ if (s->ret != XZ_STREAM_END && (s->ret != XZ_OK || s->single_call))
+ return s->ret;
+
+ bcj_apply(s, b->out, &out_start, b->out_pos);
+
+ /*
+ * As an exception, if the next filter returned XZ_STREAM_END,
+ * we can do that too, since the last few bytes that remain
+ * unfiltered are meant to remain unfiltered.
+ */
+ if (s->ret == XZ_STREAM_END)
+ return XZ_STREAM_END;
+
+ s->temp.size = b->out_pos - out_start;
+ b->out_pos -= s->temp.size;
+ memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+
+ /*
+ * If there wasn't enough input to the next filter to fill
+ * the output buffer with unfiltered data, there's no point
+ * to try decoding more data to temp.
+ */
+ if (b->out_pos + s->temp.size < b->out_size)
+ return XZ_OK;
+ }
+
+ /*
+ * We have unfiltered data in temp. If the output buffer isn't full
+ * yet, try to fill the temp buffer by decoding more data from the
+ * next filter. Apply the BCJ filter on temp. Then we hopefully can
+ * fill the actual output buffer by copying filtered data from temp.
+ * A mix of filtered and unfiltered data may be left in temp; it will
+ * be taken care on the next call to this function.
+ */
+ if (b->out_pos < b->out_size)
+ {
+ /* Make b->out{,_pos,_size} temporarily point to s->temp. */
+ s->out = b->out;
+ s->out_pos = b->out_pos;
+ s->out_size = b->out_size;
+ b->out = s->temp.buf;
+ b->out_pos = s->temp.size;
+ b->out_size = sizeof(s->temp.buf);
+
+ s->ret = xz_dec_lzma2_run(lzma2, b);
+
+ s->temp.size = b->out_pos;
+ b->out = s->out;
+ b->out_pos = s->out_pos;
+ b->out_size = s->out_size;
+
+ if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+ return s->ret;
+
+ bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+ /*
+ * If the next filter returned XZ_STREAM_END, we mark that
+ * everything is filtered, since the last unfiltered bytes
+ * of the stream are meant to be left as is.
+ */
+ if (s->ret == XZ_STREAM_END)
+ s->temp.filtered = s->temp.size;
+
+ bcj_flush(s, b);
+ if (s->temp.filtered > 0)
+ return XZ_OK;
+ }
+
+ return s->ret;
+}
+
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
+{
+ struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s != NULL)
+ s->single_call = single_call;
+
+ return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+{
+ switch (id)
+ {
+#ifdef XZ_DEC_X86
+ case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+ case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+ case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+ case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+ case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+ case BCJ_SPARC:
+#endif
+ break;
+
+ default:
+ /* Unsupported Filter ID */
+ return XZ_OPTIONS_ERROR;
+ }
+
+ s->type = id;
+ s->ret = XZ_OK;
+ s->pos = 0;
+ s->x86_prev_mask = 0;
+ s->temp.filtered = 0;
+ s->temp.size = 0;
+
+ return XZ_OK;
+}
+
+#endif
diff --git a/depends/xz-embedded/src/xz_dec_lzma2.c b/depends/xz-embedded/src/xz_dec_lzma2.c
new file mode 100644
index 00000000..3d7b9a2e
--- /dev/null
+++ b/depends/xz-embedded/src/xz_dec_lzma2.c
@@ -0,0 +1,1231 @@
+/*
+ * LZMA2 decoder
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_lzma2.h"
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ * start <= pos <= full <= end
+ * pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ * end == size
+ * size <= size_max
+ * allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary
+{
+ /* Beginning of the history buffer */
+ uint8_t *buf;
+
+ /* Old position in buf (before decoding more data) */
+ size_t start;
+
+ /* Position in buf */
+ size_t pos;
+
+ /*
+ * How full dictionary is. This is used to detect corrupt input that
+ * would read beyond the beginning of the uncompressed stream.
+ */
+ size_t full;
+
+ /* Write limit; we don't write to buf[limit] or later bytes. */
+ size_t limit;
+
+ /*
+ * End of the dictionary buffer. In multi-call mode, this is
+ * the same as the dictionary size. In single-call mode, this
+ * indicates the size of the output buffer.
+ */
+ size_t end;
+
+ /*
+ * Size of the dictionary as specified in Block Header. This is used
+ * together with "full" to detect corrupt input that would make us
+ * read beyond the beginning of the uncompressed stream.
+ */
+ uint32_t size;
+
+ /*
+ * Maximum allowed dictionary size in multi-call mode.
+ * This is ignored in single-call mode.
+ */
+ uint32_t size_max;
+
+ /*
+ * Amount of memory currently allocated for the dictionary.
+ * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+ * size_max is always the same as the allocated size.)
+ */
+ uint32_t allocated;
+
+ /* Operation mode */
+ enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec
+{
+ uint32_t range;
+ uint32_t code;
+
+ /*
+ * Number of initializing bytes remaining to be read
+ * by rc_read_init().
+ */
+ uint32_t init_bytes_left;
+
+ /*
+ * Buffer from which we read our input. It can be either
+ * temp.buf or the caller-provided input buffer.
+ */
+ const uint8_t *in;
+ size_t in_pos;
+ size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec
+{
+ /* Probability of match length being at least 10 */
+ uint16_t choice;
+
+ /* Probability of match length being at least 18 */
+ uint16_t choice2;
+
+ /* Probabilities for match lengths 2-9 */
+ uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+ /* Probabilities for match lengths 10-17 */
+ uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+ /* Probabilities for match lengths 18-273 */
+ uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec
+{
+ /* Distances of latest four matches */
+ uint32_t rep0;
+ uint32_t rep1;
+ uint32_t rep2;
+ uint32_t rep3;
+
+ /* Types of the most recently seen LZMA symbols */
+ enum lzma_state state;
+
+ /*
+ * Length of a match. This is updated so that dict_repeat can
+ * be called again to finish repeating the whole match.
+ */
+ uint32_t len;
+
+ /*
+ * LZMA properties or related bit masks (number of literal
+ * context bits, a mask dervied from the number of literal
+ * position bits, and a mask dervied from the number
+ * position bits)
+ */
+ uint32_t lc;
+ uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+ uint32_t pos_mask; /* (1 << pb) - 1 */
+
+ /* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+ uint16_t is_match[STATES][POS_STATES_MAX];
+
+ /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+ uint16_t is_rep[STATES];
+
+ /*
+ * If 0, distance of a repeated match is rep0.
+ * Otherwise check is_rep1.
+ */
+ uint16_t is_rep0[STATES];
+
+ /*
+ * If 0, distance of a repeated match is rep1.
+ * Otherwise check is_rep2.
+ */
+ uint16_t is_rep1[STATES];
+
+ /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+ uint16_t is_rep2[STATES];
+
+ /*
+ * If 1, the repeated match has length of one byte. Otherwise
+ * the length is decoded from rep_len_decoder.
+ */
+ uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+ /*
+ * Probability tree for the highest two bits of the match
+ * distance. There is a separate probability tree for match
+ * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+ */
+ uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+ /*
+ * Probility trees for additional bits for match distance
+ * when the distance is in the range [4, 127].
+ */
+ uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+ /*
+ * Probability tree for the lowest four bits of a match
+ * distance that is equal to or greater than 128.
+ */
+ uint16_t dist_align[ALIGN_SIZE];
+
+ /* Length of a normal match */
+ struct lzma_len_dec match_len_dec;
+
+ /* Length of a repeated match */
+ struct lzma_len_dec rep_len_dec;
+
+ /* Probabilities of literals */
+ uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec
+{
+ /* Position in xz_dec_lzma2_run(). */
+ enum lzma2_seq
+ {
+ SEQ_CONTROL,
+ SEQ_UNCOMPRESSED_1,
+ SEQ_UNCOMPRESSED_2,
+ SEQ_COMPRESSED_0,
+ SEQ_COMPRESSED_1,
+ SEQ_PROPERTIES,
+ SEQ_LZMA_PREPARE,
+ SEQ_LZMA_RUN,
+ SEQ_COPY
+ } sequence;
+
+ /* Next position after decoding the compressed size of the chunk. */
+ enum lzma2_seq next_sequence;
+
+ /* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+ uint32_t uncompressed;
+
+ /*
+ * Compressed size of LZMA chunk or compressed/uncompressed
+ * size of uncompressed chunk (64 KiB at maximum)
+ */
+ uint32_t compressed;
+
+ /*
+ * True if dictionary reset is needed. This is false before
+ * the first chunk (LZMA or uncompressed).
+ */
+ bool need_dict_reset;
+
+ /*
+ * True if new LZMA properties are needed. This is false
+ * before the first LZMA chunk.
+ */
+ bool need_props;
+};
+
+struct xz_dec_lzma2
+{
+ /*
+ * The order below is important on x86 to reduce code size and
+ * it shouldn't hurt on other platforms. Everything up to and
+ * including lzma.pos_mask are in the first 128 bytes on x86-32,
+ * which allows using smaller instructions to access those
+ * variables. On x86-64, fewer variables fit into the first 128
+ * bytes, but this is still the best order without sacrificing
+ * the readability by splitting the structures.
+ */
+ struct rc_dec rc;
+ struct dictionary dict;
+ struct lzma2_dec lzma2;
+ struct lzma_dec lzma;
+
+ /*
+ * Temporary buffer which holds small number of input bytes between
+ * decoder calls. See lzma2_lzma() for details.
+ */
+ struct
+ {
+ uint32_t size;
+ uint8_t buf[3 * LZMA_IN_REQUIRED];
+ } temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+ if (DEC_IS_SINGLE(dict->mode))
+ {
+ dict->buf = b->out + b->out_pos;
+ dict->end = b->out_size - b->out_pos;
+ }
+
+ dict->start = 0;
+ dict->pos = 0;
+ dict->limit = 0;
+ dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void dict_limit(struct dictionary *dict, size_t out_max)
+{
+ if (dict->end - dict->pos <= out_max)
+ dict->limit = dict->end;
+ else
+ dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static inline bool dict_has_space(const struct dictionary *dict)
+{
+ return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)
+{
+ size_t offset = dict->pos - dist - 1;
+
+ if (dist >= dict->pos)
+ offset += dict->end;
+
+ return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void dict_put(struct dictionary *dict, uint8_t byte)
+{
+ dict->buf[dict->pos++] = byte;
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+ size_t back;
+ uint32_t left;
+
+ if (dist >= dict->full || dist >= dict->size)
+ return false;
+
+ left = min_t(size_t, dict->limit - dict->pos, *len);
+ *len -= left;
+
+ back = dict->pos - dist - 1;
+ if (dist >= dict->pos)
+ back += dict->end;
+
+ do
+ {
+ dict->buf[dict->pos++] = dict->buf[back++];
+ if (back == dict->end)
+ back = 0;
+ } while (--left > 0);
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+
+ return true;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b, uint32_t *left)
+{
+ size_t copy_size;
+
+ while (*left > 0 && b->in_pos < b->in_size && b->out_pos < b->out_size)
+ {
+ copy_size = min(b->in_size - b->in_pos, b->out_size - b->out_pos);
+ if (copy_size > dict->end - dict->pos)
+ copy_size = dict->end - dict->pos;
+ if (copy_size > *left)
+ copy_size = *left;
+
+ *left -= copy_size;
+
+ memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+ dict->pos += copy_size;
+
+ if (dict->full < dict->pos)
+ dict->full = dict->pos;
+
+ if (DEC_IS_MULTI(dict->mode))
+ {
+ if (dict->pos == dict->end)
+ dict->pos = 0;
+
+ memcpy(b->out + b->out_pos, b->in + b->in_pos, copy_size);
+ }
+
+ dict->start = dict->pos;
+
+ b->out_pos += copy_size;
+ b->in_pos += copy_size;
+ }
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+ size_t copy_size = dict->pos - dict->start;
+
+ if (DEC_IS_MULTI(dict->mode))
+ {
+ if (dict->pos == dict->end)
+ dict->pos = 0;
+
+ memcpy(b->out + b->out_pos, dict->buf + dict->start, copy_size);
+ }
+
+ dict->start = dict->pos;
+ b->out_pos += copy_size;
+ return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void rc_reset(struct rc_dec *rc)
+{
+ rc->range = (uint32_t) - 1;
+ rc->code = 0;
+ rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+ while (rc->init_bytes_left > 0)
+ {
+ if (b->in_pos == b->in_size)
+ return false;
+
+ rc->code = (rc->code << 8) + b->in[b->in_pos++];
+ --rc->init_bytes_left;
+ }
+
+ return true;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline bool rc_limit_exceeded(const struct rc_dec *rc)
+{
+ return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline bool rc_is_finished(const struct rc_dec *rc)
+{
+ return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static __always_inline void rc_normalize(struct rc_dec *rc)
+{
+ if (rc->range < RC_TOP_VALUE)
+ {
+ rc->range <<= RC_SHIFT_BITS;
+ rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+ }
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+ uint32_t bound;
+ int bit;
+
+ rc_normalize(rc);
+ bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+ if (rc->code < bound)
+ {
+ rc->range = bound;
+ *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+ bit = 0;
+ }
+ else
+ {
+ rc->range -= bound;
+ rc->code -= bound;
+ *prob -= *prob >> RC_MOVE_BITS;
+ bit = 1;
+ }
+
+ return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static __always_inline uint32_t rc_bittree(struct rc_dec *rc, uint16_t *probs, uint32_t limit)
+{
+ uint32_t symbol = 1;
+
+ do
+ {
+ if (rc_bit(rc, &probs[symbol]))
+ symbol = (symbol << 1) + 1;
+ else
+ symbol <<= 1;
+ } while (symbol < limit);
+
+ return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static __always_inline void rc_bittree_reverse(struct rc_dec *rc, uint16_t *probs,
+ uint32_t *dest, uint32_t limit)
+{
+ uint32_t symbol = 1;
+ uint32_t i = 0;
+
+ do
+ {
+ if (rc_bit(rc, &probs[symbol]))
+ {
+ symbol = (symbol << 1) + 1;
+ *dest += 1 << i;
+ }
+ else
+ {
+ symbol <<= 1;
+ }
+ } while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+ uint32_t mask;
+
+ do
+ {
+ rc_normalize(rc);
+ rc->range >>= 1;
+ rc->code -= rc->range;
+ mask = (uint32_t)0 - (rc->code >> 31);
+ rc->code += rc->range & mask;
+ *dest = (*dest << 1) + (mask + 1);
+ } while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+ uint32_t prev_byte = dict_get(&s->dict, 0);
+ uint32_t low = prev_byte >> (8 - s->lzma.lc);
+ uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+ return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void lzma_literal(struct xz_dec_lzma2 *s)
+{
+ uint16_t *probs;
+ uint32_t symbol;
+ uint32_t match_byte;
+ uint32_t match_bit;
+ uint32_t offset;
+ uint32_t i;
+
+ probs = lzma_literal_probs(s);
+
+ if (lzma_state_is_literal(s->lzma.state))
+ {
+ symbol = rc_bittree(&s->rc, probs, 0x100);
+ }
+ else
+ {
+ symbol = 1;
+ match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+ offset = 0x100;
+
+ do
+ {
+ match_bit = match_byte & offset;
+ match_byte <<= 1;
+ i = offset + match_bit + symbol;
+
+ if (rc_bit(&s->rc, &probs[i]))
+ {
+ symbol = (symbol << 1) + 1;
+ offset &= match_bit;
+ }
+ else
+ {
+ symbol <<= 1;
+ offset &= ~match_bit;
+ }
+ } while (symbol < 0x100);
+ }
+
+ dict_put(&s->dict, (uint8_t)symbol);
+ lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, uint32_t pos_state)
+{
+ uint16_t *probs;
+ uint32_t limit;
+
+ if (!rc_bit(&s->rc, &l->choice))
+ {
+ probs = l->low[pos_state];
+ limit = LEN_LOW_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN;
+ }
+ else
+ {
+ if (!rc_bit(&s->rc, &l->choice2))
+ {
+ probs = l->mid[pos_state];
+ limit = LEN_MID_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+ }
+ else
+ {
+ probs = l->high;
+ limit = LEN_HIGH_SYMBOLS;
+ s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS;
+ }
+ }
+
+ s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+ uint16_t *probs;
+ uint32_t dist_slot;
+ uint32_t limit;
+
+ lzma_state_match(&s->lzma.state);
+
+ s->lzma.rep3 = s->lzma.rep2;
+ s->lzma.rep2 = s->lzma.rep1;
+ s->lzma.rep1 = s->lzma.rep0;
+
+ lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+ probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+ dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+ if (dist_slot < DIST_MODEL_START)
+ {
+ s->lzma.rep0 = dist_slot;
+ }
+ else
+ {
+ limit = (dist_slot >> 1) - 1;
+ s->lzma.rep0 = 2 + (dist_slot & 1);
+
+ if (dist_slot < DIST_MODEL_END)
+ {
+ s->lzma.rep0 <<= limit;
+ probs = s->lzma.dist_special + s->lzma.rep0 - dist_slot - 1;
+ rc_bittree_reverse(&s->rc, probs, &s->lzma.rep0, limit);
+ }
+ else
+ {
+ rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+ s->lzma.rep0 <<= ALIGN_BITS;
+ rc_bittree_reverse(&s->rc, s->lzma.dist_align, &s->lzma.rep0, ALIGN_BITS);
+ }
+ }
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+ uint32_t tmp;
+
+ if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state]))
+ {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[s->lzma.state][pos_state]))
+ {
+ lzma_state_short_rep(&s->lzma.state);
+ s->lzma.len = 1;
+ return;
+ }
+ }
+ else
+ {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state]))
+ {
+ tmp = s->lzma.rep1;
+ }
+ else
+ {
+ if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state]))
+ {
+ tmp = s->lzma.rep2;
+ }
+ else
+ {
+ tmp = s->lzma.rep3;
+ s->lzma.rep3 = s->lzma.rep2;
+ }
+
+ s->lzma.rep2 = s->lzma.rep1;
+ }
+
+ s->lzma.rep1 = s->lzma.rep0;
+ s->lzma.rep0 = tmp;
+ }
+
+ lzma_state_long_rep(&s->lzma.state);
+ lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static bool lzma_main(struct xz_dec_lzma2 *s)
+{
+ uint32_t pos_state;
+
+ /*
+ * If the dictionary was reached during the previous call, try to
+ * finish the possibly pending repeat in the dictionary.
+ */
+ if (dict_has_space(&s->dict) && s->lzma.len > 0)
+ dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+ /*
+ * Decode more LZMA symbols. One iteration may consume up to
+ * LZMA_IN_REQUIRED - 1 bytes.
+ */
+ while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc))
+ {
+ pos_state = s->dict.pos & s->lzma.pos_mask;
+
+ if (!rc_bit(&s->rc, &s->lzma.is_match[s->lzma.state][pos_state]))
+ {
+ lzma_literal(s);
+ }
+ else
+ {
+ if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+ lzma_rep_match(s, pos_state);
+ else
+ lzma_match(s, pos_state);
+
+ if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+ return false;
+ }
+ }
+
+ /*
+ * Having the range decoder always normalized when we are outside
+ * this function makes it easier to correctly handle end of the chunk.
+ */
+ rc_normalize(&s->rc);
+
+ return true;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void lzma_reset(struct xz_dec_lzma2 *s)
+{
+ uint16_t *probs;
+ size_t i;
+
+ s->lzma.state = STATE_LIT_LIT;
+ s->lzma.rep0 = 0;
+ s->lzma.rep1 = 0;
+ s->lzma.rep2 = 0;
+ s->lzma.rep3 = 0;
+
+ /*
+ * All probabilities are initialized to the same value. This hack
+ * makes the code smaller by avoiding a separate loop for each
+ * probability array.
+ *
+ * This could be optimized so that only that part of literal
+ * probabilities that are actually required. In the common case
+ * we would write 12 KiB less.
+ */
+ probs = s->lzma.is_match[0];
+ for (i = 0; i < PROBS_TOTAL; ++i)
+ probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+ rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+ if (props > (4 * 5 + 4) * 9 + 8)
+ return false;
+
+ s->lzma.pos_mask = 0;
+ while (props >= 9 * 5)
+ {
+ props -= 9 * 5;
+ ++s->lzma.pos_mask;
+ }
+
+ s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+ s->lzma.literal_pos_mask = 0;
+ while (props >= 9)
+ {
+ props -= 9;
+ ++s->lzma.literal_pos_mask;
+ }
+
+ s->lzma.lc = props;
+
+ if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+ return false;
+
+ s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+ lzma_reset(s);
+
+ return true;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+ size_t in_avail;
+ uint32_t tmp;
+
+ in_avail = b->in_size - b->in_pos;
+ if (s->temp.size > 0 || s->lzma2.compressed == 0)
+ {
+ tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+ if (tmp > s->lzma2.compressed - s->temp.size)
+ tmp = s->lzma2.compressed - s->temp.size;
+ if (tmp > in_avail)
+ tmp = in_avail;
+
+ memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+ if (s->temp.size + tmp == s->lzma2.compressed)
+ {
+ memzero(s->temp.buf + s->temp.size + tmp, sizeof(s->temp.buf) - s->temp.size - tmp);
+ s->rc.in_limit = s->temp.size + tmp;
+ }
+ else if (s->temp.size + tmp < LZMA_IN_REQUIRED)
+ {
+ s->temp.size += tmp;
+ b->in_pos += tmp;
+ return true;
+ }
+ else
+ {
+ s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+ }
+
+ s->rc.in = s->temp.buf;
+ s->rc.in_pos = 0;
+
+ if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+ return false;
+
+ s->lzma2.compressed -= s->rc.in_pos;
+
+ if (s->rc.in_pos < s->temp.size)
+ {
+ s->temp.size -= s->rc.in_pos;
+ memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, s->temp.size);
+ return true;
+ }
+
+ b->in_pos += s->rc.in_pos - s->temp.size;
+ s->temp.size = 0;
+ }
+
+ in_avail = b->in_size - b->in_pos;
+ if (in_avail >= LZMA_IN_REQUIRED)
+ {
+ s->rc.in = b->in;
+ s->rc.in_pos = b->in_pos;
+
+ if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+ s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+ else
+ s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+ if (!lzma_main(s))
+ return false;
+
+ in_avail = s->rc.in_pos - b->in_pos;
+ if (in_avail > s->lzma2.compressed)
+ return false;
+
+ s->lzma2.compressed -= in_avail;
+ b->in_pos = s->rc.in_pos;
+ }
+
+ in_avail = b->in_size - b->in_pos;
+ if (in_avail < LZMA_IN_REQUIRED)
+ {
+ if (in_avail > s->lzma2.compressed)
+ in_avail = s->lzma2.compressed;
+
+ memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+ s->temp.size = in_avail;
+ b->in_pos += in_avail;
+ }
+
+ return true;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+ uint32_t tmp;
+
+ while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN)
+ {
+ switch (s->lzma2.sequence)
+ {
+ case SEQ_CONTROL:
+ /*
+ * LZMA2 control byte
+ *
+ * Exact values:
+ * 0x00 End marker
+ * 0x01 Dictionary reset followed by
+ * an uncompressed chunk
+ * 0x02 Uncompressed chunk (no dictionary reset)
+ *
+ * Highest three bits (s->control & 0xE0):
+ * 0xE0 Dictionary reset, new properties and state
+ * reset, followed by LZMA compressed chunk
+ * 0xC0 New properties and state reset, followed
+ * by LZMA compressed chunk (no dictionary
+ * reset)
+ * 0xA0 State reset using old properties,
+ * followed by LZMA compressed chunk (no
+ * dictionary reset)
+ * 0x80 LZMA chunk (no dictionary or state reset)
+ *
+ * For LZMA compressed chunks, the lowest five bits
+ * (s->control & 1F) are the highest bits of the
+ * uncompressed size (bits 16-20).
+ *
+ * A new LZMA2 stream must begin with a dictionary
+ * reset. The first LZMA chunk must set new
+ * properties and reset the LZMA state.
+ *
+ * Values that don't match anything described above
+ * are invalid and we return XZ_DATA_ERROR.
+ */
+ tmp = b->in[b->in_pos++];
+
+ if (tmp == 0x00)
+ return XZ_STREAM_END;
+
+ if (tmp >= 0xE0 || tmp == 0x01)
+ {
+ s->lzma2.need_props = true;
+ s->lzma2.need_dict_reset = false;
+ dict_reset(&s->dict, b);
+ }
+ else if (s->lzma2.need_dict_reset)
+ {
+ return XZ_DATA_ERROR;
+ }
+
+ if (tmp >= 0x80)
+ {
+ s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+ s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+ if (tmp >= 0xC0)
+ {
+ /*
+ * When there are new properties,
+ * state reset is done at
+ * SEQ_PROPERTIES.
+ */
+ s->lzma2.need_props = false;
+ s->lzma2.next_sequence = SEQ_PROPERTIES;
+ }
+ else if (s->lzma2.need_props)
+ {
+ return XZ_DATA_ERROR;
+ }
+ else
+ {
+ s->lzma2.next_sequence = SEQ_LZMA_PREPARE;
+ if (tmp >= 0xA0)
+ lzma_reset(s);
+ }
+ }
+ else
+ {
+ if (tmp > 0x02)
+ return XZ_DATA_ERROR;
+
+ s->lzma2.sequence = SEQ_COMPRESSED_0;
+ s->lzma2.next_sequence = SEQ_COPY;
+ }
+
+ break;
+
+ case SEQ_UNCOMPRESSED_1:
+ s->lzma2.uncompressed += (uint32_t)b->in[b->in_pos++] << 8;
+ s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+ break;
+
+ case SEQ_UNCOMPRESSED_2:
+ s->lzma2.uncompressed += (uint32_t)b->in[b->in_pos++] + 1;
+ s->lzma2.sequence = SEQ_COMPRESSED_0;
+ break;
+
+ case SEQ_COMPRESSED_0:
+ s->lzma2.compressed = (uint32_t)b->in[b->in_pos++] << 8;
+ s->lzma2.sequence = SEQ_COMPRESSED_1;
+ break;
+
+ case SEQ_COMPRESSED_1:
+ s->lzma2.compressed += (uint32_t)b->in[b->in_pos++] + 1;
+ s->lzma2.sequence = s->lzma2.next_sequence;
+ break;
+
+ case SEQ_PROPERTIES:
+ if (!lzma_props(s, b->in[b->in_pos++]))
+ return XZ_DATA_ERROR;
+
+ s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+ case SEQ_LZMA_PREPARE:
+ if (s->lzma2.compressed < RC_INIT_BYTES)
+ return XZ_DATA_ERROR;
+
+ if (!rc_read_init(&s->rc, b))
+ return XZ_OK;
+
+ s->lzma2.compressed -= RC_INIT_BYTES;
+ s->lzma2.sequence = SEQ_LZMA_RUN;
+
+ case SEQ_LZMA_RUN:
+ /*
+ * Set dictionary limit to indicate how much we want
+ * to be encoded at maximum. Decode new data into the
+ * dictionary. Flush the new data from dictionary to
+ * b->out. Check if we finished decoding this chunk.
+ * In case the dictionary got full but we didn't fill
+ * the output buffer yet, we may run this loop
+ * multiple times without changing s->lzma2.sequence.
+ */
+ dict_limit(&s->dict,
+ min_t(size_t, b->out_size - b->out_pos, s->lzma2.uncompressed));
+ if (!lzma2_lzma(s, b))
+ return XZ_DATA_ERROR;
+
+ s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+ if (s->lzma2.uncompressed == 0)
+ {
+ if (s->lzma2.compressed > 0 || s->lzma.len > 0 || !rc_is_finished(&s->rc))
+ return XZ_DATA_ERROR;
+
+ rc_reset(&s->rc);
+ s->lzma2.sequence = SEQ_CONTROL;
+ }
+ else if (b->out_pos == b->out_size ||
+ (b->in_pos == b->in_size && s->temp.size < s->lzma2.compressed))
+ {
+ return XZ_OK;
+ }
+
+ break;
+
+ case SEQ_COPY:
+ dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+ if (s->lzma2.compressed > 0)
+ return XZ_OK;
+
+ s->lzma2.sequence = SEQ_CONTROL;
+ break;
+ }
+ }
+
+ return XZ_OK;
+}
+
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, uint32_t dict_max)
+{
+ struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->dict.mode = mode;
+ s->dict.size_max = dict_max;
+
+ if (DEC_IS_PREALLOC(mode))
+ {
+ s->dict.buf = vmalloc(dict_max);
+ if (s->dict.buf == NULL)
+ {
+ kfree(s);
+ return NULL;
+ }
+ }
+ else if (DEC_IS_DYNALLOC(mode))
+ {
+ s->dict.buf = NULL;
+ s->dict.allocated = 0;
+ }
+
+ return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+{
+ /* This limits dictionary size to 3 GiB to keep parsing simpler. */
+ if (props > 39)
+ return XZ_OPTIONS_ERROR;
+
+ s->dict.size = 2 + (props & 1);
+ s->dict.size <<= (props >> 1) + 11;
+
+ if (DEC_IS_MULTI(s->dict.mode))
+ {
+ if (s->dict.size > s->dict.size_max)
+ return XZ_MEMLIMIT_ERROR;
+
+ s->dict.end = s->dict.size;
+
+ if (DEC_IS_DYNALLOC(s->dict.mode))
+ {
+ if (s->dict.allocated < s->dict.size)
+ {
+ vfree(s->dict.buf);
+ s->dict.buf = vmalloc(s->dict.size);
+ if (s->dict.buf == NULL)
+ {
+ s->dict.allocated = 0;
+ return XZ_MEM_ERROR;
+ }
+ }
+ }
+ }
+
+ s->lzma.len = 0;
+
+ s->lzma2.sequence = SEQ_CONTROL;
+ s->lzma2.need_dict_reset = true;
+
+ s->temp.size = 0;
+
+ return XZ_OK;
+}
+
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+{
+ if (DEC_IS_MULTI(s->dict.mode))
+ vfree(s->dict.buf);
+
+ kfree(s);
+}
diff --git a/depends/xz-embedded/src/xz_dec_stream.c b/depends/xz-embedded/src/xz_dec_stream.c
new file mode 100644
index 00000000..6e935ded
--- /dev/null
+++ b/depends/xz-embedded/src/xz_dec_stream.c
@@ -0,0 +1,860 @@
+/*
+ * .xz Stream decoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_stream.h"
+
+#ifdef XZ_USE_CRC64
+#define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
+#else
+#define IS_CRC64(check_type) false
+#endif
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash
+{
+ vli_type unpadded;
+ vli_type uncompressed;
+ uint32_t crc32;
+};
+
+struct xz_dec
+{
+ /* Position in dec_main() */
+ enum
+ {
+ SEQ_STREAM_HEADER,
+ SEQ_BLOCK_START,
+ SEQ_BLOCK_HEADER,
+ SEQ_BLOCK_UNCOMPRESS,
+ SEQ_BLOCK_PADDING,
+ SEQ_BLOCK_CHECK,
+ SEQ_INDEX,
+ SEQ_INDEX_PADDING,
+ SEQ_INDEX_CRC32,
+ SEQ_STREAM_FOOTER
+ } sequence;
+
+ /* Position in variable-length integers and Check fields */
+ uint32_t pos;
+
+ /* Variable-length integer decoded by dec_vli() */
+ vli_type vli;
+
+ /* Saved in_pos and out_pos */
+ size_t in_start;
+ size_t out_start;
+
+#ifdef XZ_USE_CRC64
+ /* CRC32 or CRC64 value in Block or CRC32 value in Index */
+ uint64_t crc;
+#else
+ /* CRC32 value in Block or Index */
+ uint32_t crc;
+#endif
+
+ /* Type of the integrity check calculated from uncompressed data */
+ enum xz_check check_type;
+
+ /* Operation mode */
+ enum xz_mode mode;
+
+ /*
+ * True if the next call to xz_dec_run() is allowed to return
+ * XZ_BUF_ERROR.
+ */
+ bool allow_buf_error;
+
+ /* Information stored in Block Header */
+ struct
+ {
+ /*
+ * Value stored in the Compressed Size field, or
+ * VLI_UNKNOWN if Compressed Size is not present.
+ */
+ vli_type compressed;
+
+ /*
+ * Value stored in the Uncompressed Size field, or
+ * VLI_UNKNOWN if Uncompressed Size is not present.
+ */
+ vli_type uncompressed;
+
+ /* Size of the Block Header field */
+ uint32_t size;
+ } block_header;
+
+ /* Information collected when decoding Blocks */
+ struct
+ {
+ /* Observed compressed size of the current Block */
+ vli_type compressed;
+
+ /* Observed uncompressed size of the current Block */
+ vli_type uncompressed;
+
+ /* Number of Blocks decoded so far */
+ vli_type count;
+
+ /*
+ * Hash calculated from the Block sizes. This is used to
+ * validate the Index field.
+ */
+ struct xz_dec_hash hash;
+ } block;
+
+ /* Variables needed when verifying the Index field */
+ struct
+ {
+ /* Position in dec_index() */
+ enum
+ {
+ SEQ_INDEX_COUNT,
+ SEQ_INDEX_UNPADDED,
+ SEQ_INDEX_UNCOMPRESSED
+ } sequence;
+
+ /* Size of the Index in bytes */
+ vli_type size;
+
+ /* Number of Records (matches block.count in valid files) */
+ vli_type count;
+
+ /*
+ * Hash calculated from the Records (matches block.hash in
+ * valid files).
+ */
+ struct xz_dec_hash hash;
+ } index;
+
+ /*
+ * Temporary buffer needed to hold Stream Header, Block Header,
+ * and Stream Footer. The Block Header is the biggest (1 KiB)
+ * so we reserve space according to that. buf[] has to be aligned
+ * to a multiple of four bytes; the size_t variables before it
+ * should guarantee this.
+ */
+ struct
+ {
+ size_t pos;
+ size_t size;
+ uint8_t buf[1024];
+ } temp;
+
+ struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+ struct xz_dec_bcj *bcj;
+ bool bcj_active;
+#endif
+};
+
+#ifdef XZ_DEC_ANY_CHECK
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {0, 4, 4, 4, 8, 8, 8, 16,
+ 16, 16, 32, 32, 32, 64, 64, 64};
+#endif
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static bool fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+ size_t copy_size = min_t(size_t, b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+ memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+ b->in_pos += copy_size;
+ s->temp.pos += copy_size;
+
+ if (s->temp.pos == s->temp.size)
+ {
+ s->temp.pos = 0;
+ return true;
+ }
+
+ return false;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in, size_t *in_pos, size_t in_size)
+{
+ uint8_t byte;
+
+ if (s->pos == 0)
+ s->vli = 0;
+
+ while (*in_pos < in_size)
+ {
+ byte = in[*in_pos];
+ ++*in_pos;
+
+ s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+ if ((byte & 0x80) == 0)
+ {
+ /* Don't allow non-minimal encodings. */
+ if (byte == 0 && s->pos != 0)
+ return XZ_DATA_ERROR;
+
+ s->pos = 0;
+ return XZ_STREAM_END;
+ }
+
+ s->pos += 7;
+ if (s->pos == 7 * VLI_BYTES_MAX)
+ return XZ_DATA_ERROR;
+ }
+
+ return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 or CRC64 value if presence of
+ * the CRC32 or CRC64 field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ s->in_start = b->in_pos;
+ s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+ if (s->bcj_active)
+ ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+ else
+#endif
+ ret = xz_dec_lzma2_run(s->lzma2, b);
+
+ s->block.compressed += b->in_pos - s->in_start;
+ s->block.uncompressed += b->out_pos - s->out_start;
+
+ /*
+ * There is no need to separately check for VLI_UNKNOWN, since
+ * the observed sizes are always smaller than VLI_UNKNOWN.
+ */
+ if (s->block.compressed > s->block_header.compressed ||
+ s->block.uncompressed > s->block_header.uncompressed)
+ return XZ_DATA_ERROR;
+
+ if (s->check_type == XZ_CHECK_CRC32)
+ s->crc = xz_crc32(b->out + s->out_start, b->out_pos - s->out_start, s->crc);
+#ifdef XZ_USE_CRC64
+ else if (s->check_type == XZ_CHECK_CRC64)
+ s->crc = xz_crc64(b->out + s->out_start, b->out_pos - s->out_start, s->crc);
+#endif
+
+ if (ret == XZ_STREAM_END)
+ {
+ if (s->block_header.compressed != VLI_UNKNOWN &&
+ s->block_header.compressed != s->block.compressed)
+ return XZ_DATA_ERROR;
+
+ if (s->block_header.uncompressed != VLI_UNKNOWN &&
+ s->block_header.uncompressed != s->block.uncompressed)
+ return XZ_DATA_ERROR;
+
+ s->block.hash.unpadded += s->block_header.size + s->block.compressed;
+
+#ifdef XZ_DEC_ANY_CHECK
+ s->block.hash.unpadded += check_sizes[s->check_type];
+#else
+ if (s->check_type == XZ_CHECK_CRC32)
+ s->block.hash.unpadded += 4;
+ else if (IS_CRC64(s->check_type))
+ s->block.hash.unpadded += 8;
+#endif
+
+ s->block.hash.uncompressed += s->block.uncompressed;
+ s->block.hash.crc32 = xz_crc32((const uint8_t *)&s->block.hash, sizeof(s->block.hash),
+ s->block.hash.crc32);
+
+ ++s->block.count;
+ }
+
+ return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+ size_t in_used = b->in_pos - s->in_start;
+ s->index.size += in_used;
+ s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ do
+ {
+ ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+ if (ret != XZ_STREAM_END)
+ {
+ index_update(s, b);
+ return ret;
+ }
+
+ switch (s->index.sequence)
+ {
+ case SEQ_INDEX_COUNT:
+ s->index.count = s->vli;
+
+ /*
+ * Validate that the Number of Records field
+ * indicates the same number of Records as
+ * there were Blocks in the Stream.
+ */
+ if (s->index.count != s->block.count)
+ return XZ_DATA_ERROR;
+
+ s->index.sequence = SEQ_INDEX_UNPADDED;
+ break;
+
+ case SEQ_INDEX_UNPADDED:
+ s->index.hash.unpadded += s->vli;
+ s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+ break;
+
+ case SEQ_INDEX_UNCOMPRESSED:
+ s->index.hash.uncompressed += s->vli;
+ s->index.hash.crc32 = xz_crc32((const uint8_t *)&s->index.hash,
+ sizeof(s->index.hash), s->index.hash.crc32);
+ --s->index.count;
+ s->index.sequence = SEQ_INDEX_UNPADDED;
+ break;
+ }
+ } while (s->index.count > 0);
+
+ return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four or eight input bytes match the value
+ * of s->crc. s->pos must be zero when starting to validate the first byte.
+ * The "bits" argument allows using the same code for both CRC32 and CRC64.
+ */
+static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b, uint32_t bits)
+{
+ do
+ {
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
+ return XZ_DATA_ERROR;
+
+ s->pos += 8;
+
+ } while (s->pos < bits);
+
+ s->crc = 0;
+ s->pos = 0;
+
+ return XZ_STREAM_END;
+}
+
+#ifdef XZ_DEC_ANY_CHECK
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static bool check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+ while (s->pos < check_sizes[s->check_type])
+ {
+ if (b->in_pos == b->in_size)
+ return false;
+
+ ++b->in_pos;
+ ++s->pos;
+ }
+
+ s->pos = 0;
+
+ return true;
+}
+#endif
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret dec_stream_header(struct xz_dec *s)
+{
+ if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+ return XZ_FORMAT_ERROR;
+
+ if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) !=
+ get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+ return XZ_DATA_ERROR;
+
+ if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+ return XZ_OPTIONS_ERROR;
+
+ /*
+ * Of integrity checks, we support none (Check ID = 0),
+ * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
+ * However, if XZ_DEC_ANY_CHECK is defined, we will accept other
+ * check types too, but then the check won't be verified and
+ * a warning (XZ_UNSUPPORTED_CHECK) will be given.
+ */
+ s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+#ifdef XZ_DEC_ANY_CHECK
+ if (s->check_type > XZ_CHECK_MAX)
+ return XZ_OPTIONS_ERROR;
+
+ if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+ return XZ_UNSUPPORTED_CHECK;
+#else
+ if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+ return XZ_OPTIONS_ERROR;
+#endif
+
+ return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret dec_stream_footer(struct xz_dec *s)
+{
+ if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+ return XZ_DATA_ERROR;
+
+ if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+ return XZ_DATA_ERROR;
+
+ /*
+ * Validate Backward Size. Note that we never added the size of the
+ * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+ * instead of s->index.size / 4 - 1.
+ */
+ if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+ return XZ_DATA_ERROR;
+
+ if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+ return XZ_DATA_ERROR;
+
+ /*
+ * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+ * for the caller.
+ */
+ return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret dec_block_header(struct xz_dec *s)
+{
+ enum xz_ret ret;
+
+ /*
+ * Validate the CRC32. We know that the temp buffer is at least
+ * eight bytes so this is safe.
+ */
+ s->temp.size -= 4;
+ if (xz_crc32(s->temp.buf, s->temp.size, 0) != get_le32(s->temp.buf + s->temp.size))
+ return XZ_DATA_ERROR;
+
+ s->temp.pos = 2;
+
+/*
+ * Catch unsupported Block Flags. We support only one or two filters
+ * in the chain, so we catch that with the same test.
+ */
+#ifdef XZ_DEC_BCJ
+ if (s->temp.buf[1] & 0x3E)
+#else
+ if (s->temp.buf[1] & 0x3F)
+#endif
+ return XZ_OPTIONS_ERROR;
+
+ /* Compressed Size */
+ if (s->temp.buf[1] & 0x40)
+ {
+ if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) != XZ_STREAM_END)
+ return XZ_DATA_ERROR;
+
+ s->block_header.compressed = s->vli;
+ }
+ else
+ {
+ s->block_header.compressed = VLI_UNKNOWN;
+ }
+
+ /* Uncompressed Size */
+ if (s->temp.buf[1] & 0x80)
+ {
+ if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) != XZ_STREAM_END)
+ return XZ_DATA_ERROR;
+
+ s->block_header.uncompressed = s->vli;
+ }
+ else
+ {
+ s->block_header.uncompressed = VLI_UNKNOWN;
+ }
+
+#ifdef XZ_DEC_BCJ
+ /* If there are two filters, the first one must be a BCJ filter. */
+ s->bcj_active = s->temp.buf[1] & 0x01;
+ if (s->bcj_active)
+ {
+ if (s->temp.size - s->temp.pos < 2)
+ return XZ_OPTIONS_ERROR;
+
+ ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+ if (ret != XZ_OK)
+ return ret;
+
+ /*
+ * We don't support custom start offset,
+ * so Size of Properties must be zero.
+ */
+ if (s->temp.buf[s->temp.pos++] != 0x00)
+ return XZ_OPTIONS_ERROR;
+ }
+#endif
+
+ /* Valid Filter Flags always take at least two bytes. */
+ if (s->temp.size - s->temp.pos < 2)
+ return XZ_DATA_ERROR;
+
+ /* Filter ID = LZMA2 */
+ if (s->temp.buf[s->temp.pos++] != 0x21)
+ return XZ_OPTIONS_ERROR;
+
+ /* Size of Properties = 1-byte Filter Properties */
+ if (s->temp.buf[s->temp.pos++] != 0x01)
+ return XZ_OPTIONS_ERROR;
+
+ /* Filter Properties contains LZMA2 dictionary size. */
+ if (s->temp.size - s->temp.pos < 1)
+ return XZ_DATA_ERROR;
+
+ ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+ if (ret != XZ_OK)
+ return ret;
+
+ /* The rest must be Header Padding. */
+ while (s->temp.pos < s->temp.size)
+ if (s->temp.buf[s->temp.pos++] != 0x00)
+ return XZ_OPTIONS_ERROR;
+
+ s->temp.pos = 0;
+ s->block.compressed = 0;
+ s->block.uncompressed = 0;
+
+ return XZ_OK;
+}
+
+static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+ enum xz_ret ret;
+
+ /*
+ * Store the start position for the case when we are in the middle
+ * of the Index field.
+ */
+ s->in_start = b->in_pos;
+
+ while (true)
+ {
+ switch (s->sequence)
+ {
+ case SEQ_STREAM_HEADER:
+ /*
+ * Stream Header is copied to s->temp, and then
+ * decoded from there. This way if the caller
+ * gives us only little input at a time, we can
+ * still keep the Stream Header decoding code
+ * simple. Similar approach is used in many places
+ * in this file.
+ */
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ /*
+ * If dec_stream_header() returns
+ * XZ_UNSUPPORTED_CHECK, it is still possible
+ * to continue decoding if working in multi-call
+ * mode. Thus, update s->sequence before calling
+ * dec_stream_header().
+ */
+ s->sequence = SEQ_BLOCK_START;
+
+ ret = dec_stream_header(s);
+ if (ret != XZ_OK)
+ return ret;
+
+ case SEQ_BLOCK_START:
+ /* We need one byte of input to continue. */
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ /* See if this is the beginning of the Index field. */
+ if (b->in[b->in_pos] == 0)
+ {
+ s->in_start = b->in_pos++;
+ s->sequence = SEQ_INDEX;
+ break;
+ }
+
+ /*
+ * Calculate the size of the Block Header and
+ * prepare to decode it.
+ */
+ s->block_header.size = ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+ s->temp.size = s->block_header.size;
+ s->temp.pos = 0;
+ s->sequence = SEQ_BLOCK_HEADER;
+
+ case SEQ_BLOCK_HEADER:
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ ret = dec_block_header(s);
+ if (ret != XZ_OK)
+ return ret;
+
+ s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+ case SEQ_BLOCK_UNCOMPRESS:
+ ret = dec_block(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->sequence = SEQ_BLOCK_PADDING;
+
+ case SEQ_BLOCK_PADDING:
+ /*
+ * Size of Compressed Data + Block Padding
+ * must be a multiple of four. We don't need
+ * s->block.compressed for anything else
+ * anymore, so we use it here to test the size
+ * of the Block Padding field.
+ */
+ while (s->block.compressed & 3)
+ {
+ if (b->in_pos == b->in_size)
+ return XZ_OK;
+
+ if (b->in[b->in_pos++] != 0)
+ return XZ_DATA_ERROR;
+
+ ++s->block.compressed;
+ }
+
+ s->sequence = SEQ_BLOCK_CHECK;
+
+ case SEQ_BLOCK_CHECK:
+ if (s->check_type == XZ_CHECK_CRC32)
+ {
+ ret = crc_validate(s, b, 32);
+ if (ret != XZ_STREAM_END)
+ return ret;
+ }
+ else if (IS_CRC64(s->check_type))
+ {
+ ret = crc_validate(s, b, 64);
+ if (ret != XZ_STREAM_END)
+ return ret;
+ }
+#ifdef XZ_DEC_ANY_CHECK
+ else if (!check_skip(s, b))
+ {
+ return XZ_OK;
+ }
+#endif
+
+ s->sequence = SEQ_BLOCK_START;
+ break;
+
+ case SEQ_INDEX:
+ ret = dec_index(s, b);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->sequence = SEQ_INDEX_PADDING;
+
+ case SEQ_INDEX_PADDING:
+ while ((s->index.size + (b->in_pos - s->in_start)) & 3)
+ {
+ if (b->in_pos == b->in_size)
+ {
+ index_update(s, b);
+ return XZ_OK;
+ }
+
+ if (b->in[b->in_pos++] != 0)
+ return XZ_DATA_ERROR;
+ }
+
+ /* Finish the CRC32 value and Index size. */
+ index_update(s, b);
+
+ /* Compare the hashes to validate the Index field. */
+ if (!memeq(&s->block.hash, &s->index.hash, sizeof(s->block.hash)))
+ return XZ_DATA_ERROR;
+
+ s->sequence = SEQ_INDEX_CRC32;
+
+ case SEQ_INDEX_CRC32:
+ ret = crc_validate(s, b, 32);
+ if (ret != XZ_STREAM_END)
+ return ret;
+
+ s->temp.size = STREAM_HEADER_SIZE;
+ s->sequence = SEQ_STREAM_FOOTER;
+
+ case SEQ_STREAM_FOOTER:
+ if (!fill_temp(s, b))
+ return XZ_OK;
+
+ return dec_stream_footer(s);
+ }
+ }
+
+ /* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+ size_t in_start;
+ size_t out_start;
+ enum xz_ret ret;
+
+ if (DEC_IS_SINGLE(s->mode))
+ xz_dec_reset(s);
+
+ in_start = b->in_pos;
+ out_start = b->out_pos;
+ ret = dec_main(s, b);
+
+ if (DEC_IS_SINGLE(s->mode))
+ {
+ if (ret == XZ_OK)
+ ret = b->in_pos == b->in_size ? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+ if (ret != XZ_STREAM_END)
+ {
+ b->in_pos = in_start;
+ b->out_pos = out_start;
+ }
+ }
+ else if (ret == XZ_OK && in_start == b->in_pos && out_start == b->out_pos)
+ {
+ if (s->allow_buf_error)
+ ret = XZ_BUF_ERROR;
+
+ s->allow_buf_error = true;
+ }
+ else
+ {
+ s->allow_buf_error = false;
+ }
+
+ return ret;
+}
+
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+{
+ struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+ s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+ if (s->bcj == NULL)
+ goto error_bcj;
+#endif
+
+ s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+ if (s->lzma2 == NULL)
+ goto error_lzma2;
+
+ xz_dec_reset(s);
+ return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+ xz_dec_bcj_end(s->bcj);
+error_bcj:
+#endif
+ kfree(s);
+ return NULL;
+}
+
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
+{
+ s->sequence = SEQ_STREAM_HEADER;
+ s->allow_buf_error = false;
+ s->pos = 0;
+ s->crc = 0;
+ memzero(&s->block, sizeof(s->block));
+ memzero(&s->index, sizeof(s->index));
+ s->temp.pos = 0;
+ s->temp.size = STREAM_HEADER_SIZE;
+}
+
+XZ_EXTERN void xz_dec_end(struct xz_dec *s)
+{
+ if (s != NULL)
+ {
+ xz_dec_lzma2_end(s->lzma2);
+#ifdef XZ_DEC_BCJ
+ xz_dec_bcj_end(s->bcj);
+#endif
+ kfree(s);
+ }
+}
diff --git a/depends/xz-embedded/src/xz_lzma2.h b/depends/xz-embedded/src/xz_lzma2.h
new file mode 100644
index 00000000..3976033a
--- /dev/null
+++ b/depends/xz-embedded/src/xz_lzma2.h
@@ -0,0 +1,204 @@
+/*
+ * LZMA2 definitions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_LZMA2_H
+#define XZ_LZMA2_H
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ * - Literal: One 8-bit byte
+ * - Match: Repeat a chunk of data at some distance
+ * - Long repeat: Multi-byte match at a recently seen distance
+ * - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state
+{
+ STATE_LIT_LIT,
+ STATE_MATCH_LIT_LIT,
+ STATE_REP_LIT_LIT,
+ STATE_SHORTREP_LIT_LIT,
+ STATE_MATCH_LIT,
+ STATE_REP_LIT,
+ STATE_SHORTREP_LIT,
+ STATE_LIT_MATCH,
+ STATE_LIT_LONGREP,
+ STATE_LIT_SHORTREP,
+ STATE_NONLIT_MATCH,
+ STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void lzma_state_literal(enum lzma_state *state)
+{
+ if (*state <= STATE_SHORTREP_LIT_LIT)
+ *state = STATE_LIT_LIT;
+ else if (*state <= STATE_LIT_SHORTREP)
+ *state -= 3;
+ else
+ *state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void lzma_state_match(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void lzma_state_long_rep(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void lzma_state_short_rep(enum lzma_state *state)
+{
+ *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline bool lzma_state_is_literal(enum lzma_state state)
+{
+ return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ * - 0x001-0x0FF: Without match byte
+ * - 0x101-0x1FF: With match byte; match bit is 0
+ * - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length Bits
+ * 2-9 4 = Choice=0 + 3 bits
+ * 10-17 5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273 10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t lzma_get_dist_state(uint32_t len)
+{
+ return len < DIST_STATES + MATCH_LEN_MIN ? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ * - distance slot: the highest two bits
+ * - direct bits: 2-26 bits below the highest two bits
+ * - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX *LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+#endif
diff --git a/depends/xz-embedded/src/xz_private.h b/depends/xz-embedded/src/xz_private.h
new file mode 100644
index 00000000..55a3af1c
--- /dev/null
+++ b/depends/xz-embedded/src/xz_private.h
@@ -0,0 +1,150 @@
+/*
+ * Private includes and definitions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_PRIVATE_H
+#define XZ_PRIVATE_H
+
+#ifdef __KERNEL__
+#include <linux/xz.h>
+#include <linux/kernel.h>
+#include <asm/unaligned.h>
+/* XZ_PREBOOT may be defined only via decompress_unxz.c. */
+#ifndef XZ_PREBOOT
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#ifdef CONFIG_XZ_DEC_X86
+#define XZ_DEC_X86
+#endif
+#ifdef CONFIG_XZ_DEC_POWERPC
+#define XZ_DEC_POWERPC
+#endif
+#ifdef CONFIG_XZ_DEC_IA64
+#define XZ_DEC_IA64
+#endif
+#ifdef CONFIG_XZ_DEC_ARM
+#define XZ_DEC_ARM
+#endif
+#ifdef CONFIG_XZ_DEC_ARMTHUMB
+#define XZ_DEC_ARMTHUMB
+#endif
+#ifdef CONFIG_XZ_DEC_SPARC
+#define XZ_DEC_SPARC
+#endif
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#define memzero(buf, size) memset(buf, 0, size)
+#endif
+#define get_le32(p) le32_to_cpup((const uint32_t *)(p))
+#else
+/*
+ * For userspace builds, use a separate header to define the required
+ * macros and functions. This makes it easier to adapt the code into
+ * different environments and avoids clutter in the Linux kernel tree.
+ */
+#include "xz_config.h"
+#endif
+
+/* If no specific decoding mode is requested, enable support for all modes. */
+#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) && !defined(XZ_DEC_DYNALLOC)
+#define XZ_DEC_SINGLE
+#define XZ_DEC_PREALLOC
+#define XZ_DEC_DYNALLOC
+#endif
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+#define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+#define DEC_IS_SINGLE(mode) (false)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+#define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+#define DEC_IS_PREALLOC(mode) (false)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+#define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+#define DEC_IS_DYNALLOC(mode) (false)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+#define DEC_IS_MULTI(mode) (true)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+#define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+#define DEC_IS_MULTI(mode) (false)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+#if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) || defined(XZ_DEC_IA64) || \
+ defined(XZ_DEC_ARM) || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) || \
+ defined(XZ_DEC_SPARC)
+#define XZ_DEC_BCJ
+#endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, struct xz_buf *b);
+
+/* Free the memory allocated for the LZMA2 decoder. */
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b);
+
+/* Free the memory allocated for the BCJ filters. */
+#define xz_dec_bcj_end(s) kfree(s)
+#endif
+
+#endif
diff --git a/depends/xz-embedded/src/xz_stream.h b/depends/xz-embedded/src/xz_stream.h
new file mode 100644
index 00000000..c0e191e6
--- /dev/null
+++ b/depends/xz-embedded/src/xz_stream.h
@@ -0,0 +1,62 @@
+/*
+ * Definitions for handling the .xz file format
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_STREAM_H
+#define XZ_STREAM_H
+
+#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
+#include <linux/crc32.h>
+#undef crc32
+#define xz_crc32(buf, size, crc) (~crc32_le(~(uint32_t)(crc), buf, size))
+#endif
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ"
+#define HEADER_MAGIC_SIZE 6
+
+#define FOOTER_MAGIC "YZ"
+#define FOOTER_MAGIC_SIZE 2
+
+/*
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type) - 1 / 2)
+#define VLI_UNKNOWN ((vli_type) - 1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check
+{
+ XZ_CHECK_NONE = 0,
+ XZ_CHECK_CRC32 = 1,
+ XZ_CHECK_CRC64 = 4,
+ XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+
+#endif
diff --git a/depends/xz-embedded/xzminidec.c b/depends/xz-embedded/xzminidec.c
new file mode 100644
index 00000000..bb62c3ac
--- /dev/null
+++ b/depends/xz-embedded/xzminidec.c
@@ -0,0 +1,144 @@
+/*
+ * Simple XZ decoder command line tool
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is really limited: Not all filters from .xz format are supported,
+ * only CRC32 is supported as the integrity check, and decoding of
+ * concatenated .xz streams is not supported. Thus, you may want to look
+ * at xzdec from XZ Utils if a few KiB bigger tool is not a problem.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+static uint8_t in[BUFSIZ];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ const char *msg;
+
+ if (argc >= 2 && strcmp(argv[1], "--help") == 0)
+ {
+ fputs("Uncompress a .xz file from stdin to stdout.\n"
+ "Arguments other than `--help' are ignored.\n",
+ stdout);
+ return 0;
+ }
+
+ xz_crc32_init();
+#ifdef XZ_USE_CRC64
+ xz_crc64_init();
+#endif
+
+ /*
+ * Support up to 64 MiB dictionary. The actually needed memory
+ * is allocated once the headers have been parsed.
+ */
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == NULL)
+ {
+ msg = "Memory allocation failed\n";
+ goto error;
+ }
+
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = 0;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = BUFSIZ;
+
+ while (true)
+ {
+ if (b.in_pos == b.in_size)
+ {
+ b.in_size = fread(in, 1, sizeof(in), stdin);
+ b.in_pos = 0;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (b.out_pos == sizeof(out))
+ {
+ if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos)
+ {
+ msg = "Write error\n";
+ goto error;
+ }
+
+ b.out_pos = 0;
+ }
+
+ if (ret == XZ_OK)
+ continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+ if (ret == XZ_UNSUPPORTED_CHECK)
+ {
+ fputs(argv[0], stderr);
+ fputs(": ", stderr);
+ fputs("Unsupported check; not verifying "
+ "file integrity\n",
+ stderr);
+ continue;
+ }
+#endif
+
+ if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos || fclose(stdout))
+ {
+ msg = "Write error\n";
+ goto error;
+ }
+
+ switch (ret)
+ {
+ case XZ_STREAM_END:
+ xz_dec_end(s);
+ return 0;
+
+ case XZ_MEM_ERROR:
+ msg = "Memory allocation failed\n";
+ goto error;
+
+ case XZ_MEMLIMIT_ERROR:
+ msg = "Memory usage limit reached\n";
+ goto error;
+
+ case XZ_FORMAT_ERROR:
+ msg = "Not a .xz file\n";
+ goto error;
+
+ case XZ_OPTIONS_ERROR:
+ msg = "Unsupported options in the .xz headers\n";
+ goto error;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ msg = "File is corrupt\n";
+ goto error;
+
+ default:
+ msg = "Bug!\n";
+ goto error;
+ }
+ }
+
+error:
+ xz_dec_end(s);
+ fputs(argv[0], stderr);
+ fputs(": ", stderr);
+ fputs(msg, stderr);
+ return 1;
+}
diff --git a/gui/ConsoleWindow.cpp b/gui/ConsoleWindow.cpp
new file mode 100644
index 00000000..dc36a8ff
--- /dev/null
+++ b/gui/ConsoleWindow.cpp
@@ -0,0 +1,280 @@
+/* Copyright 2013 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 "ConsoleWindow.h"
+#include "ui_ConsoleWindow.h"
+#include "MultiMC.h"
+
+#include <QScrollBar>
+#include <QMessageBox>
+#include <QSystemTrayIcon>
+
+#include <gui/Platform.h>
+#include <gui/dialogs/CustomMessageBox.h>
+#include <gui/dialogs/ProgressDialog.h>
+
+#include "logic/net/PasteUpload.h"
+#include "logic/icons/IconList.h"
+
+ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
+ : QMainWindow(parent), ui(new Ui::ConsoleWindow), proc(mcproc)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ connect(mcproc, SIGNAL(log(QString, MessageLevel::Enum)), this,
+ SLOT(write(QString, MessageLevel::Enum)));
+ connect(mcproc, SIGNAL(ended(BaseInstance *, int, QProcess::ExitStatus)), this,
+ SLOT(onEnded(BaseInstance *, int, QProcess::ExitStatus)));
+ connect(mcproc, SIGNAL(prelaunch_failed(BaseInstance *, int, QProcess::ExitStatus)), this,
+ SLOT(onEnded(BaseInstance *, int, QProcess::ExitStatus)));
+ connect(mcproc, SIGNAL(launch_failed(BaseInstance *)), this,
+ SLOT(onLaunchFailed(BaseInstance *)));
+
+ restoreState(
+ QByteArray::fromBase64(MMC->settings()->get("ConsoleWindowState").toByteArray()));
+ restoreGeometry(
+ QByteArray::fromBase64(MMC->settings()->get("ConsoleWindowGeometry").toByteArray()));
+
+ QString iconKey = proc->instance()->iconKey();
+ QString name = proc->instance()->name();
+ auto icon = MMC->icons()->getIcon(iconKey);
+ setWindowIcon(icon);
+ m_trayIcon = new QSystemTrayIcon(icon, this);
+ QString consoleTitle = tr("Console window for ") + name;
+ m_trayIcon->setToolTip(consoleTitle);
+ setWindowTitle(consoleTitle);
+
+ connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+ SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
+ m_trayIcon->show();
+ if (mcproc->instance()->settings().get("ShowConsole").toBool())
+ {
+ show();
+ }
+ setMayClose(false);
+}
+
+ConsoleWindow::~ConsoleWindow()
+{
+ delete ui;
+}
+
+void ConsoleWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
+{
+ switch (reason)
+ {
+ case QSystemTrayIcon::Trigger:
+ {
+ toggleConsole();
+ }
+ default:
+ return;
+ }
+}
+
+void ConsoleWindow::writeColor(QString text, const char *color)
+{
+ // append a paragraph
+ QString newtext;
+ newtext += "<span style=\"";
+ {
+ if (color)
+ newtext += QString("color:") + color + ";";
+ newtext += "font-family: monospace;";
+ }
+ newtext += "\">";
+ newtext += text.toHtmlEscaped();
+ newtext += "</span>";
+ ui->text->appendHtml(newtext);
+}
+
+void ConsoleWindow::write(QString data, MessageLevel::Enum mode)
+{
+ QScrollBar *bar = ui->text->verticalScrollBar();
+ int max_bar = bar->maximum();
+ int val_bar = bar->value();
+ if(isVisible())
+ {
+ if (m_scroll_active)
+ {
+ m_scroll_active = (max_bar - val_bar) <= 1;
+ }
+ else
+ {
+ m_scroll_active = val_bar == max_bar;
+ }
+ }
+ if (data.endsWith('\n'))
+ data = data.left(data.length() - 1);
+ QStringList paragraphs = data.split('\n');
+ for (QString &paragraph : paragraphs)
+ {
+ paragraph = paragraph.trimmed();
+ }
+
+ QListIterator<QString> iter(paragraphs);
+ if (mode == MessageLevel::MultiMC)
+ while (iter.hasNext())
+ writeColor(iter.next(), "blue");
+ else if (mode == MessageLevel::Error)
+ while (iter.hasNext())
+ writeColor(iter.next(), "red");
+ else if (mode == MessageLevel::Warning)
+ while (iter.hasNext())
+ writeColor(iter.next(), "orange");
+ else if (mode == MessageLevel::Fatal)
+ while (iter.hasNext())
+ writeColor(iter.next(), "pink");
+ else if (mode == MessageLevel::Debug)
+ while (iter.hasNext())
+ writeColor(iter.next(), "green");
+ else if (mode == MessageLevel::PrePost)
+ while (iter.hasNext())
+ writeColor(iter.next(), "grey");
+ // TODO: implement other MessageLevels
+ else
+ while (iter.hasNext())
+ writeColor(iter.next());
+ if(isVisible())
+ {
+ if (m_scroll_active)
+ {
+ bar->setValue(bar->maximum());
+ }
+ m_last_scroll_value = bar->value();
+ }
+}
+
+void ConsoleWindow::clear()
+{
+ ui->text->clear();
+}
+
+void ConsoleWindow::on_closeButton_clicked()
+{
+ close();
+}
+
+void ConsoleWindow::setMayClose(bool mayclose)
+{
+ if(mayclose)
+ ui->closeButton->setText(tr("Close"));
+ else
+ ui->closeButton->setText(tr("Hide"));
+ m_mayclose = mayclose;
+}
+
+void ConsoleWindow::toggleConsole()
+{
+ QScrollBar *bar = ui->text->verticalScrollBar();
+ if (isVisible())
+ {
+ int max_bar = bar->maximum();
+ int val_bar = m_last_scroll_value = bar->value();
+ m_scroll_active = (max_bar - val_bar) <= 1;
+ hide();
+ }
+ else
+ {
+ show();
+ if (m_scroll_active)
+ {
+ bar->setValue(bar->maximum());
+ }
+ else
+ {
+ bar->setValue(m_last_scroll_value);
+ }
+ }
+}
+
+void ConsoleWindow::closeEvent(QCloseEvent *event)
+{
+ if (!m_mayclose)
+ {
+ toggleConsole();
+ }
+ else
+ {
+ MMC->settings()->set("ConsoleWindowState", saveState().toBase64());
+ MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64());
+
+ emit isClosing();
+ m_trayIcon->hide();
+ QMainWindow::closeEvent(event);
+ }
+}
+
+void ConsoleWindow::on_btnKillMinecraft_clicked()
+{
+ ui->btnKillMinecraft->setEnabled(false);
+ auto response = CustomMessageBox::selectable(
+ this, tr("Kill Minecraft?"),
+ tr("This can cause the instance to get corrupted and should only be used if Minecraft "
+ "is frozen for some reason"),
+ QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
+ if (response == QMessageBox::Yes)
+ proc->killMinecraft();
+ else
+ ui->btnKillMinecraft->setEnabled(true);
+}
+
+void ConsoleWindow::onEnded(BaseInstance *instance, int code, QProcess::ExitStatus status)
+{
+ bool peacefulExit = code == 0 && status != QProcess::CrashExit;
+ ui->btnKillMinecraft->setEnabled(false);
+
+ setMayClose(true);
+
+ if (instance->settings().get("AutoCloseConsole").toBool())
+ {
+ if (peacefulExit)
+ {
+ this->close();
+ return;
+ }
+ }
+ /*
+ if(!peacefulExit)
+ {
+ m_trayIcon->showMessage(tr("Oh no!"), tr("Minecraft crashed!"), QSystemTrayIcon::Critical);
+ }
+ */
+ if (!isVisible())
+ show();
+}
+
+void ConsoleWindow::onLaunchFailed(BaseInstance *instance)
+{
+ ui->btnKillMinecraft->setEnabled(false);
+
+ setMayClose(true);
+
+ if (!isVisible())
+ show();
+}
+
+void ConsoleWindow::on_btnPaste_clicked()
+{
+ auto text = ui->text->toPlainText();
+ ProgressDialog dialog(this);
+ PasteUpload *paste = new PasteUpload(this, text);
+ dialog.exec(paste);
+ if (!paste->successful())
+ {
+ CustomMessageBox::selectable(this, "Upload failed", paste->failReason(),
+ QMessageBox::Critical)->exec();
+ }
+}
diff --git a/gui/ConsoleWindow.h b/gui/ConsoleWindow.h
new file mode 100644
index 00000000..9291320e
--- /dev/null
+++ b/gui/ConsoleWindow.h
@@ -0,0 +1,94 @@
+/* Copyright 2013 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 <QMainWindow>
+#include <QSystemTrayIcon>
+#include "logic/MinecraftProcess.h"
+
+namespace Ui
+{
+class ConsoleWindow;
+}
+
+class ConsoleWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit ConsoleWindow(MinecraftProcess *proc, QWidget *parent = 0);
+ ~ConsoleWindow();
+
+ /**
+ * @brief specify if the window is allowed to close
+ * @param mayclose
+ * used to keep it alive while MC runs
+ */
+ void setMayClose(bool mayclose);
+
+private:
+ /**
+ * @brief write a colored paragraph
+ * @param data the string
+ * @param color the css color name
+ * this will only insert a single paragraph.
+ * \n are ignored. a real \n is always appended.
+ */
+ void writeColor(QString data, const char *color = nullptr);
+
+signals:
+ void isClosing();
+
+public
+slots:
+ /**
+ * @brief write a string
+ * @param data the string
+ * @param mode the WriteMode
+ * lines have to be put through this as a whole!
+ */
+ void write(QString data, MessageLevel::Enum level = MessageLevel::MultiMC);
+
+ /**
+ * @brief clear the text widget
+ */
+ void clear();
+
+private
+slots:
+ void on_closeButton_clicked();
+ void on_btnKillMinecraft_clicked();
+ void onEnded(BaseInstance *instance, int code, QProcess::ExitStatus status);
+ void onLaunchFailed(BaseInstance *instance);
+
+ // FIXME: add handlers for the other MinecraftProcess signals (pre/post launch command
+ // failures)
+
+ void on_btnPaste_clicked();
+ void iconActivated(QSystemTrayIcon::ActivationReason);
+ void toggleConsole();
+protected:
+ void closeEvent(QCloseEvent *);
+
+private:
+ Ui::ConsoleWindow *ui = nullptr;
+ MinecraftProcess *proc = nullptr;
+ bool m_mayclose = true;
+ int m_last_scroll_value = 0;
+ bool m_scroll_active = true;
+ QSystemTrayIcon *m_trayIcon = nullptr;
+ int m_saved_offset = 0;
+};
diff --git a/gui/ConsoleWindow.ui b/gui/ConsoleWindow.ui
new file mode 100644
index 00000000..c2307ecc
--- /dev/null
+++ b/gui/ConsoleWindow.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConsoleWindow</class>
+ <widget class="QMainWindow" name="ConsoleWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>640</width>
+ <height>440</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MultiMC Console</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPlainTextEdit" name="text">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="plainText">
+ <string notr="true"/>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ <property name="centerOnScroll">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="btnPaste">
+ <property name="text">
+ <string>Upload Log</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnKillMinecraft">
+ <property name="text">
+ <string>&amp;Kill Minecraft</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
new file mode 100644
index 00000000..9977dc75
--- /dev/null
+++ b/gui/MainWindow.cpp
@@ -0,0 +1,1501 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Andrew Okin
+ * Peterix
+ * Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 "MultiMC.h"
+
+#include "MainWindow.h"
+#include "ui_MainWindow.h"
+
+#include <QMenu>
+#include <QMessageBox>
+#include <QInputDialog>
+
+#include <QDesktopServices>
+#include <QUrl>
+#include <QDir>
+#include <QFileInfo>
+#include <QLabel>
+#include <QToolButton>
+#include <QWidgetAction>
+
+#include "osutils.h"
+#include "userutils.h"
+#include "pathutils.h"
+
+#include "categorizedview.h"
+#include "categorydrawer.h"
+
+#include "gui/Platform.h"
+
+#include "gui/widgets/InstanceDelegate.h"
+#include "gui/widgets/LabeledToolButton.h"
+
+#include "gui/dialogs/SettingsDialog.h"
+#include "gui/dialogs/NewInstanceDialog.h"
+#include "gui/dialogs/ProgressDialog.h"
+#include "gui/dialogs/AboutDialog.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "gui/dialogs/CustomMessageBox.h"
+#include "gui/dialogs/LwjglSelectDialog.h"
+#include "gui/dialogs/InstanceSettings.h"
+#include "gui/dialogs/IconPickerDialog.h"
+#include "gui/dialogs/EditNotesDialog.h"
+#include "gui/dialogs/CopyInstanceDialog.h"
+#include "gui/dialogs/AccountListDialog.h"
+#include "gui/dialogs/AccountSelectDialog.h"
+#include "gui/dialogs/UpdateDialog.h"
+#include "gui/dialogs/EditAccountDialog.h"
+
+#include "gui/ConsoleWindow.h"
+
+#include "logic/lists/InstanceList.h"
+#include "logic/lists/MinecraftVersionList.h"
+#include "logic/lists/LwjglVersionList.h"
+#include "logic/icons/IconList.h"
+#include "logic/lists/JavaVersionList.h"
+
+#include "logic/auth/flows/AuthenticateTask.h"
+#include "logic/auth/flows/RefreshTask.h"
+
+#include "logic/updater/DownloadUpdateTask.h"
+
+#include "logic/news/NewsChecker.h"
+
+#include "logic/status/StatusChecker.h"
+
+#include "logic/net/URLConstants.h"
+
+#include "logic/BaseInstance.h"
+#include "logic/InstanceFactory.h"
+#include "logic/MinecraftProcess.h"
+#include "logic/OneSixUpdate.h"
+#include "logic/JavaUtils.h"
+#include "logic/NagUtils.h"
+#include "logic/SkinUtils.h"
+
+#include "logic/LegacyInstance.h"
+
+#include "logic/assets/AssetsUtils.h"
+#include "logic/assets/AssetsMigrateTask.h"
+#include <logic/updater/UpdateChecker.h>
+#include <logic/updater/NotificationChecker.h>
+#include <logic/tasks/ThreadTask.h>
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+
+ QString winTitle = QString("MultiMC 5 - Version %1").arg(MMC->version().toString());
+ if (!MMC->version().platform.isEmpty())
+ winTitle += " on " + MMC->version().platform;
+ setWindowTitle(winTitle);
+
+ // OSX magic.
+ // setUnifiedTitleAndToolBarOnMac(true);
+
+ // The instance action toolbar customizations
+ {
+ // disabled until we have an instance selected
+ ui->instanceToolBar->setEnabled(false);
+
+ // the rename label is inside the rename tool button
+ renameButton = new LabeledToolButton();
+ renameButton->setText("Instance Name");
+ renameButton->setToolTip(ui->actionRenameInstance->toolTip());
+ connect(renameButton, SIGNAL(clicked(bool)), SLOT(on_actionRenameInstance_triggered()));
+ ui->instanceToolBar->insertWidget(ui->actionLaunchInstance, renameButton);
+ ui->instanceToolBar->insertSeparator(ui->actionLaunchInstance);
+ renameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ }
+
+ // Add the news label to the news toolbar.
+ {
+ newsLabel = new QToolButton();
+ newsLabel->setIcon(QIcon::fromTheme("news"));
+ newsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
+ QObject::connect(newsLabel, &QAbstractButton::clicked, this,
+ &MainWindow::newsButtonClicked);
+ QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this,
+ &MainWindow::updateNewsLabel);
+ updateNewsLabel();
+ }
+
+ // Create the instance list widget
+ {
+ view = new KCategorizedView(ui->centralWidget);
+ drawer = new KCategoryDrawer(view);
+
+ view->setSelectionMode(QAbstractItemView::SingleSelection);
+ view->setCategoryDrawer(drawer);
+ view->setCollapsibleBlocks(true);
+ view->setViewMode(QListView::IconMode);
+ view->setFlow(QListView::LeftToRight);
+ view->setWordWrap(true);
+ view->setMouseTracking(true);
+ view->viewport()->setAttribute(Qt::WA_Hover);
+ auto delegate = new ListViewDelegate();
+ view->setItemDelegate(delegate);
+ view->setSpacing(10);
+ view->setUniformItemWidths(true);
+
+ // do not show ugly blue border on the mac
+ view->setAttribute(Qt::WA_MacShowFocusRect, false);
+
+ view->installEventFilter(this);
+
+ proxymodel = new InstanceProxyModel(this);
+ // proxymodel->setSortRole(KCategorizedSortFilterProxyModel::CategorySortRole);
+ // proxymodel->setFilterRole(KCategorizedSortFilterProxyModel::CategorySortRole);
+ // proxymodel->setDynamicSortFilter ( true );
+
+ // FIXME: instList should be global-ish, or at least not tied to the main window...
+ // maybe the application itself?
+ proxymodel->setSourceModel(MMC->instances().get());
+ proxymodel->sort(0);
+ view->setFrameShape(QFrame::NoFrame);
+ view->setModel(proxymodel);
+
+ view->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(view, SIGNAL(customContextMenuRequested(const QPoint &)), this,
+ SLOT(showInstanceContextMenu(const QPoint &)));
+
+ ui->horizontalLayout->addWidget(view);
+ }
+ // The cat background
+ {
+ bool cat_enable = MMC->settings()->get("TheCat").toBool();
+ ui->actionCAT->setChecked(cat_enable);
+ connect(ui->actionCAT, SIGNAL(toggled(bool)), SLOT(onCatToggled(bool)));
+ setCatBackground(cat_enable);
+ }
+ // start instance when double-clicked
+ connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this,
+ SLOT(instanceActivated(const QModelIndex &)));
+ // track the selection -- update the instance toolbar
+ connect(view->selectionModel(),
+ SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this,
+ SLOT(instanceChanged(const QModelIndex &, const QModelIndex &)));
+
+ // track icon changes and update the toolbar!
+ connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString)));
+
+ // model reset -> selection is invalid. All the instance pointers are wrong.
+ // FIXME: stop using POINTERS everywhere
+ connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad()));
+
+ m_statusLeft = new QLabel(tr("No instance selected"), this);
+ m_statusRight = new QLabel(tr("No status available"), this);
+ m_statusRefresh = new QToolButton(this);
+ m_statusRefresh->setCheckable(true);
+ m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ m_statusRefresh->setIcon(QIcon::fromTheme("refresh"));
+
+ statusBar()->addPermanentWidget(m_statusLeft, 1);
+ statusBar()->addPermanentWidget(m_statusRight, 0);
+ statusBar()->addPermanentWidget(m_statusRefresh, 0);
+
+ // Start status checker
+ {
+ connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this,
+ &MainWindow::updateStatusUI);
+ connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this,
+ &MainWindow::updateStatusFailedUI);
+
+ connect(m_statusRefresh, &QAbstractButton::clicked, this, &MainWindow::reloadStatus);
+ connect(&statusTimer, &QTimer::timeout, this, &MainWindow::reloadStatus);
+ statusTimer.setSingleShot(true);
+
+ reloadStatus();
+ }
+
+ // Add "manage accounts" button, right align
+ QWidget *spacer = new QWidget();
+ spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ ui->mainToolBar->addWidget(spacer);
+
+ accountMenu = new QMenu(this);
+ manageAccountsAction = new QAction(tr("Manage Accounts"), this);
+ manageAccountsAction->setCheckable(false);
+ connect(manageAccountsAction, SIGNAL(triggered(bool)), this,
+ SLOT(on_actionManageAccounts_triggered()));
+
+ repopulateAccountsMenu();
+
+ accountMenuButton = new QToolButton(this);
+ accountMenuButton->setText(tr("Accounts"));
+ accountMenuButton->setMenu(accountMenu);
+ accountMenuButton->setPopupMode(QToolButton::InstantPopup);
+ accountMenuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ accountMenuButton->setIcon(QIcon::fromTheme("noaccount"));
+
+ QWidgetAction *accountMenuButtonAction = new QWidgetAction(this);
+ accountMenuButtonAction->setDefaultWidget(accountMenuButton);
+
+ ui->mainToolBar->addAction(accountMenuButtonAction);
+
+ // Update the menu when the active account changes.
+ // Shouldn't have to use lambdas here like this, but if I don't, the compiler throws a fit.
+ // Template hell sucks...
+ connect(MMC->accounts().get(), &MojangAccountList::activeAccountChanged, [this]
+ { activeAccountChanged(); });
+ connect(MMC->accounts().get(), &MojangAccountList::listChanged, [this]
+ { repopulateAccountsMenu(); });
+
+ // Show initial account
+ activeAccountChanged();
+
+ auto accounts = MMC->accounts();
+
+ // TODO: Nicer way to iterate?
+ for (int i = 0; i < accounts->count(); i++)
+ {
+ auto account = accounts->at(i);
+ if (account != nullptr)
+ {
+ auto job = new NetJob("Startup player skins: " + account->username());
+
+ for (auto profile : account->profiles())
+ {
+ auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
+ auto action = CacheDownload::make(
+ QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"), meta);
+ job->addNetAction(action);
+ meta->stale = true;
+ }
+
+ connect(job, SIGNAL(succeeded()), SLOT(activeAccountChanged()));
+ job->start();
+ }
+ }
+
+ // run the things that load and download other things... FIXME: this is NOT the place
+ // FIXME: invisible actions in the background = NOPE.
+ {
+ if (!MMC->minecraftlist()->isLoaded())
+ {
+ m_versionLoadTask = MMC->minecraftlist()->getLoadTask();
+ startTask(m_versionLoadTask);
+ }
+ if (!MMC->lwjgllist()->isLoaded())
+ {
+ MMC->lwjgllist()->loadList();
+ }
+
+ MMC->newsChecker()->reloadNews();
+ updateNewsLabel();
+
+ // set up the updater object.
+ auto updater = MMC->updateChecker();
+ connect(updater.get(), &UpdateChecker::updateAvailable, this,
+ &MainWindow::updateAvailable);
+ connect(updater.get(), &UpdateChecker::noUpdateFound, [this]()
+ {
+ CustomMessageBox::selectable(
+ this, tr("No update found."),
+ tr("No MultiMC update was found!\nYou are using the latest version."))->exec();
+ });
+ // if automatic update checks are allowed, start one.
+ if (MMC->settings()->get("AutoUpdate").toBool())
+ on_actionCheckUpdate_triggered();
+
+ connect(MMC->notificationChecker().get(),
+ &NotificationChecker::notificationCheckFinished, this,
+ &MainWindow::notificationsChanged);
+ }
+
+ setSelectedInstanceById(MMC->settings()->get("SelectedInstance").toString());
+
+ // removing this looks stupid
+ view->setFocus();
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+ delete proxymodel;
+ delete drawer;
+}
+
+void MainWindow::showInstanceContextMenu(const QPoint &pos)
+{
+ if (!view->indexAt(pos).isValid())
+ {
+ return;
+ }
+
+ QList<QAction *> actions = ui->instanceToolBar->actions();
+
+ // HACK: Filthy rename button hack because the instance view is getting rewritten anyway
+ QAction *actionRename;
+ actionRename = new QAction(tr("Rename"), this);
+ actionRename->setToolTip(ui->actionRenameInstance->toolTip());
+
+ connect(actionRename, SIGNAL(triggered(bool)), SLOT(on_actionRenameInstance_triggered()));
+
+ actions.replace(1, actionRename);
+
+ QMenu myMenu;
+ myMenu.addActions(actions);
+ myMenu.exec(view->mapToGlobal(pos));
+}
+
+void MainWindow::repopulateAccountsMenu()
+{
+ accountMenu->clear();
+
+ std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
+ MojangAccountPtr active_account = accounts->activeAccount();
+
+ QString active_username = "";
+ if (active_account != nullptr)
+ {
+ active_username = accounts->activeAccount()->username();
+ }
+
+ if (accounts->count() <= 0)
+ {
+ QAction *action = new QAction(tr("No accounts added!"), this);
+ action->setEnabled(false);
+ accountMenu->addAction(action);
+
+ accountMenu->addSeparator();
+ }
+ else
+ {
+ // TODO: Nicer way to iterate?
+ for (int i = 0; i < accounts->count(); i++)
+ {
+ MojangAccountPtr account = accounts->at(i);
+
+ // Styling hack
+ QAction *section = new QAction(account->username(), this);
+ section->setEnabled(false);
+ accountMenu->addAction(section);
+
+ for (auto profile : account->profiles())
+ {
+ QAction *action = new QAction(profile.name, this);
+ action->setData(account->username());
+ action->setCheckable(true);
+ if (active_username == account->username())
+ {
+ action->setChecked(true);
+ }
+
+ action->setIcon(SkinUtils::getFaceFromCache(profile.name));
+ accountMenu->addAction(action);
+ connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
+ }
+
+ accountMenu->addSeparator();
+ }
+ }
+
+ QAction *action = new QAction(tr("No Default Account"), this);
+ action->setCheckable(true);
+ action->setIcon(QIcon::fromTheme("noaccount"));
+ action->setData("");
+ if (active_username.isEmpty())
+ {
+ action->setChecked(true);
+ }
+
+ accountMenu->addAction(action);
+ connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
+
+ accountMenu->addSeparator();
+ accountMenu->addAction(manageAccountsAction);
+}
+
+/*
+ * Assumes the sender is a QAction
+ */
+void MainWindow::changeActiveAccount()
+{
+ QAction *sAction = (QAction *)sender();
+ // Profile's associated Mojang username
+ // Will need to change when profiles are properly implemented
+ if (sAction->data().type() != QVariant::Type::String)
+ return;
+
+ QVariant data = sAction->data();
+ QString id = "";
+ if (!data.isNull())
+ {
+ id = data.toString();
+ }
+
+ MMC->accounts()->setActiveAccount(id);
+
+ activeAccountChanged();
+}
+
+void MainWindow::activeAccountChanged()
+{
+ repopulateAccountsMenu();
+
+ MojangAccountPtr account = MMC->accounts()->activeAccount();
+
+ if (account != nullptr && account->username() != "")
+ {
+ const AccountProfile *profile = account->currentProfile();
+ if (profile != nullptr)
+ {
+ accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name));
+ return;
+ }
+ }
+
+ // Set the icon to the "no account" icon.
+ accountMenuButton->setIcon(QIcon::fromTheme("noaccount"));
+}
+
+bool MainWindow::eventFilter(QObject *obj, QEvent *ev)
+{
+ if (obj == view)
+ {
+ if (ev->type() == QEvent::KeyPress)
+ {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ on_actionLaunchInstance_triggered();
+ return true;
+ case Qt::Key_Delete:
+ on_actionDeleteInstance_triggered();
+ return true;
+ case Qt::Key_F5:
+ on_actionRefresh_triggered();
+ return true;
+ case Qt::Key_F2:
+ on_actionRenameInstance_triggered();
+ return true;
+ default:
+ break;
+ }
+ }
+ }
+ return QMainWindow::eventFilter(obj, ev);
+}
+
+void MainWindow::updateNewsLabel()
+{
+ auto newsChecker = MMC->newsChecker();
+ if (newsChecker->isLoadingNews())
+ {
+ newsLabel->setText(tr("Loading news..."));
+ newsLabel->setEnabled(false);
+ }
+ else
+ {
+ QList<NewsEntryPtr> entries = newsChecker->getNewsEntries();
+ if (entries.length() > 0)
+ {
+ newsLabel->setText(entries[0]->title);
+ newsLabel->setEnabled(true);
+ }
+ else
+ {
+ newsLabel->setText(tr("No news available."));
+ newsLabel->setEnabled(false);
+ }
+ }
+}
+
+static QString convertStatus(const QString &status)
+{
+ QString ret = "?";
+
+ if (status == "green")
+ ret = "↑";
+ else if (status == "yellow")
+ ret = "-";
+ else if (status == "red")
+ ret = "↓";
+
+ return "<span style=\"font-size:11pt; font-weight:600;\">" + ret + "</span>";
+}
+
+void MainWindow::reloadStatus()
+{
+ m_statusRefresh->setChecked(true);
+ MMC->statusChecker()->reloadStatus();
+ // updateStatusUI();
+}
+
+static QString makeStatusString(const QMap<QString, QString> statuses)
+{
+ QString status = "";
+ status += "Web: " + convertStatus(statuses["minecraft.net"]);
+ status += " Account: " + convertStatus(statuses["account.mojang.com"]);
+ status += " Skins: " + convertStatus(statuses["skins.minecraft.net"]);
+ status += " Auth: " + convertStatus(statuses["authserver.mojang.com"]);
+ status += " Session: " + convertStatus(statuses["sessionserver.mojang.com"]);
+
+ return status;
+}
+
+void MainWindow::updateStatusUI()
+{
+ auto statusChecker = MMC->statusChecker();
+ auto statuses = statusChecker->getStatusEntries();
+
+ QString status = makeStatusString(statuses);
+ m_statusRefresh->setChecked(false);
+
+ m_statusRight->setText(status);
+
+ statusTimer.start(60 * 1000);
+}
+
+void MainWindow::updateStatusFailedUI()
+{
+ m_statusRight->setText(makeStatusString(QMap<QString, QString>()));
+ m_statusRefresh->setChecked(false);
+
+ statusTimer.start(60 * 1000);
+}
+
+void MainWindow::updateAvailable(QString repo, QString versionName, int versionId)
+{
+ UpdateDialog dlg;
+ UpdateAction action = (UpdateAction)dlg.exec();
+ switch (action)
+ {
+ case UPDATE_LATER:
+ QLOG_INFO() << "Update will be installed later.";
+ break;
+ case UPDATE_NOW:
+ downloadUpdates(repo, versionId);
+ break;
+ case UPDATE_ONEXIT:
+ downloadUpdates(repo, versionId, true);
+ break;
+ }
+}
+
+QList<int> stringToIntList(const QString &string)
+{
+ QStringList split = string.split(',', QString::SkipEmptyParts);
+ QList<int> out;
+ for (int i = 0; i < split.size(); ++i)
+ {
+ out.append(split.at(i).toInt());
+ }
+ return out;
+}
+QString intListToString(const QList<int> &list)
+{
+ QStringList slist;
+ for (int i = 0; i < list.size(); ++i)
+ {
+ slist.append(QString::number(list.at(i)));
+ }
+ return slist.join(',');
+}
+void MainWindow::notificationsChanged()
+{
+ QList<NotificationChecker::NotificationEntry> entries =
+ MMC->notificationChecker()->notificationEntries();
+ QList<int> shownNotifications =
+ stringToIntList(MMC->settings()->get("ShownNotifications").toString());
+ for (auto it = entries.begin(); it != entries.end(); ++it)
+ {
+ NotificationChecker::NotificationEntry entry = *it;
+ if (!shownNotifications.contains(entry.id) && entry.applies())
+ {
+ QMessageBox::Icon icon;
+ switch (entry.type)
+ {
+ case NotificationChecker::NotificationEntry::Critical:
+ icon = QMessageBox::Critical;
+ break;
+ case NotificationChecker::NotificationEntry::Warning:
+ icon = QMessageBox::Warning;
+ break;
+ case NotificationChecker::NotificationEntry::Information:
+ icon = QMessageBox::Information;
+ break;
+ }
+
+ QMessageBox box(icon, tr("Notification"), entry.message, QMessageBox::Close, this);
+ QPushButton *dontShowAgainButton =
+ box.addButton(tr("Don't show again"), QMessageBox::AcceptRole);
+ box.setDefaultButton(QMessageBox::Close);
+ box.exec();
+ if (box.clickedButton() == dontShowAgainButton)
+ {
+ shownNotifications.append(entry.id);
+ }
+ }
+ }
+ MMC->settings()->set("ShownNotifications", intListToString(shownNotifications));
+}
+
+void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit)
+{
+ QLOG_INFO() << "Downloading updates.";
+ // TODO: If the user chooses to update on exit, we should download updates in the
+ // background.
+ // Doing so is a bit complicated, because we'd have to make sure it finished downloading
+ // before actually exiting MultiMC.
+ ProgressDialog updateDlg(this);
+ DownloadUpdateTask updateTask(repo, versionId, &updateDlg);
+ // If the task succeeds, install the updates.
+ if (updateDlg.exec(&updateTask))
+ {
+ UpdateFlags baseFlags = None;
+#ifdef MultiMC_UPDATER_DRY_RUN
+ baseFlags |= DryRun;
+#endif
+ if (installOnExit)
+ MMC->installUpdates(updateTask.updateFilesDir(), baseFlags | OnExit);
+ else
+ MMC->installUpdates(updateTask.updateFilesDir(), baseFlags | RestartOnFinish);
+ }
+}
+
+void MainWindow::onCatToggled(bool state)
+{
+ setCatBackground(state);
+ MMC->settings()->set("TheCat", state);
+}
+
+void MainWindow::setCatBackground(bool enabled)
+{
+ if (enabled)
+ {
+ view->setStyleSheet("QListView"
+ "{"
+ "background-image: url(:/backgrounds/kitteh);"
+ "background-attachment: fixed;"
+ "background-clip: padding;"
+ "background-position: top right;"
+ "background-repeat: none;"
+ "background-color:palette(base);"
+ "}");
+ }
+ else
+ {
+ view->setStyleSheet(QString());
+ }
+}
+
+void MainWindow::on_actionAddInstance_triggered()
+{
+ if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask &&
+ m_versionLoadTask->isRunning())
+ {
+ QEventLoop waitLoop;
+ waitLoop.connect(m_versionLoadTask, SIGNAL(failed(QString)), SLOT(quit()));
+ waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit()));
+ waitLoop.exec();
+ }
+
+ NewInstanceDialog newInstDlg(this);
+ if (!newInstDlg.exec())
+ return;
+
+ BaseInstance *newInstance = NULL;
+
+ QString instancesDir = MMC->settings()->get("InstanceDir").toString();
+ QString instDirName = DirNameFromString(newInstDlg.instName(), instancesDir);
+ QString instDir = PathCombine(instancesDir, instDirName);
+
+ auto &loader = InstanceFactory::get();
+
+ auto error = loader.createInstance(newInstance, newInstDlg.selectedVersion(), instDir);
+ QString errorMsg = QString("Failed to create instance %1: ").arg(instDirName);
+ switch (error)
+ {
+ case InstanceFactory::NoCreateError:
+ newInstance->setName(newInstDlg.instName());
+ newInstance->setIconKey(newInstDlg.iconKey());
+ MMC->instances()->add(InstancePtr(newInstance));
+ break;
+
+ case InstanceFactory::InstExists:
+ {
+ errorMsg += "An instance with the given directory name already exists.";
+ CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
+ return;
+ }
+
+ case InstanceFactory::CantCreateDir:
+ {
+ errorMsg += "Failed to create the instance directory.";
+ CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
+ return;
+ }
+
+ default:
+ {
+ errorMsg += QString("Unknown instance loader error %1").arg(error);
+ CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
+ return;
+ }
+ }
+
+ if (MMC->accounts()->anyAccountIsValid())
+ {
+ ProgressDialog loadDialog(this);
+ auto update = newInstance->doUpdate();
+ connect(update.get(), &Task::failed, [this](QString reason)
+ {
+ QString error = QString("Instance load failed: %1").arg(reason);
+ CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)
+ ->show();
+ });
+ loadDialog.exec(update.get());
+ }
+ else
+ {
+ CustomMessageBox::selectable(
+ this, tr("Error"),
+ tr("MultiMC cannot download Minecraft or update instances unless you have at least "
+ "one account added.\nPlease add your Mojang or Minecraft account."),
+ QMessageBox::Warning)->show();
+ }
+}
+
+void MainWindow::on_actionCopyInstance_triggered()
+{
+ if (!m_selectedInstance)
+ return;
+
+ CopyInstanceDialog copyInstDlg(m_selectedInstance, this);
+ if (!copyInstDlg.exec())
+ return;
+
+ QString instancesDir = MMC->settings()->get("InstanceDir").toString();
+ QString instDirName = DirNameFromString(copyInstDlg.instName(), instancesDir);
+ QString instDir = PathCombine(instancesDir, instDirName);
+
+ auto &loader = InstanceFactory::get();
+
+ BaseInstance *newInstance = NULL;
+ auto error = loader.copyInstance(newInstance, m_selectedInstance, instDir);
+
+ QString errorMsg = QString("Failed to create instance %1: ").arg(instDirName);
+ switch (error)
+ {
+ case InstanceFactory::NoCreateError:
+ newInstance->setName(copyInstDlg.instName());
+ newInstance->setIconKey(copyInstDlg.iconKey());
+ MMC->instances()->add(InstancePtr(newInstance));
+ return;
+
+ case InstanceFactory::InstExists:
+ {
+ errorMsg += "An instance with the given directory name already exists.";
+ CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
+ break;
+ }
+
+ case InstanceFactory::CantCreateDir:
+ {
+ errorMsg += "Failed to create the instance directory.";
+ CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
+ break;
+ }
+
+ default:
+ {
+ errorMsg += QString("Unknown instance loader error %1").arg(error);
+ CustomMessageBox::selectable(this, tr("Error"), errorMsg, QMessageBox::Warning)->show();
+ break;
+ }
+ }
+}
+
+void MainWindow::on_actionChangeInstIcon_triggered()
+{
+ if (!m_selectedInstance)
+ return;
+
+ IconPickerDialog dlg(this);
+ dlg.exec(m_selectedInstance->iconKey());
+ if (dlg.result() == QDialog::Accepted)
+ {
+ m_selectedInstance->setIconKey(dlg.selectedIconKey);
+ auto ico = MMC->icons()->getBigIcon(dlg.selectedIconKey);
+ ui->actionChangeInstIcon->setIcon(ico);
+ }
+}
+
+void MainWindow::iconUpdated(QString icon)
+{
+ if (icon == m_currentInstIcon)
+ {
+ ui->actionChangeInstIcon->setIcon(MMC->icons()->getBigIcon(m_currentInstIcon));
+ }
+}
+
+void MainWindow::updateInstanceToolIcon(QString new_icon)
+{
+ m_currentInstIcon = new_icon;
+ ui->actionChangeInstIcon->setIcon(MMC->icons()->getBigIcon(m_currentInstIcon));
+}
+
+void MainWindow::setSelectedInstanceById(const QString &id)
+{
+ QModelIndex selectionIndex = proxymodel->index(0, 0);
+ if (!id.isNull())
+ {
+ const QModelIndex index = MMC->instances()->getInstanceIndexById(id);
+ if (index.isValid())
+ {
+ selectionIndex = proxymodel->mapFromSource(index);
+ }
+ }
+ view->selectionModel()->setCurrentIndex(selectionIndex,
+ QItemSelectionModel::ClearAndSelect);
+}
+
+void MainWindow::on_actionChangeInstGroup_triggered()
+{
+ if (!m_selectedInstance)
+ return;
+
+ bool ok = false;
+ QString name(m_selectedInstance->group());
+ auto groups = MMC->instances()->getGroups();
+ groups.insert(0, "");
+ groups.sort(Qt::CaseInsensitive);
+ int foo = groups.indexOf(name);
+
+ name = QInputDialog::getItem(this, tr("Group name"), tr("Enter a new group name."), groups,
+ foo, true, &ok);
+ name = name.simplified();
+ if (ok)
+ m_selectedInstance->setGroupPost(name);
+}
+
+void MainWindow::on_actionViewInstanceFolder_triggered()
+{
+ QString str = MMC->settings()->get("InstanceDir").toString();
+ openDirInDefaultProgram(str);
+}
+
+void MainWindow::on_actionRefresh_triggered()
+{
+ MMC->instances()->loadList();
+}
+
+void MainWindow::on_actionViewCentralModsFolder_triggered()
+{
+ openDirInDefaultProgram(MMC->settings()->get("CentralModsDir").toString(), true);
+}
+
+void MainWindow::on_actionConfig_Folder_triggered()
+{
+ if (m_selectedInstance)
+ {
+ QString str = m_selectedInstance->instanceConfigFolder();
+ openDirInDefaultProgram(QDir(str).absolutePath());
+ }
+}
+
+void MainWindow::on_actionCheckUpdate_triggered()
+{
+ auto updater = MMC->updateChecker();
+
+ updater->checkForUpdate(true);
+}
+
+void MainWindow::on_actionSettings_triggered()
+{
+ SettingsDialog dialog(this);
+ dialog.exec();
+ // FIXME: quick HACK to make this work. improve, optimize.
+ proxymodel->invalidate();
+ proxymodel->sort(0);
+}
+
+void MainWindow::on_actionManageAccounts_triggered()
+{
+ AccountListDialog dialog(this);
+ dialog.exec();
+}
+
+void MainWindow::on_actionReportBug_triggered()
+{
+ openWebPage(QUrl("https://github.com/MultiMC/MultiMC5/issues"));
+}
+
+void MainWindow::on_actionMoreNews_triggered()
+{
+ openWebPage(QUrl("http://multimc.org/posts.html"));
+}
+
+void MainWindow::newsButtonClicked()
+{
+ QList<NewsEntryPtr> entries = MMC->newsChecker()->getNewsEntries();
+ if (entries.count() > 0)
+ openWebPage(QUrl(entries[0]->link));
+ else
+ openWebPage(QUrl("http://multimc.org/posts.html"));
+}
+
+void MainWindow::on_actionAbout_triggered()
+{
+ AboutDialog dialog(this);
+ dialog.exec();
+}
+
+void MainWindow::on_mainToolBar_visibilityChanged(bool)
+{
+ // Don't allow hiding the main toolbar.
+ // This is the only way I could find to prevent it... :/
+ ui->mainToolBar->setVisible(true);
+}
+
+void MainWindow::on_actionDeleteInstance_triggered()
+{
+ if (m_selectedInstance)
+ {
+ auto response = CustomMessageBox::selectable(
+ this, tr("CAREFUL"), tr("This is permanent! Are you sure?\nAbout to delete: ") +
+ m_selectedInstance->name(),
+ QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec();
+ if (response == QMessageBox::Yes)
+ {
+ m_selectedInstance->nuke();
+ }
+ }
+}
+
+void MainWindow::on_actionRenameInstance_triggered()
+{
+ if (m_selectedInstance)
+ {
+ bool ok = false;
+ QString name(m_selectedInstance->name());
+ name =
+ QInputDialog::getText(this, tr("Instance name"), tr("Enter a new instance name."),
+ QLineEdit::Normal, name, &ok);
+
+ if (name.length() > 0)
+ {
+ if (ok && name.length())
+ {
+ m_selectedInstance->setName(name);
+ renameButton->setText(name);
+ }
+ }
+ }
+}
+
+void MainWindow::on_actionViewSelectedInstFolder_triggered()
+{
+ if (m_selectedInstance)
+ {
+ QString str = m_selectedInstance->instanceRoot();
+ openDirInDefaultProgram(QDir(str).absolutePath());
+ }
+}
+
+void MainWindow::on_actionEditInstMods_triggered()
+{
+ if (m_selectedInstance)
+ {
+ auto dialog = m_selectedInstance->createModEditDialog(this);
+ if (dialog)
+ dialog->exec();
+ dialog->deleteLater();
+ }
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ // Save the window state and geometry.
+
+ MMC->settings()->set("MainWindowState", saveState().toBase64());
+ MMC->settings()->set("MainWindowGeometry", saveGeometry().toBase64());
+
+ QMainWindow::closeEvent(event);
+ QApplication::exit();
+}
+/*
+void MainWindow::on_instanceView_customContextMenuRequested(const QPoint &pos)
+{
+ QMenu *instContextMenu = new QMenu("Instance", this);
+
+ // Add the actions from the toolbar to the context menu.
+ instContextMenu->addActions(ui->instanceToolBar->actions());
+
+ instContextMenu->exec(view->mapToGlobal(pos));
+}
+*/
+void MainWindow::instanceActivated(QModelIndex index)
+{
+ if (!index.isValid())
+ return;
+
+ BaseInstance *inst =
+ (BaseInstance *)index.data(InstanceList::InstancePointerRole).value<void *>();
+
+ NagUtils::checkJVMArgs(inst->settings().get("JvmArgs").toString(), this);
+
+ doLaunch();
+}
+
+void MainWindow::on_actionLaunchInstance_triggered()
+{
+ if (m_selectedInstance)
+ {
+ NagUtils::checkJVMArgs(m_selectedInstance->settings().get("JvmArgs").toString(), this);
+ doLaunch();
+ }
+}
+
+void MainWindow::on_actionLaunchInstanceOffline_triggered()
+{
+ if (m_selectedInstance)
+ {
+ NagUtils::checkJVMArgs(m_selectedInstance->settings().get("JvmArgs").toString(), this);
+ doLaunch(false);
+ }
+}
+
+void MainWindow::doLaunch(bool online)
+{
+ if (!m_selectedInstance)
+ return;
+
+ // Find an account to use.
+ std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
+ MojangAccountPtr account = accounts->activeAccount();
+ if (accounts->count() <= 0)
+ {
+ // Tell the user they need to log in at least one account in order to play.
+ auto reply = CustomMessageBox::selectable(
+ this, tr("No Accounts"),
+ tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
+ "account logged in to MultiMC."
+ "Would you like to open the account manager to add an account now?"),
+ QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
+
+ if (reply == QMessageBox::Yes)
+ {
+ // Open the account manager.
+ on_actionManageAccounts_triggered();
+ }
+ }
+ else if (account.get() == nullptr)
+ {
+ // If no default account is set, ask the user which one to use.
+ AccountSelectDialog selectDialog(tr("Which account would you like to use?"),
+ AccountSelectDialog::GlobalDefaultCheckbox, this);
+
+ selectDialog.exec();
+
+ // Launch the instance with the selected account.
+ account = selectDialog.selectedAccount();
+
+ // If the user said to use the account as default, do that.
+ if (selectDialog.useAsGlobalDefault() && account.get() != nullptr)
+ accounts->setActiveAccount(account->username());
+ }
+
+ // if no account is selected, we bail
+ if (!account.get())
+ return;
+
+ // we try empty password first :)
+ QString password;
+ // we loop until the user succeeds in logging in or gives up
+ bool tryagain = true;
+ // the failure. the default failure.
+ QString failReason = tr("Your account is currently not logged in. Please enter "
+ "your password to log in again.");
+
+ while (tryagain)
+ {
+ AuthSessionPtr session(new AuthSession());
+ session->wants_online = online;
+ auto task = account->login(session, password);
+ if (task)
+ {
+ // We'll need to validate the access token to make sure the account
+ // is still logged in.
+ ProgressDialog progDialog(this);
+ if (online)
+ progDialog.setSkipButton(true, tr("Play Offline"));
+ progDialog.exec(task.get());
+ if (!task->successful())
+ {
+ failReason = task->failReason();
+ }
+ }
+ switch (session->status)
+ {
+ case AuthSession::Undetermined:
+ {
+ QLOG_ERROR() << "Received undetermined session status during login. Bye.";
+ tryagain = false;
+ break;
+ }
+ case AuthSession::RequiresPassword:
+ {
+ EditAccountDialog passDialog(failReason, this, EditAccountDialog::PasswordField);
+ if (passDialog.exec() == QDialog::Accepted)
+ {
+ password = passDialog.password();
+ }
+ else
+ {
+ tryagain = false;
+ }
+ break;
+ }
+ case AuthSession::PlayableOffline:
+ {
+ // we ask the user for a player name
+ bool ok = false;
+ QString usedname = session->player_name;
+ QString name = QInputDialog::getText(this, tr("Player name"),
+ tr("Choose your offline mode player name."),
+ QLineEdit::Normal, session->player_name, &ok);
+ if (!ok)
+ {
+ tryagain = false;
+ break;
+ }
+ if (name.length())
+ {
+ usedname = name;
+ }
+ session->MakeOffline(usedname);
+ // offline flavored game from here :3
+ }
+ case AuthSession::PlayableOnline:
+ {
+ // update first if the server actually responded
+ if (session->auth_server_online)
+ {
+ updateInstance(m_selectedInstance, session);
+ }
+ else
+ {
+ launchInstance(m_selectedInstance, session);
+ }
+ tryagain = false;
+ }
+ }
+ }
+}
+
+void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session)
+{
+ auto updateTask = instance->doUpdate();
+ if (!updateTask)
+ {
+ launchInstance(instance, session);
+ return;
+ }
+ ProgressDialog tDialog(this);
+ connect(updateTask.get(), &Task::succeeded, [this, instance, session]
+ { launchInstance(instance, session); });
+ connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
+ tDialog.exec(updateTask.get());
+}
+
+void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session)
+{
+ Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL");
+ Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL");
+
+ proc = instance->prepareForLaunch(session);
+ if (!proc)
+ return;
+
+ this->hide();
+
+ console = new ConsoleWindow(proc);
+ connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded()));
+
+ proc->setLogin(session);
+ proc->launch();
+}
+
+void MainWindow::onGameUpdateError(QString error)
+{
+ CustomMessageBox::selectable(this, tr("Error updating instance"), error,
+ QMessageBox::Warning)->show();
+}
+
+void MainWindow::taskStart()
+{
+ // Nothing to do here yet.
+}
+
+void MainWindow::taskEnd()
+{
+ QObject *sender = QObject::sender();
+ if (sender == m_versionLoadTask)
+ m_versionLoadTask = NULL;
+
+ sender->deleteLater();
+}
+
+void MainWindow::startTask(Task *task)
+{
+ connect(task, SIGNAL(started()), SLOT(taskStart()));
+ connect(task, SIGNAL(succeeded()), SLOT(taskEnd()));
+ connect(task, SIGNAL(failed(QString)), SLOT(taskEnd()));
+ task->start();
+}
+
+// Create A Desktop Shortcut
+void MainWindow::on_actionMakeDesktopShortcut_triggered()
+{
+ QString name("Test");
+ name = QInputDialog::getText(this, tr("MultiMC Shortcut"), tr("Enter a Shortcut Name."),
+ QLineEdit::Normal, name);
+
+ Util::createShortCut(Util::getDesktopDir(), QApplication::instance()->applicationFilePath(),
+ QStringList() << "-dl" << QDir::currentPath() << "test", name,
+ "application-x-octet-stream");
+
+ CustomMessageBox::selectable(
+ this, tr("Not useful"),
+ tr("A Dummy Shortcut was created. it will not do anything productive"),
+ QMessageBox::Warning)->show();
+}
+
+// BrowserDialog
+void MainWindow::openWebPage(QUrl url)
+{
+ QDesktopServices::openUrl(url);
+}
+
+void MainWindow::on_actionChangeInstMCVersion_triggered()
+{
+ if (view->selectionModel()->selectedIndexes().count() < 1)
+ return;
+
+ VersionSelectDialog vselect(m_selectedInstance->versionList().get(),
+ tr("Change Minecraft version"), this);
+ vselect.setFilter(1, "OneSix");
+ if (!vselect.exec() || !vselect.selectedVersion())
+ return;
+
+ if (!MMC->accounts()->anyAccountIsValid())
+ {
+ CustomMessageBox::selectable(
+ this, tr("Error"),
+ tr("MultiMC cannot download Minecraft or update instances unless you have at least "
+ "one account added.\nPlease add your Mojang or Minecraft account."),
+ QMessageBox::Warning)->show();
+ return;
+ }
+
+ if (m_selectedInstance->versionIsCustom())
+ {
+ auto result = CustomMessageBox::selectable(
+ this, tr("Are you sure?"),
+ tr("This will remove any library/version customization you did previously. "
+ "This includes things like Forge install and similar."),
+ QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
+ QMessageBox::Abort)->exec();
+
+ if (result != QMessageBox::Ok)
+ return;
+ }
+ m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
+
+ auto updateTask = m_selectedInstance->doUpdate();
+ if (!updateTask)
+ {
+ return;
+ }
+ ProgressDialog tDialog(this);
+ connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
+ tDialog.exec(updateTask.get());
+}
+
+void MainWindow::on_actionChangeInstLWJGLVersion_triggered()
+{
+ if (!m_selectedInstance)
+ return;
+
+ LWJGLSelectDialog lselect(this);
+ lselect.exec();
+ if (lselect.result() == QDialog::Accepted)
+ {
+ LegacyInstance *linst = (LegacyInstance *)m_selectedInstance;
+ linst->setLWJGLVersion(lselect.selectedVersion());
+ }
+}
+
+void MainWindow::on_actionInstanceSettings_triggered()
+{
+ if (view->selectionModel()->selectedIndexes().count() < 1)
+ return;
+
+ InstanceSettings settings(&m_selectedInstance->settings(), this);
+ settings.setWindowTitle(tr("Instance settings"));
+ settings.exec();
+}
+
+void MainWindow::instanceChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ if (current.isValid() &&
+ nullptr != (m_selectedInstance =
+ (BaseInstance *)current.data(InstanceList::InstancePointerRole)
+ .value<void *>()))
+ {
+ ui->instanceToolBar->setEnabled(true);
+ renameButton->setText(m_selectedInstance->name());
+ ui->actionChangeInstLWJGLVersion->setEnabled(
+ m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion"));
+ ui->actionEditInstMods->setEnabled(
+ m_selectedInstance->menuActionEnabled("actionEditInstMods"));
+ ui->actionChangeInstMCVersion->setEnabled(
+ m_selectedInstance->menuActionEnabled("actionChangeInstMCVersion"));
+ m_statusLeft->setText(m_selectedInstance->getStatusbarDescription());
+ updateInstanceToolIcon(m_selectedInstance->iconKey());
+
+ MMC->settings()->set("SelectedInstance", m_selectedInstance->id());
+ }
+ else
+ {
+ selectionBad();
+
+ MMC->settings()->set("SelectedInstance", QString());
+ }
+}
+
+void MainWindow::selectionBad()
+{
+ // start by reseting everything...
+ m_selectedInstance = nullptr;
+
+ statusBar()->clearMessage();
+ ui->instanceToolBar->setEnabled(false);
+ renameButton->setText(tr("Rename Instance"));
+ updateInstanceToolIcon("infinity");
+
+ // ...and then see if we can enable the previously selected instance
+ setSelectedInstanceById(MMC->settings()->get("SelectedInstance").toString());
+}
+
+void MainWindow::on_actionEditInstNotes_triggered()
+{
+ if (!m_selectedInstance)
+ return;
+ LegacyInstance *linst = (LegacyInstance *)m_selectedInstance;
+
+ EditNotesDialog noteedit(linst->notes(), linst->name(), this);
+ noteedit.exec();
+ if (noteedit.result() == QDialog::Accepted)
+ {
+
+ linst->setNotes(noteedit.getText());
+ }
+}
+
+void MainWindow::instanceEnded()
+{
+ this->show();
+}
+
+void MainWindow::checkMigrateLegacyAssets()
+{
+ int legacyAssets = AssetsUtils::findLegacyAssets();
+ if (legacyAssets > 0)
+ {
+ ProgressDialog migrateDlg(this);
+ AssetsMigrateTask migrateTask(legacyAssets, &migrateDlg);
+ {
+ ThreadTask threadTask(&migrateTask);
+
+ if (migrateDlg.exec(&threadTask))
+ {
+ QLOG_INFO() << "Assets migration task completed successfully";
+ }
+ else
+ {
+ QLOG_INFO() << "Assets migration task reported failure";
+ }
+ }
+ }
+ else
+ {
+ QLOG_INFO() << "Didn't find any legacy assets to migrate";
+ }
+}
+
+void MainWindow::checkSetDefaultJava()
+{
+ bool askForJava = false;
+ {
+ QString currentHostName = QHostInfo::localHostName();
+ QString oldHostName = MMC->settings()->get("LastHostname").toString();
+ if (currentHostName != oldHostName)
+ {
+ MMC->settings()->set("LastHostname", currentHostName);
+ askForJava = true;
+ }
+ }
+
+ {
+ QString currentJavaPath = MMC->settings()->get("JavaPath").toString();
+ if (currentJavaPath.isEmpty())
+ {
+ askForJava = true;
+ }
+ }
+
+ if (askForJava)
+ {
+ QLOG_DEBUG() << "Java path needs resetting, showing Java selection dialog...";
+
+ JavaVersionPtr java;
+
+ VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this,
+ false);
+ vselect.setResizeOn(2);
+ vselect.exec();
+
+ if (vselect.selectedVersion())
+ java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
+ else
+ {
+ CustomMessageBox::selectable(
+ this, tr("Invalid version selected"),
+ tr("You didn't select a valid Java version, so MultiMC will "
+ "select the default. "
+ "You can change this in the settings dialog."),
+ QMessageBox::Warning)->show();
+
+ JavaUtils ju;
+ java = ju.GetDefaultJava();
+ }
+ if (java)
+ MMC->settings()->set("JavaPath", java->path);
+ else
+ MMC->settings()->set("JavaPath", QString("java"));
+ }
+}
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
new file mode 100644
index 00000000..eeba2c26
--- /dev/null
+++ b/gui/MainWindow.h
@@ -0,0 +1,211 @@
+/* Copyright 2013 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 <QMainWindow>
+#include <QProcess>
+#include <QTimer>
+
+#include "logic/lists/InstanceList.h"
+#include "logic/BaseInstance.h"
+
+#include "logic/auth/MojangAccount.h"
+
+class QToolButton;
+class LabeledToolButton;
+class QLabel;
+class InstanceProxyModel;
+class KCategorizedView;
+class KCategoryDrawer;
+class MinecraftProcess;
+class ConsoleWindow;
+
+namespace Ui
+{
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+ void closeEvent(QCloseEvent *event);
+
+ // Browser Dialog
+ void openWebPage(QUrl url);
+
+ void checkSetDefaultJava();
+ void checkMigrateLegacyAssets();
+
+private
+slots:
+ void onCatToggled(bool);
+
+ void on_actionAbout_triggered();
+
+ void on_actionAddInstance_triggered();
+
+ void on_actionCopyInstance_triggered();
+
+ void on_actionChangeInstGroup_triggered();
+
+ void on_actionChangeInstIcon_triggered();
+
+ void on_actionViewInstanceFolder_triggered();
+
+ void on_actionConfig_Folder_triggered();
+
+ void on_actionViewSelectedInstFolder_triggered();
+
+ void on_actionRefresh_triggered();
+
+ void on_actionViewCentralModsFolder_triggered();
+
+ void on_actionCheckUpdate_triggered();
+
+ void on_actionSettings_triggered();
+
+ void on_actionManageAccounts_triggered();
+
+ void on_actionReportBug_triggered();
+
+ void on_actionMoreNews_triggered();
+
+ void newsButtonClicked();
+
+ void on_mainToolBar_visibilityChanged(bool);
+
+ // void on_instanceView_customContextMenuRequested(const QPoint &pos);
+
+ void on_actionLaunchInstance_triggered();
+
+ void on_actionLaunchInstanceOffline_triggered();
+
+ void on_actionDeleteInstance_triggered();
+
+ void on_actionRenameInstance_triggered();
+
+ void on_actionMakeDesktopShortcut_triggered();
+
+ void on_actionChangeInstMCVersion_triggered();
+
+ void on_actionEditInstMods_triggered();
+
+ void on_actionEditInstNotes_triggered();
+
+ /*!
+ * Launches the currently selected instance with the default account.
+ * If no default account is selected, prompts the user to pick an account.
+ */
+ void doLaunch(bool online = true);
+
+ /*!
+ * Launches the given instance with the given account.
+ * This function assumes that the given account has a valid, usable access token.
+ */
+ void launchInstance(BaseInstance *instance, AuthSessionPtr session);
+
+ /*!
+ * Prepares the given instance for launch with the given account.
+ */
+ void updateInstance(BaseInstance *instance, AuthSessionPtr account);
+
+ void onGameUpdateError(QString error);
+
+ void taskStart();
+ void taskEnd();
+
+ void on_actionChangeInstLWJGLVersion_triggered();
+
+ void instanceEnded();
+
+ void on_actionInstanceSettings_triggered();
+
+ // called when an icon is changed in the icon model.
+ void iconUpdated(QString);
+
+ void showInstanceContextMenu(const QPoint&);
+
+public
+slots:
+ void instanceActivated(QModelIndex);
+
+ void instanceChanged(const QModelIndex &current, const QModelIndex &previous);
+
+ void selectionBad();
+
+ void startTask(Task *task);
+
+ void updateAvailable(QString repo, QString versionName, int versionId);
+
+ void notificationsChanged();
+
+ void activeAccountChanged();
+
+ void changeActiveAccount();
+
+ void repopulateAccountsMenu();
+
+ void updateNewsLabel();
+
+ void updateStatusUI();
+
+ void updateStatusFailedUI();
+
+ void reloadStatus();
+
+ /*!
+ * Runs the DownloadUpdateTask and installs updates.
+ */
+ void downloadUpdates(QString repo, int versionId, bool installOnExit=false);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *ev);
+ void setCatBackground(bool enabled);
+ void updateInstanceToolIcon(QString new_icon);
+
+ void setSelectedInstanceById(const QString &id);
+
+private:
+ Ui::MainWindow *ui;
+ KCategoryDrawer *drawer;
+ KCategorizedView *view;
+ InstanceProxyModel *proxymodel;
+ MinecraftProcess *proc;
+ ConsoleWindow *console;
+ LabeledToolButton *renameButton;
+ QToolButton *changeIconButton;
+ QToolButton* newsLabel;
+
+ BaseInstance *m_selectedInstance;
+ QString m_currentInstIcon;
+
+ Task *m_versionLoadTask;
+
+ QLabel *m_statusLeft;
+ QLabel *m_statusRight;
+ QToolButton *m_statusRefresh;
+
+ QMenu *accountMenu;
+ QToolButton *accountMenuButton;
+ QAction *manageAccountsAction;
+
+ QTimer statusTimer;
+};
diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui
new file mode 100644
index 00000000..8cf26d18
--- /dev/null
+++ b/gui/MainWindow.ui
@@ -0,0 +1,539 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>694</width>
+ <height>563</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MultiMC 5</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../resources/multimc/multimc.qrc">
+ <normaloff>:/icons/multimc/scalable/apps/multimc.svg</normaloff>:/icons/multimc/scalable/apps/multimc.svg</iconset>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <property name="windowTitle">
+ <string>Main Toolbar</string>
+ </property>
+ <property name="movable">
+ <bool>false</bool>
+ </property>
+ <property name="allowedAreas">
+ <set>Qt::TopToolBarArea</set>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonIconOnly</enum>
+ </property>
+ <property name="floatable">
+ <bool>false</bool>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="actionAddInstance"/>
+ <addaction name="actionCopyInstance"/>
+ <addaction name="separator"/>
+ <addaction name="actionViewInstanceFolder"/>
+ <addaction name="actionViewCentralModsFolder"/>
+ <addaction name="actionRefresh"/>
+ <addaction name="separator"/>
+ <addaction name="actionCheckUpdate"/>
+ <addaction name="actionSettings"/>
+ <addaction name="separator"/>
+ <addaction name="actionReportBug"/>
+ <addaction name="actionAbout"/>
+ <addaction name="separator"/>
+ <addaction name="actionCAT"/>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ <widget class="QToolBar" name="instanceToolBar">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="windowTitle">
+ <string>Instance Toolbar</string>
+ </property>
+ <property name="allowedAreas">
+ <set>Qt::LeftToolBarArea|Qt::RightToolBarArea</set>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>80</width>
+ <height>80</height>
+ </size>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonIconOnly</enum>
+ </property>
+ <property name="floatable">
+ <bool>false</bool>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>RightToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="actionChangeInstIcon"/>
+ <addaction name="actionLaunchInstance"/>
+ <addaction name="actionLaunchInstanceOffline"/>
+ <addaction name="separator"/>
+ <addaction name="actionEditInstNotes"/>
+ <addaction name="actionChangeInstGroup"/>
+ <addaction name="separator"/>
+ <addaction name="actionInstanceSettings"/>
+ <addaction name="actionChangeInstMCVersion"/>
+ <addaction name="actionChangeInstLWJGLVersion"/>
+ <addaction name="actionEditInstMods"/>
+ <addaction name="actionViewSelectedInstFolder"/>
+ <addaction name="actionConfig_Folder"/>
+ <addaction name="separator"/>
+ <addaction name="actionDeleteInstance"/>
+ </widget>
+ <widget class="QToolBar" name="newsToolBar">
+ <property name="windowTitle">
+ <string>News Toolbar</string>
+ </property>
+ <property name="movable">
+ <bool>false</bool>
+ </property>
+ <property name="allowedAreas">
+ <set>Qt::BottomToolBarArea</set>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ <property name="floatable">
+ <bool>false</bool>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>BottomToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="actionMoreNews"/>
+ </widget>
+ <action name="actionAddInstance">
+ <property name="icon">
+ <iconset theme="new">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Add Instance</string>
+ </property>
+ <property name="toolTip">
+ <string>Add a new instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Add a new instance.</string>
+ </property>
+ </action>
+ <action name="actionViewInstanceFolder">
+ <property name="icon">
+ <iconset theme="viewfolder">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>View Instance Folder</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the instance folder in a file browser.</string>
+ </property>
+ <property name="statusTip">
+ <string>Open the instance folder in a file browser.</string>
+ </property>
+ </action>
+ <action name="actionRefresh">
+ <property name="icon">
+ <iconset theme="refresh">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Refresh</string>
+ </property>
+ <property name="toolTip">
+ <string>Reload the instance list.</string>
+ </property>
+ <property name="statusTip">
+ <string>Reload the instance list.</string>
+ </property>
+ </action>
+ <action name="actionViewCentralModsFolder">
+ <property name="icon">
+ <iconset theme="centralmods">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>View Central Mods Folder</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the central mods folder in a file browser.</string>
+ </property>
+ <property name="statusTip">
+ <string>Open the central mods folder in a file browser.</string>
+ </property>
+ </action>
+ <action name="actionCheckUpdate">
+ <property name="icon">
+ <iconset theme="checkupdate">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Check for Updates</string>
+ </property>
+ <property name="toolTip">
+ <string>Check for new updates for MultiMC</string>
+ </property>
+ <property name="statusTip">
+ <string>Check for new updates for MultiMC</string>
+ </property>
+ </action>
+ <action name="actionSettings">
+ <property name="icon">
+ <iconset theme="settings">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ <property name="toolTip">
+ <string>Change settings.</string>
+ </property>
+ <property name="statusTip">
+ <string>Change settings.</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::PreferencesRole</enum>
+ </property>
+ </action>
+ <action name="actionReportBug">
+ <property name="icon">
+ <iconset theme="bug">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Report a Bug</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the bug tracker to report a bug with MultiMC.</string>
+ </property>
+ <property name="statusTip">
+ <string>Open the bug tracker to report a bug with MultiMC.</string>
+ </property>
+ </action>
+ <action name="actionMoreNews">
+ <property name="icon">
+ <iconset theme="news">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>More News</string>
+ </property>
+ <property name="iconText">
+ <string>More news...</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the MultiMC development blog to read more news about MultiMC.</string>
+ </property>
+ <property name="statusTip">
+ <string>Open the MultiMC development blog to read more news about MultiMC.</string>
+ </property>
+ </action>
+ <action name="actionAbout">
+ <property name="icon">
+ <iconset theme="about">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>About MultiMC</string>
+ </property>
+ <property name="toolTip">
+ <string>View information about MultiMC.</string>
+ </property>
+ <property name="statusTip">
+ <string>About MultiMC</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::AboutRole</enum>
+ </property>
+ </action>
+ <action name="actionLaunchInstance">
+ <property name="text">
+ <string>Play</string>
+ </property>
+ <property name="toolTip">
+ <string>Launch the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Launch the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionRenameInstance">
+ <property name="text">
+ <string>Instance Name</string>
+ </property>
+ <property name="toolTip">
+ <string>Rename the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Rename the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionChangeInstGroup">
+ <property name="text">
+ <string>Change Group</string>
+ </property>
+ <property name="toolTip">
+ <string>Change the selected instance's group.</string>
+ </property>
+ <property name="statusTip">
+ <string>Change the selected instance's group.</string>
+ </property>
+ </action>
+ <action name="actionChangeInstIcon">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="../resources/instances/instances.qrc">
+ <normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
+ </property>
+ <property name="text">
+ <string>Change Icon</string>
+ </property>
+ <property name="toolTip">
+ <string>Change the selected instance's icon.</string>
+ </property>
+ <property name="statusTip">
+ <string>Change the selected instance's icon.</string>
+ </property>
+ <property name="iconVisibleInMenu">
+ <bool>true</bool>
+ </property>
+ </action>
+ <action name="actionEditInstNotes">
+ <property name="text">
+ <string>Edit Notes</string>
+ </property>
+ <property name="toolTip">
+ <string>Edit the notes for the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Edit the notes for the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionInstanceSettings">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ <property name="toolTip">
+ <string>Change settings for the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Change settings for the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionMakeDesktopShortcut">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Make Shortcut</string>
+ </property>
+ <property name="toolTip">
+ <string>Make a shortcut on the desktop for the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Make a shortcut on the desktop for the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionManageInstSaves">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Manage Saves</string>
+ </property>
+ <property name="toolTip">
+ <string>Manage saves for the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Manage saves for the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionEditInstMods">
+ <property name="text">
+ <string>Edit Mods</string>
+ </property>
+ <property name="toolTip">
+ <string>Edit the mods for the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Edit the mods for the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionChangeInstMCVersion">
+ <property name="text">
+ <string>Change Version</string>
+ </property>
+ <property name="toolTip">
+ <string>Change the selected instance's Minecraft version.</string>
+ </property>
+ <property name="statusTip">
+ <string>Change the selected instance's Minecraft version.</string>
+ </property>
+ </action>
+ <action name="actionChangeInstLWJGLVersion">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Change LWJGL</string>
+ </property>
+ <property name="toolTip">
+ <string>Change the version of LWJGL for the selected instance to use.</string>
+ </property>
+ <property name="statusTip">
+ <string>Change the version of LWJGL for the selected instance to use.</string>
+ </property>
+ </action>
+ <action name="actionViewSelectedInstFolder">
+ <property name="text">
+ <string>Instance Folder</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the selected instance's root folder in a file browser.</string>
+ </property>
+ <property name="statusTip">
+ <string>Open the selected instance's root folder in a file browser.</string>
+ </property>
+ </action>
+ <action name="actionDeleteInstance">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ <property name="toolTip">
+ <string>Delete the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Delete the selected instance.</string>
+ </property>
+ </action>
+ <action name="actionConfig_Folder">
+ <property name="text">
+ <string>Config Folder</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the instance's config folder</string>
+ </property>
+ </action>
+ <action name="actionCAT">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset theme="cat">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Meow</string>
+ </property>
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;It's a fluffy kitty :3&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </action>
+ <action name="actionCopyInstance">
+ <property name="icon">
+ <iconset theme="copy">
+ <normaloff/>
+ </iconset>
+ </property>
+ <property name="text">
+ <string>Copy Instance</string>
+ </property>
+ <property name="toolTip">
+ <string>Copy the selected instance.</string>
+ </property>
+ <property name="statusTip">
+ <string>Add a new instance.</string>
+ </property>
+ </action>
+ <action name="actionManageAccounts">
+ <property name="text">
+ <string>Manage Accounts</string>
+ </property>
+ <property name="toolTip">
+ <string>Manage your Mojang or Minecraft accounts.</string>
+ </property>
+ </action>
+ <action name="actionLaunchInstanceOffline">
+ <property name="text">
+ <string>Play Offline</string>
+ </property>
+ <property name="toolTip">
+ <string>Launch the selected instance in offline mode.</string>
+ </property>
+ <property name="statusTip">
+ <string>Launch the selected instance.</string>
+ </property>
+ </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources>
+ <include location="../resources/instances/instances.qrc"/>
+ <include location="../resources/multimc/multimc.qrc"/>
+ <include location="../resources/backgrounds/backgrounds.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/gui/Platform.h b/gui/Platform.h
new file mode 100644
index 00000000..5cde9505
--- /dev/null
+++ b/gui/Platform.h
@@ -0,0 +1,32 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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
+
+/**
+ * @file Platform.h
+ * This file contains platform-specific functions, tweaks and fixes.
+ */
+
+#include <QWidget>
+
+class MultiMCPlatform
+{
+public:
+ // X11 WM_CLASS
+ static void fixWM_CLASS(QWidget *widget);
+};
diff --git a/gui/Platform_Other.cpp b/gui/Platform_Other.cpp
new file mode 100644
index 00000000..e02bd921
--- /dev/null
+++ b/gui/Platform_Other.cpp
@@ -0,0 +1,27 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <gui/Platform.h>
+/**
+ * Stub for non-X11 platforms
+ * @brief MultiMCPlatform::fixWM_CLASS
+ * @param widget
+ */
+void MultiMCPlatform::fixWM_CLASS(QWidget *widget)
+{
+ Q_UNUSED(widget);
+}
diff --git a/gui/Platform_X11.cpp b/gui/Platform_X11.cpp
new file mode 100644
index 00000000..bcd26f53
--- /dev/null
+++ b/gui/Platform_X11.cpp
@@ -0,0 +1,62 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <gui/Platform.h>
+#include <QtX11Extras/QX11Info>
+#include <xcb/xcb.h>
+
+static QByteArray WM_CLASS = "MultiMC5\0MultiMC5";
+
+template <typename... ArgTypes, typename... ArgTypes2>
+static inline unsigned int XcbCallVoid(xcb_void_cookie_t (*func)(xcb_connection_t *, ArgTypes...), ArgTypes2... args...)
+{
+ return func(QX11Info::connection(), args...).sequence;
+}
+
+static void getAtoms(size_t n, xcb_atom_t *atoms, const char *const names[], bool create)
+{
+ xcb_connection_t *conn = QX11Info::connection();
+ xcb_intern_atom_cookie_t *cookies = (xcb_intern_atom_cookie_t *)malloc(sizeof(xcb_intern_atom_cookie_t) * 2);
+ for (size_t i = 0; i < n; ++i)
+ cookies[i] = xcb_intern_atom(conn, create, strlen(names[i]), names[i]);
+ memset(atoms, 0, sizeof(xcb_atom_t) * n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(conn, cookies[i], 0);
+ if (r)
+ {
+ atoms[i] = r->atom;
+ free(r);
+ }
+ }
+ free(cookies);
+}
+
+static inline xcb_atom_t getAtom(const char *name, bool create=false)
+{
+ xcb_atom_t atom;
+ getAtoms(1, &atom, &name, create);
+ return atom;
+}
+
+void MultiMCPlatform::fixWM_CLASS(QWidget *widget)
+{
+ static const xcb_atom_t atom = getAtom("WM_CLASS");
+ XcbCallVoid(xcb_change_property, XCB_PROP_MODE_REPLACE,
+ widget->winId(), atom, XCB_ATOM_STRING, 8, WM_CLASS.count(),
+ WM_CLASS.constData());
+}
diff --git a/gui/dialogs/AboutDialog.cpp b/gui/dialogs/AboutDialog.cpp
new file mode 100644
index 00000000..35c85815
--- /dev/null
+++ b/gui/dialogs/AboutDialog.cpp
@@ -0,0 +1,54 @@
+/* Copyright 2013 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 "AboutDialog.h"
+#include "ui_AboutDialog.h"
+#include <QIcon>
+#include "MultiMC.h"
+#include "gui/Platform.h"
+
+AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+
+ ui->urlLabel->setOpenExternalLinks(true);
+
+ ui->icon->setPixmap(QIcon(":/icons/multimc/scalable/apps/multimc.svg").pixmap(64));
+ ui->title->setText("MultiMC 5 " + MMC->version().toString());
+
+ ui->versionLabel->setText(tr("Version") +": " + MMC->version().toString());
+ ui->vtypeLabel->setText(tr("Version Type") +": " + MMC->version().typeName());
+ ui->platformLabel->setText(tr("Platform") +": " + MMC->version().platform);
+
+ if (MMC->version().build >= 0)
+ ui->buildNumLabel->setText(tr("Build Number") +": " + QString::number(MMC->version().build));
+ else
+ ui->buildNumLabel->setVisible(false);
+
+ if (!MMC->version().channel.isEmpty())
+ ui->channelLabel->setText(tr("Channel") +": " + MMC->version().channel);
+ else
+ ui->channelLabel->setVisible(false);
+
+ connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
+
+ MMC->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt()));
+}
+
+AboutDialog::~AboutDialog()
+{
+ delete ui;
+}
diff --git a/gui/dialogs/AboutDialog.h b/gui/dialogs/AboutDialog.h
new file mode 100644
index 00000000..9d747bac
--- /dev/null
+++ b/gui/dialogs/AboutDialog.h
@@ -0,0 +1,35 @@
+/* Copyright 2013 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 <QDialog>
+
+namespace Ui
+{
+class AboutDialog;
+}
+
+class AboutDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AboutDialog(QWidget *parent = 0);
+ ~AboutDialog();
+
+private:
+ Ui::AboutDialog *ui;
+};
diff --git a/gui/dialogs/AboutDialog.ui b/gui/dialogs/AboutDialog.ui
new file mode 100644
index 00000000..64a355d3
--- /dev/null
+++ b/gui/dialogs/AboutDialog.ui
@@ -0,0 +1,543 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AboutDialog</class>
+ <widget class="QDialog" name="AboutDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>707</width>
+ <height>593</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>About MultiMC</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="icon">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="../../graphics.qrc">:/icons/multimc/scalable/apps/multimc.svg</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="title">
+ <property name="font">
+ <font>
+ <pointsize>15</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>MultiMC 5</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolBox" name="toolBox">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="aboutPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>689</width>
+ <height>311</height>
+ </rect>
+ </property>
+ <attribute name="label">
+ <string>About</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="versionLabel">
+ <property name="text">
+ <string>Version:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="vtypeLabel">
+ <property name="text">
+ <string>Version Type:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="platformLabel">
+ <property name="text">
+ <string>Platform:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="buildNumLabel">
+ <property name="text">
+ <string>Build Number:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="channelLabel">
+ <property name="text">
+ <string>Channel:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="aboutLabel">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="copyLabel">
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>© 2013 MultiMC Contributors</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="urlLabel">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/Forkk/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/MultiMC/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="creditsPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>689</width>
+ <height>311</height>
+ </rect>
+ </property>
+ <attribute name="label">
+ <string>Credits</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QTextEdit" name="creditsText">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Andrew Okin &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Petr Mrázek &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Sky &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://www.twitter.com/drayshak&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@drayshak&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Ubuntu'; font-size:10pt; font-weight:600;&quot;&gt;With thanks to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Orochimarufan &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;TakSuyu &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:taksuyu@gmail.com&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;taksuyu@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Kilobyte &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:stiepen22@gmx.de&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;stiepen22@gmx.de&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Jan (02JanDal) &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:02jandal@gmail.com&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;02jandal@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Robotbrain &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/skylordelros&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@skylordelros&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;Rootbear75 &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/rootbear75&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@rootbear75&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt;&quot;&gt;&amp;gt; (build server)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="translationInfo">
+ <property name="text">
+ <string extracomment="Hey, Translator, feel free to put credit to you here">No Language file loaded.</string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="licensePage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>689</width>
+ <height>311</height>
+ </rect>
+ </property>
+ <attribute name="label">
+ <string>License</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QTextEdit" name="licenseText">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans Mono</family>
+ </font>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans Mono'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012-2014 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;QSLog&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (c) 2010, Razvan Petru&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without modification,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions of source code must retain the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions in binary form must reproduce the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer in the documentation and/or other&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* The name of the contributors may not be used to endorse or promote products&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Group View (instance view)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; /*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 Rafael Fernández López &amp;lt;ereslibre@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 John Tapsell &amp;lt;tapsell@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is free software; you can redistribute it and/or&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * modify it under the terms of the GNU Library General Public&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * License as published by the Free Software Foundation; either&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * version 2 of the License, or (at your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Library General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You should have received a copy of the GNU Library General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * along with this library; see the file COPYING.LIB. If not, write to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Boston, MA 02110-1301, USA.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Pack200&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;The GNU General Public License (GPL)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Version 2, June 1991&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;+ &amp;quot;CLASSPATH&amp;quot; EXCEPTION TO THE GPL&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Certain source files distributed by Oracle America and/or its affiliates are&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;subject to the following clarification and special exception to the GPL, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;only where Oracle has expressly included in the particular source file's header&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the words &amp;quot;Oracle designates this particular file as subject to the &amp;quot;Classpath&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;exception as provided by Oracle in the LICENSE file that accompanied this code.&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; Linking this library statically or dynamically with other modules is making&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; a combined work based on this library. Thus, the terms and conditions of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the GNU General Public License cover the whole combination.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; As a special exception, the copyright holders of this library give you&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; permission to link this library with independent modules to produce an&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; executable, regardless of the license terms of these independent modules,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; and to copy and distribute the resulting executable under terms of your&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; choice, provided that you also meet, for each linked independent module,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the terms and conditions of the license of that module. An independent&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; module is a module which is not derived from or based on this library. If&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; you modify this library, you may extend this exception to your version of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the library, but you are not obligated to do so. If you do not wish to do&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; so, delete this exception statement from your version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Quazip&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (C) 2005-2011 Sergey A. Tachenov&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is free software; you can redistribute it and/or modify it&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the Free Software Foundation; either version 2 of the License, or (at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is distributed in the hope that it will be useful, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You should have received a copy of the GNU Lesser General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;along with this program; if not, write to the Free Software Foundation,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;See COPYING file for the full LGPL text.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Original ZIP package is copyrighted by Gilles Vollant, see&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;quazip/(un)zip.h files for details, basically it's zlib license.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;xz-minidec&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;/*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * XZ decompressor&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Authors: Lasse Collin &amp;lt;lasse.collin@tukaani.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Igor Pavlov &amp;lt;http://7-zip.org/&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This file has been put into the public domain.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You can do whatever you want with this file.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Java IconLoader class&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (c) 2011, Chris Molini&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;modification, are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Redistributions of source code must retain the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; notice, this list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Redistributions in binary form must reproduce the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; notice, this list of conditions and the following disclaimer in the&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; documentation and/or other materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Neither the name of the &amp;lt;organization&amp;gt; nor the&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; names of its contributors may be used to endorse or promote products&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; BE LIABLE FOR ANY&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="forkPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>689</width>
+ <height>311</height>
+ </rect>
+ </property>
+ <attribute name="label">
+ <string>Forking/Redistribution</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_33">
+ <item>
+ <widget class="QTextEdit" name="textEdit">
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Part of the reason for using the Apache license is we don't want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;span style=&quot; font-weight:600;&quot;&gt;without&lt;/span&gt; implying that you have our blessing.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="aboutQt">
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>About Qt</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../../graphics.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp
new file mode 100644
index 00000000..a38035a6
--- /dev/null
+++ b/gui/dialogs/AccountListDialog.cpp
@@ -0,0 +1,160 @@
+/* Copyright 2013 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 "AccountListDialog.h"
+#include "ui_AccountListDialog.h"
+
+#include <QItemSelectionModel>
+
+#include <logger/QsLog.h>
+
+#include <logic/net/NetJob.h>
+#include <logic/net/URLConstants.h>
+
+#include <gui/dialogs/EditAccountDialog.h>
+#include <gui/dialogs/ProgressDialog.h>
+#include <gui/dialogs/AccountSelectDialog.h>
+#include "CustomMessageBox.h"
+#include <logic/tasks/Task.h>
+#include <logic/auth/YggdrasilTask.h>
+
+#include <MultiMC.h>
+
+AccountListDialog::AccountListDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::AccountListDialog)
+{
+ ui->setupUi(this);
+
+ m_accounts = MMC->accounts();
+
+ ui->listView->setModel(m_accounts.get());
+ ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+
+ // Expand the account column
+ ui->listView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
+
+ QItemSelectionModel* selectionModel = ui->listView->selectionModel();
+
+ connect(selectionModel, &QItemSelectionModel::selectionChanged,
+ [this] (const QItemSelection& sel, const QItemSelection& dsel) { updateButtonStates(); });
+
+ connect(m_accounts.get(), SIGNAL(listChanged()), SLOT(listChanged()));
+ connect(m_accounts.get(), SIGNAL(activeAccountChanged()), SLOT(listChanged()));
+
+ updateButtonStates();
+}
+
+AccountListDialog::~AccountListDialog()
+{
+ delete ui;
+}
+
+void AccountListDialog::listChanged()
+{
+ updateButtonStates();
+}
+
+void AccountListDialog::on_addAccountBtn_clicked()
+{
+ addAccount(tr("Please enter your Mojang or Minecraft account username and password to add your account."));
+}
+
+void AccountListDialog::on_rmAccountBtn_clicked()
+{
+ QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
+ if (selection.size() > 0)
+ {
+ QModelIndex selected = selection.first();
+ m_accounts->removeAccount(selected);
+ }
+}
+
+void AccountListDialog::on_setDefaultBtn_clicked()
+{
+ QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
+ if (selection.size() > 0)
+ {
+ QModelIndex selected = selection.first();
+ MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
+ m_accounts->setActiveAccount(account->username());
+ }
+}
+
+void AccountListDialog::on_noDefaultBtn_clicked()
+{
+ m_accounts->setActiveAccount("");
+}
+
+void AccountListDialog::on_closeBtnBox_rejected()
+{
+ close();
+}
+
+void AccountListDialog::updateButtonStates()
+{
+ // If there is no selection, disable buttons that require something selected.
+ QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
+
+ ui->rmAccountBtn->setEnabled(selection.size() > 0);
+ ui->setDefaultBtn->setEnabled(selection.size() > 0);
+
+ ui->noDefaultBtn->setDown(m_accounts->activeAccount().get() == nullptr);
+}
+
+void AccountListDialog::addAccount(const QString& errMsg)
+{
+ // TODO: We can use the login dialog for this for now, but we'll have to make something better for it eventually.
+ EditAccountDialog loginDialog(errMsg, this, EditAccountDialog::UsernameField | EditAccountDialog::PasswordField);
+ loginDialog.exec();
+
+ if (loginDialog.result() == QDialog::Accepted)
+ {
+ QString username(loginDialog.username());
+ QString password(loginDialog.password());
+
+ MojangAccountPtr account = MojangAccount::createFromUsername(username);
+ ProgressDialog progDialog(this);
+ auto task = account->login(nullptr, password);
+ progDialog.exec(task.get());
+ if(task->successful())
+ {
+ m_accounts->addAccount(account);
+ if (m_accounts->count() == 1)
+ m_accounts->setActiveAccount(account->username());
+
+ // Grab associated player skins
+ auto job = new NetJob("Player skins: " + account->username());
+
+ for(AccountProfile profile : account->profiles())
+ {
+ auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
+ auto action = CacheDownload::make(
+ QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"),
+ meta);
+ job->addNetAction(action);
+ meta->stale = true;
+ }
+
+ job->start();
+ }
+ else
+ {
+ auto reason = task->failReason();
+ auto dlg = CustomMessageBox::selectable(this, tr("Login error."), reason, QMessageBox::Critical);
+ dlg->exec();
+ delete dlg;
+ }
+ }
+}
diff --git a/gui/dialogs/AccountListDialog.h b/gui/dialogs/AccountListDialog.h
new file mode 100644
index 00000000..fe0c8773
--- /dev/null
+++ b/gui/dialogs/AccountListDialog.h
@@ -0,0 +1,65 @@
+/* Copyright 2013 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 <QDialog>
+
+#include <memory>
+
+#include "logic/auth/MojangAccountList.h"
+
+namespace Ui
+{
+class AccountListDialog;
+}
+
+class AuthenticateTask;
+
+class AccountListDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit AccountListDialog(QWidget *parent = 0);
+ ~AccountListDialog();
+
+public
+slots:
+ void on_addAccountBtn_clicked();
+
+ void on_rmAccountBtn_clicked();
+
+ void on_setDefaultBtn_clicked();
+
+ void on_noDefaultBtn_clicked();
+
+ // This will be sent when the "close" button is clicked.
+ void on_closeBtnBox_rejected();
+
+ void listChanged();
+
+ //! Updates the states of the dialog's buttons.
+ void updateButtonStates();
+
+protected:
+ std::shared_ptr<MojangAccountList> m_accounts;
+
+protected
+slots:
+ void addAccount(const QString& errMsg="");
+
+private:
+ Ui::AccountListDialog *ui;
+};
diff --git a/gui/dialogs/AccountListDialog.ui b/gui/dialogs/AccountListDialog.ui
new file mode 100644
index 00000000..72682163
--- /dev/null
+++ b/gui/dialogs/AccountListDialog.ui
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AccountListDialog</class>
+ <widget class="QDialog" name="AccountListDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Manage Accounts</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="welcomeLabel">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome! If you're new here, you can click the &amp;quot;Add&amp;quot; button to add your Mojang or Minecraft account.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QTreeView" name="listView"/>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="manageAcctsBtnBox">
+ <item>
+ <widget class="QPushButton" name="addAccountBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmAccountBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="buttonSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="setDefaultBtn">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the currently selected account as the active account. The active account is the account that is used to log in (unless it is overridden in an instance-specific setting).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>&amp;Set Default</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="noDefaultBtn">
+ <property name="toolTip">
+ <string>Set no default account. This will cause MultiMC to prompt you to select an account every time you launch an instance that doesn't have its own default set.</string>
+ </property>
+ <property name="text">
+ <string>&amp;No Default</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="closeBtnBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/AccountSelectDialog.cpp b/gui/dialogs/AccountSelectDialog.cpp
new file mode 100644
index 00000000..ec2f09be
--- /dev/null
+++ b/gui/dialogs/AccountSelectDialog.cpp
@@ -0,0 +1,84 @@
+/* Copyright 2013 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 "AccountSelectDialog.h"
+#include "ui_AccountSelectDialog.h"
+
+#include <QItemSelectionModel>
+
+#include <logger/QsLog.h>
+
+#include <gui/dialogs/ProgressDialog.h>
+
+#include <MultiMC.h>
+
+AccountSelectDialog::AccountSelectDialog(const QString &message, int flags, QWidget *parent)
+ : QDialog(parent), ui(new Ui::AccountSelectDialog)
+{
+ ui->setupUi(this);
+
+ m_accounts = MMC->accounts();
+ ui->listView->setModel(m_accounts.get());
+ ui->listView->hideColumn(MojangAccountList::ActiveColumn);
+
+ // Set the message label.
+ ui->msgLabel->setVisible(!message.isEmpty());
+ ui->msgLabel->setText(message);
+
+ // Flags...
+ ui->globalDefaultCheck->setVisible(flags & GlobalDefaultCheckbox);
+ ui->instDefaultCheck->setVisible(flags & InstanceDefaultCheckbox);
+ QLOG_DEBUG() << flags;
+
+ // Select the first entry in the list.
+ ui->listView->setCurrentIndex(ui->listView->model()->index(0, 0));
+}
+
+AccountSelectDialog::~AccountSelectDialog()
+{
+ delete ui;
+}
+
+MojangAccountPtr AccountSelectDialog::selectedAccount() const
+{
+ return m_selected;
+}
+
+bool AccountSelectDialog::useAsGlobalDefault() const
+{
+ return ui->globalDefaultCheck->isChecked();
+}
+
+bool AccountSelectDialog::useAsInstDefaullt() const
+{
+ return ui->instDefaultCheck->isChecked();
+}
+
+void AccountSelectDialog::on_buttonBox_accepted()
+{
+ QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
+ if (selection.size() > 0)
+ {
+ QModelIndex selected = selection.first();
+ MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
+ m_selected = account;
+ }
+ close();
+}
+
+void AccountSelectDialog::on_buttonBox_rejected()
+{
+ close();
+}
diff --git a/gui/dialogs/AccountSelectDialog.h b/gui/dialogs/AccountSelectDialog.h
new file mode 100644
index 00000000..6007253d
--- /dev/null
+++ b/gui/dialogs/AccountSelectDialog.h
@@ -0,0 +1,90 @@
+/* Copyright 2013 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 <QDialog>
+
+#include <memory>
+
+#include "logic/auth/MojangAccountList.h"
+
+namespace Ui
+{
+class AccountSelectDialog;
+}
+
+class AccountSelectDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ enum Flags
+ {
+ NoFlags = 0,
+
+ /*!
+ * Shows a check box on the dialog that allows the user to specify that the account
+ * they've selected should be used as the global default for all instances.
+ */
+ GlobalDefaultCheckbox,
+
+ /*!
+ * Shows a check box on the dialog that allows the user to specify that the account
+ * they've selected should be used as the default for the instance they are currently launching.
+ * This is not currently implemented.
+ */
+ InstanceDefaultCheckbox,
+ };
+
+ /*!
+ * Constructs a new account select dialog with the given parent and message.
+ * The message will be shown at the top of the dialog. It is an empty string by default.
+ */
+ explicit AccountSelectDialog(const QString& message="", int flags=0, QWidget *parent = 0);
+ ~AccountSelectDialog();
+
+ /*!
+ * Gets a pointer to the account that the user selected.
+ * This is null if the user clicked cancel or hasn't clicked OK yet.
+ */
+ MojangAccountPtr selectedAccount() const;
+
+ /*!
+ * Returns true if the user checked the "use as global default" checkbox.
+ * If the checkbox wasn't shown, this function returns false.
+ */
+ bool useAsGlobalDefault() const;
+
+ /*!
+ * Returns true if the user checked the "use as instance default" checkbox.
+ * If the checkbox wasn't shown, this function returns false.
+ */
+ bool useAsInstDefaullt() const;
+
+public
+slots:
+ void on_buttonBox_accepted();
+
+ void on_buttonBox_rejected();
+
+protected:
+ std::shared_ptr<MojangAccountList> m_accounts;
+
+ //! The account that was selected when the user clicked OK.
+ MojangAccountPtr m_selected;
+
+private:
+ Ui::AccountSelectDialog *ui;
+};
diff --git a/gui/dialogs/AccountSelectDialog.ui b/gui/dialogs/AccountSelectDialog.ui
new file mode 100644
index 00000000..7af1512a
--- /dev/null
+++ b/gui/dialogs/AccountSelectDialog.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AccountSelectDialog</class>
+ <widget class="QDialog" name="AccountSelectDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>413</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Select an Account</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="msgLabel">
+ <property name="text">
+ <string>Select an account.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTreeView" name="listView"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QCheckBox" name="globalDefaultCheck">
+ <property name="text">
+ <string>Use as default?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="instDefaultCheck">
+ <property name="text">
+ <string>Use as default for this instance only?</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/CopyInstanceDialog.cpp b/gui/dialogs/CopyInstanceDialog.cpp
new file mode 100644
index 00000000..4095408b
--- /dev/null
+++ b/gui/dialogs/CopyInstanceDialog.cpp
@@ -0,0 +1,84 @@
+/* Copyright 2013 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 <QLayout>
+#include <QPushButton>
+
+#include "MultiMC.h"
+#include "CopyInstanceDialog.h"
+#include "ui_CopyInstanceDialog.h"
+
+#include "gui/Platform.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "gui/dialogs/ProgressDialog.h"
+#include "gui/dialogs/IconPickerDialog.h"
+
+#include "logic/InstanceFactory.h"
+#include "logic/BaseVersion.h"
+#include "logic/icons/IconList.h"
+#include "logic/lists/MinecraftVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/BaseInstance.h"
+
+CopyInstanceDialog::CopyInstanceDialog(BaseInstance *original, QWidget *parent)
+ :QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ resize(minimumSizeHint());
+ layout()->setSizeConstraint(QLayout::SetFixedSize);
+
+ InstIconKey = original->iconKey();
+ ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
+ ui->instNameTextBox->setText(original->name());
+ ui->instNameTextBox->setFocus();
+}
+
+CopyInstanceDialog::~CopyInstanceDialog()
+{
+ delete ui;
+}
+
+void CopyInstanceDialog::updateDialogState()
+{
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!instName().isEmpty());
+}
+
+QString CopyInstanceDialog::instName() const
+{
+ return ui->instNameTextBox->text();
+}
+
+QString CopyInstanceDialog::iconKey() const
+{
+ return InstIconKey;
+}
+
+void CopyInstanceDialog::on_iconButton_clicked()
+{
+ IconPickerDialog dlg(this);
+ dlg.exec(InstIconKey);
+
+ if (dlg.result() == QDialog::Accepted)
+ {
+ InstIconKey = dlg.selectedIconKey;
+ ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
+ }
+}
+
+void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
+{
+ updateDialogState();
+}
diff --git a/gui/dialogs/CopyInstanceDialog.h b/gui/dialogs/CopyInstanceDialog.h
new file mode 100644
index 00000000..7ab366e2
--- /dev/null
+++ b/gui/dialogs/CopyInstanceDialog.h
@@ -0,0 +1,50 @@
+/* Copyright 2013 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 <QDialog>
+#include "logic/BaseVersion.h"
+
+class BaseInstance;
+
+namespace Ui
+{
+class CopyInstanceDialog;
+}
+
+class CopyInstanceDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit CopyInstanceDialog(BaseInstance *original, QWidget *parent = 0);
+ ~CopyInstanceDialog();
+
+ void updateDialogState();
+
+ QString instName() const;
+ QString iconKey() const;
+
+private
+slots:
+ void on_iconButton_clicked();
+ void on_instNameTextBox_textChanged(const QString &arg1);
+
+private:
+ Ui::CopyInstanceDialog *ui;
+ QString InstIconKey;
+ BaseInstance *m_original;
+};
diff --git a/gui/dialogs/CopyInstanceDialog.ui b/gui/dialogs/CopyInstanceDialog.ui
new file mode 100644
index 00000000..4aa1cb27
--- /dev/null
+++ b/gui/dialogs/CopyInstanceDialog.ui
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CopyInstanceDialog</class>
+ <widget class="QDialog" name="CopyInstanceDialog">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>345</width>
+ <height>205</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Copy Instance</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../../graphics.qrc">
+ <normaloff>:/icons/toolbar/copy</normaloff>:/icons/toolbar/copy</iconset>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="iconBtnLayout">
+ <item>
+ <spacer name="iconBtnLeftSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="iconButton">
+ <property name="icon">
+ <iconset resource="../../graphics.qrc">
+ <normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>80</width>
+ <height>80</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="iconBtnRightSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="instNameTextBox">
+ <property name="placeholderText">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../../graphics.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CopyInstanceDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>CopyInstanceDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/dialogs/CustomMessageBox.cpp b/gui/dialogs/CustomMessageBox.cpp
new file mode 100644
index 00000000..1d2ab58a
--- /dev/null
+++ b/gui/dialogs/CustomMessageBox.cpp
@@ -0,0 +1,34 @@
+/* Copyright 2013 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 "CustomMessageBox.h"
+
+namespace CustomMessageBox
+{
+QMessageBox *selectable(QWidget *parent, const QString &title, const QString &text,
+ QMessageBox::Icon icon, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton)
+{
+ QMessageBox *messageBox = new QMessageBox(parent);
+ messageBox->setWindowTitle(title);
+ messageBox->setText(text);
+ messageBox->setStandardButtons(buttons);
+ messageBox->setDefaultButton(defaultButton);
+ messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse);
+ messageBox->setIcon(icon);
+
+ return messageBox;
+}
+}
diff --git a/gui/dialogs/CustomMessageBox.h b/gui/dialogs/CustomMessageBox.h
new file mode 100644
index 00000000..b08b9f57
--- /dev/null
+++ b/gui/dialogs/CustomMessageBox.h
@@ -0,0 +1,26 @@
+/* Copyright 2013 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 <QMessageBox>
+
+namespace CustomMessageBox
+{
+QMessageBox *selectable(QWidget *parent, const QString &title, const QString &text,
+ QMessageBox::Icon icon = QMessageBox::NoIcon,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
+}
diff --git a/gui/dialogs/EditAccountDialog.cpp b/gui/dialogs/EditAccountDialog.cpp
new file mode 100644
index 00000000..a1bd5591
--- /dev/null
+++ b/gui/dialogs/EditAccountDialog.cpp
@@ -0,0 +1,51 @@
+/* Copyright 2013 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 "EditAccountDialog.h"
+#include "ui_EditAccountDialog.h"
+#include <QDesktopServices>
+#include <QUrl>
+
+EditAccountDialog::EditAccountDialog(const QString &text, QWidget *parent, int flags)
+ : QDialog(parent), ui(new Ui::EditAccountDialog)
+{
+ ui->setupUi(this);
+
+ ui->label->setText(text);
+ ui->label->setVisible(!text.isEmpty());
+
+ ui->userTextBox->setVisible(flags & UsernameField);
+ ui->passTextBox->setVisible(flags & PasswordField);
+}
+
+EditAccountDialog::~EditAccountDialog()
+{
+ delete ui;
+}
+
+void EditAccountDialog::on_label_linkActivated(const QString &link)
+{
+ QDesktopServices::openUrl(QUrl(link));
+}
+
+QString EditAccountDialog::username() const
+{
+ return ui->userTextBox->text();
+}
+
+QString EditAccountDialog::password() const
+{
+ return ui->passTextBox->text();
+}
diff --git a/gui/dialogs/EditAccountDialog.h b/gui/dialogs/EditAccountDialog.h
new file mode 100644
index 00000000..83f25124
--- /dev/null
+++ b/gui/dialogs/EditAccountDialog.h
@@ -0,0 +1,60 @@
+/* Copyright 2013 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 <QDialog>
+
+namespace Ui
+{
+class EditAccountDialog;
+}
+
+class EditAccountDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit EditAccountDialog(const QString &text = "", QWidget *parent = 0,
+ int flags = UsernameField | PasswordField);
+ ~EditAccountDialog();
+
+ /*!
+ * Gets the text entered in the dialog's username field.
+ */
+ QString username() const;
+
+ /*!
+ * Gets the text entered in the dialog's password field.
+ */
+ QString password() const;
+
+ enum Flags
+ {
+ NoFlags = 0,
+
+ //! Specifies that the dialog should have a username field.
+ UsernameField,
+
+ //! Specifies that the dialog should have a password field.
+ PasswordField,
+ };
+
+private slots:
+ void on_label_linkActivated(const QString &link);
+
+private:
+ Ui::EditAccountDialog *ui;
+};
diff --git a/gui/dialogs/EditAccountDialog.ui b/gui/dialogs/EditAccountDialog.ui
new file mode 100644
index 00000000..5f727bd4
--- /dev/null
+++ b/gui/dialogs/EditAccountDialog.ui
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EditAccountDialog</class>
+ <widget class="QDialog" name="EditAccountDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>148</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Edit Account</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Message label placeholder.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="userTextBox">
+ <property name="placeholderText">
+ <string>Email / Username</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="passTextBox">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ <property name="placeholderText">
+ <string>Password</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>EditAccountDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>EditAccountDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/dialogs/EditNotesDialog.cpp b/gui/dialogs/EditNotesDialog.cpp
new file mode 100644
index 00000000..a265a4d0
--- /dev/null
+++ b/gui/dialogs/EditNotesDialog.cpp
@@ -0,0 +1,42 @@
+/* Copyright 2013 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 "EditNotesDialog.h"
+#include "ui_EditNotesDialog.h"
+#include "gui/Platform.h"
+
+#include <QIcon>
+#include <QApplication>
+
+EditNotesDialog::EditNotesDialog(QString notes, QString name, QWidget *parent)
+ : QDialog(parent), ui(new Ui::EditNotesDialog), m_instance_name(name),
+ m_instance_notes(notes)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ ui->noteEditor->setText(notes);
+ setWindowTitle(tr("Edit notes of %1").arg(m_instance_name));
+ // connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
+}
+
+EditNotesDialog::~EditNotesDialog()
+{
+ delete ui;
+}
+
+QString EditNotesDialog::getText()
+{
+ return ui->noteEditor->toPlainText();
+}
diff --git a/gui/dialogs/EditNotesDialog.h b/gui/dialogs/EditNotesDialog.h
new file mode 100644
index 00000000..b74558c4
--- /dev/null
+++ b/gui/dialogs/EditNotesDialog.h
@@ -0,0 +1,38 @@
+/* Copyright 2013 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 <QDialog>
+
+namespace Ui
+{
+class EditNotesDialog;
+}
+
+class EditNotesDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit EditNotesDialog(QString notes, QString name, QWidget *parent = 0);
+ ~EditNotesDialog();
+ QString getText();
+
+private:
+ Ui::EditNotesDialog *ui;
+ QString m_instance_name;
+ QString m_instance_notes;
+};
diff --git a/gui/dialogs/EditNotesDialog.ui b/gui/dialogs/EditNotesDialog.ui
new file mode 100644
index 00000000..487dfb84
--- /dev/null
+++ b/gui/dialogs/EditNotesDialog.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EditNotesDialog</class>
+ <widget class="QDialog" name="EditNotesDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>459</width>
+ <height>399</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Edit Notes</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTextEdit" name="noteEditor">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOn</enum>
+ </property>
+ <property name="acceptRichText">
+ <bool>false</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>EditNotesDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>EditNotesDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/dialogs/IconPickerDialog.cpp b/gui/dialogs/IconPickerDialog.cpp
new file mode 100644
index 00000000..f7970b37
--- /dev/null
+++ b/gui/dialogs/IconPickerDialog.cpp
@@ -0,0 +1,156 @@
+/* Copyright 2013 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 <QKeyEvent>
+#include <QPushButton>
+#include <QFileDialog>
+
+#include "MultiMC.h"
+
+#include "IconPickerDialog.h"
+#include "ui_IconPickerDialog.h"
+
+#include "gui/Platform.h"
+#include "gui/widgets/InstanceDelegate.h"
+
+#include "logic/icons/IconList.h"
+
+IconPickerDialog::IconPickerDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::IconPickerDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ setWindowModality(Qt::WindowModal);
+
+ auto contentsWidget = ui->iconView;
+ contentsWidget->setViewMode(QListView::IconMode);
+ contentsWidget->setFlow(QListView::LeftToRight);
+ contentsWidget->setIconSize(QSize(48, 48));
+ contentsWidget->setMovement(QListView::Static);
+ contentsWidget->setResizeMode(QListView::Adjust);
+ contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ contentsWidget->setSpacing(5);
+ contentsWidget->setWordWrap(false);
+ contentsWidget->setWrapping(true);
+ contentsWidget->setUniformItemSizes(true);
+ contentsWidget->setTextElideMode(Qt::ElideRight);
+ contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ contentsWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ contentsWidget->setItemDelegate(new ListViewDelegate());
+
+ // contentsWidget->setAcceptDrops(true);
+ contentsWidget->setDropIndicatorShown(true);
+ contentsWidget->viewport()->setAcceptDrops(true);
+ contentsWidget->setDragDropMode(QAbstractItemView::DropOnly);
+ contentsWidget->setDefaultDropAction(Qt::CopyAction);
+
+ contentsWidget->installEventFilter(this);
+
+ contentsWidget->setModel(MMC->icons().get());
+
+ auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"), QDialogButtonBox::ResetRole);
+ auto buttonRemove =
+ ui->buttonBox->addButton(tr("Remove Icon"), QDialogButtonBox::ResetRole);
+
+ connect(buttonAdd, SIGNAL(clicked(bool)), SLOT(addNewIcon()));
+ connect(buttonRemove, SIGNAL(clicked(bool)), SLOT(removeSelectedIcon()));
+
+ connect(contentsWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(activated(QModelIndex)));
+
+ connect(contentsWidget->selectionModel(),
+ SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ SLOT(selectionChanged(QItemSelection, QItemSelection)));
+}
+bool IconPickerDialog::eventFilter(QObject *obj, QEvent *evt)
+{
+ if (obj != ui->iconView)
+ return QDialog::eventFilter(obj, evt);
+ if (evt->type() != QEvent::KeyPress)
+ {
+ return QDialog::eventFilter(obj, evt);
+ }
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(evt);
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ removeSelectedIcon();
+ return true;
+ case Qt::Key_Plus:
+ addNewIcon();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(obj, evt);
+}
+
+void IconPickerDialog::addNewIcon()
+{
+ //: The title of the select icons open file dialog
+ QString selectIcons = tr("Select Icons");
+ //: The type of icon files
+ QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(),
+ tr("Icons") + "(*.png *.jpg *.jpeg *.ico)");
+ MMC->icons()->installIcons(fileNames);
+}
+
+void IconPickerDialog::removeSelectedIcon()
+{
+ MMC->icons()->deleteIcon(selectedIconKey);
+}
+
+void IconPickerDialog::activated(QModelIndex index)
+{
+ selectedIconKey = index.data(Qt::UserRole).toString();
+ accept();
+}
+
+void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection deselected)
+{
+ if (selected.empty())
+ return;
+
+ QString key = selected.first().indexes().first().data(Qt::UserRole).toString();
+ if (!key.isEmpty())
+ selectedIconKey = key;
+}
+
+int IconPickerDialog::exec(QString selection)
+{
+ auto list = MMC->icons();
+ auto contentsWidget = ui->iconView;
+ selectedIconKey = selection;
+
+ int index_nr = list->getIconIndex(selection);
+ auto model_index = list->index(index_nr);
+ contentsWidget->selectionModel()->select(
+ model_index, QItemSelectionModel::Current | QItemSelectionModel::Select);
+
+ QMetaObject::invokeMethod(this, "delayed_scroll", Qt::QueuedConnection,
+ Q_ARG(QModelIndex, model_index));
+ return QDialog::exec();
+}
+
+void IconPickerDialog::delayed_scroll(QModelIndex model_index)
+{
+ auto contentsWidget = ui->iconView;
+ contentsWidget->scrollTo(model_index);
+}
+
+IconPickerDialog::~IconPickerDialog()
+{
+ delete ui;
+}
diff --git a/gui/dialogs/IconPickerDialog.h b/gui/dialogs/IconPickerDialog.h
new file mode 100644
index 00000000..f00c2388
--- /dev/null
+++ b/gui/dialogs/IconPickerDialog.h
@@ -0,0 +1,48 @@
+/* Copyright 2013 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 <QDialog>
+#include <QItemSelection>
+
+namespace Ui
+{
+class IconPickerDialog;
+}
+
+class IconPickerDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit IconPickerDialog(QWidget *parent = 0);
+ ~IconPickerDialog();
+ int exec(QString selection);
+ QString selectedIconKey;
+
+protected:
+ virtual bool eventFilter(QObject *, QEvent *);
+
+private:
+ Ui::IconPickerDialog *ui;
+
+private
+slots:
+ void selectionChanged(QItemSelection, QItemSelection);
+ void activated(QModelIndex);
+ void delayed_scroll(QModelIndex);
+ void addNewIcon();
+ void removeSelectedIcon();
+};
diff --git a/gui/dialogs/IconPickerDialog.ui b/gui/dialogs/IconPickerDialog.ui
new file mode 100644
index 00000000..c548edfb
--- /dev/null
+++ b/gui/dialogs/IconPickerDialog.ui
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IconPickerDialog</class>
+ <widget class="QDialog" name="IconPickerDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>676</width>
+ <height>555</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Pick icon</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QListView" name="iconView"/>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>IconPickerDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>IconPickerDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/dialogs/InstanceSettings.cpp b/gui/dialogs/InstanceSettings.cpp
new file mode 100644
index 00000000..edb4a921
--- /dev/null
+++ b/gui/dialogs/InstanceSettings.cpp
@@ -0,0 +1,243 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Andrew Okin
+ * Peterix
+ * Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 "MultiMC.h"
+#include "InstanceSettings.h"
+#include "ui_InstanceSettings.h"
+#include "gui/Platform.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+
+#include "logic/JavaUtils.h"
+#include "logic/NagUtils.h"
+#include "logic/lists/JavaVersionList.h"
+#include "logic/JavaChecker.h"
+
+#include <QFileDialog>
+#include <QMessageBox>
+
+InstanceSettings::InstanceSettings(SettingsObject *obj, QWidget *parent)
+ : QDialog(parent), ui(new Ui::InstanceSettings), m_obj(obj)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+
+ restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
+
+ loadSettings();
+}
+
+InstanceSettings::~InstanceSettings()
+{
+ delete ui;
+}
+
+void InstanceSettings::showEvent(QShowEvent *ev)
+{
+ QDialog::showEvent(ev);
+}
+
+void InstanceSettings::closeEvent(QCloseEvent *ev)
+{
+ MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
+
+ QDialog::closeEvent(ev);
+}
+
+void InstanceSettings::on_customCommandsGroupBox_toggled(bool state)
+{
+ ui->labelCustomCmdsDescription->setEnabled(state);
+}
+
+void InstanceSettings::on_buttonBox_accepted()
+{
+ MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
+
+ applySettings();
+ accept();
+}
+
+void InstanceSettings::on_buttonBox_rejected()
+{
+ MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
+
+ reject();
+}
+
+void InstanceSettings::applySettings()
+{
+ // Console
+ bool console = ui->consoleSettingsBox->isChecked();
+ m_obj->set("OverrideConsole", console);
+ if (console)
+ {
+ m_obj->set("ShowConsole", ui->showConsoleCheck->isChecked());
+ m_obj->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
+ }
+ else
+ {
+ m_obj->reset("ShowConsole");
+ m_obj->reset("AutoCloseConsole");
+ }
+
+ // Window Size
+ bool window = ui->windowSizeGroupBox->isChecked();
+ m_obj->set("OverrideWindow", window);
+ if (window)
+ {
+ m_obj->set("LaunchMaximized", ui->maximizedCheckBox->isChecked());
+ m_obj->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
+ m_obj->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
+ }
+ else
+ {
+ m_obj->reset("LaunchMaximized");
+ m_obj->reset("MinecraftWinWidth");
+ m_obj->reset("MinecraftWinHeight");
+ }
+
+ // Memory
+ bool memory = ui->memoryGroupBox->isChecked();
+ m_obj->set("OverrideMemory", memory);
+ if (memory)
+ {
+ m_obj->set("MinMemAlloc", ui->minMemSpinBox->value());
+ m_obj->set("MaxMemAlloc", ui->maxMemSpinBox->value());
+ m_obj->set("PermGen", ui->permGenSpinBox->value());
+ }
+ else
+ {
+ m_obj->reset("MinMemAlloc");
+ m_obj->reset("MaxMemAlloc");
+ m_obj->reset("PermGen");
+ }
+
+ // Java Settings
+ bool java = ui->javaSettingsGroupBox->isChecked();
+ m_obj->set("OverrideJava", java);
+ if (java)
+ {
+ m_obj->set("JavaPath", ui->javaPathTextBox->text());
+ m_obj->set("JvmArgs", ui->jvmArgsTextBox->text());
+
+ NagUtils::checkJVMArgs(m_obj->get("JvmArgs").toString(), this->parentWidget());
+ }
+ else
+ {
+ m_obj->reset("JavaPath");
+ m_obj->reset("JvmArgs");
+ }
+
+ // Custom Commands
+ bool custcmd = ui->customCommandsGroupBox->isChecked();
+ m_obj->set("OverrideCommands", custcmd);
+ if (custcmd)
+ {
+ m_obj->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text());
+ m_obj->set("PostExitCommand", ui->postExitCmdTextBox->text());
+ }
+ else
+ {
+ m_obj->reset("PreLaunchCommand");
+ m_obj->reset("PostExitCommand");
+ }
+}
+
+void InstanceSettings::loadSettings()
+{
+ // Console
+ ui->consoleSettingsBox->setChecked(m_obj->get("OverrideConsole").toBool());
+ ui->showConsoleCheck->setChecked(m_obj->get("ShowConsole").toBool());
+ ui->autoCloseConsoleCheck->setChecked(m_obj->get("AutoCloseConsole").toBool());
+
+ // Window Size
+ ui->windowSizeGroupBox->setChecked(m_obj->get("OverrideWindow").toBool());
+ ui->maximizedCheckBox->setChecked(m_obj->get("LaunchMaximized").toBool());
+ ui->windowWidthSpinBox->setValue(m_obj->get("MinecraftWinWidth").toInt());
+ ui->windowHeightSpinBox->setValue(m_obj->get("MinecraftWinHeight").toInt());
+
+ // Memory
+ ui->memoryGroupBox->setChecked(m_obj->get("OverrideMemory").toBool());
+ ui->minMemSpinBox->setValue(m_obj->get("MinMemAlloc").toInt());
+ ui->maxMemSpinBox->setValue(m_obj->get("MaxMemAlloc").toInt());
+ ui->permGenSpinBox->setValue(m_obj->get("PermGen").toInt());
+
+ // Java Settings
+ ui->javaSettingsGroupBox->setChecked(m_obj->get("OverrideJava").toBool());
+ ui->javaPathTextBox->setText(m_obj->get("JavaPath").toString());
+ ui->jvmArgsTextBox->setText(m_obj->get("JvmArgs").toString());
+
+ // Custom Commands
+ ui->customCommandsGroupBox->setChecked(m_obj->get("OverrideCommands").toBool());
+ ui->preLaunchCmdTextBox->setText(m_obj->get("PreLaunchCommand").toString());
+ ui->postExitCmdTextBox->setText(m_obj->get("PostExitCommand").toString());
+}
+
+void InstanceSettings::on_javaDetectBtn_clicked()
+{
+ JavaVersionPtr java;
+
+ VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true);
+ vselect.setResizeOn(2);
+ vselect.exec();
+
+ if (vselect.result() == QDialog::Accepted && vselect.selectedVersion())
+ {
+ java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
+ ui->javaPathTextBox->setText(java->path);
+ }
+}
+
+void InstanceSettings::on_javaBrowseBtn_clicked()
+{
+ QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
+ if (!dir.isNull())
+ {
+ ui->javaPathTextBox->setText(dir);
+ }
+}
+
+void InstanceSettings::on_javaTestBtn_clicked()
+{
+ checker.reset(new JavaChecker());
+ connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
+ SLOT(checkFinished(JavaCheckResult)));
+ checker->path = ui->javaPathTextBox->text();
+ checker->performCheck();
+}
+
+void InstanceSettings::checkFinished(JavaCheckResult result)
+{
+ if (result.valid)
+ {
+ QString text;
+ text += "Java test succeeded!\n";
+ if (result.is_64bit)
+ text += "Using 64bit java.\n";
+ text += "\n";
+ text += "Platform reported: " + result.realPlatform;
+ QMessageBox::information(this, tr("Java test success"), text);
+ }
+ else
+ {
+ QMessageBox::warning(
+ this, tr("Java test failure"),
+ tr("The specified java binary didn't work. You should use the auto-detect feature, "
+ "or set the path to the java executable."));
+ }
+}
diff --git a/gui/dialogs/InstanceSettings.h b/gui/dialogs/InstanceSettings.h
new file mode 100644
index 00000000..e296db4c
--- /dev/null
+++ b/gui/dialogs/InstanceSettings.h
@@ -0,0 +1,60 @@
+/* Copyright 2013 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 <QDialog>
+#include "settingsobject.h"
+#include "logic/JavaChecker.h"
+
+namespace Ui
+{
+class InstanceSettings;
+}
+
+class InstanceSettings : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit InstanceSettings(SettingsObject *s, QWidget *parent = 0);
+ ~InstanceSettings();
+
+ void updateCheckboxStuff();
+
+ void applySettings();
+ void loadSettings();
+
+protected:
+ virtual void showEvent(QShowEvent *);
+ virtual void closeEvent(QCloseEvent *);
+private
+slots:
+ void on_customCommandsGroupBox_toggled(bool arg1);
+ void on_buttonBox_accepted();
+ void on_buttonBox_rejected();
+
+ void on_javaDetectBtn_clicked();
+
+ void on_javaTestBtn_clicked();
+
+ void on_javaBrowseBtn_clicked();
+
+ void checkFinished(JavaCheckResult result);
+private:
+ Ui::InstanceSettings *ui;
+ SettingsObject *m_obj;
+ std::shared_ptr<JavaChecker> checker;
+};
diff --git a/gui/dialogs/InstanceSettings.ui b/gui/dialogs/InstanceSettings.ui
new file mode 100644
index 00000000..9c7e1757
--- /dev/null
+++ b/gui/dialogs/InstanceSettings.ui
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>InstanceSettings</class>
+ <widget class="QDialog" name="InstanceSettings">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>526</width>
+ <height>637</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Instance Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="settingsTabs">
+ <property name="tabShape">
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="minecraftTab">
+ <attribute name="title">
+ <string>Minecraft</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="windowSizeGroupBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Window Size</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QCheckBox" name="maximizedCheckBox">
+ <property name="text">
+ <string>Start Minecraft maximized?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayoutWindowSize">
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelWindowHeight">
+ <property name="text">
+ <string>Window height:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelWindowWidth">
+ <property name="text">
+ <string>Window width:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="windowWidthSpinBox">
+ <property name="minimum">
+ <number>854</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>854</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="windowHeightSpinBox">
+ <property name="minimum">
+ <number>480</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="value">
+ <number>480</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="consoleSettingsBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Console Settings</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="showConsoleCheck">
+ <property name="text">
+ <string>Show console while the game is running?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoCloseConsoleCheck">
+ <property name="text">
+ <string>Automatically close console when the game quits?</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerMinecraft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="javaTab">
+ <attribute name="title">
+ <string>Java</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QGroupBox" name="memoryGroupBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Memory</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="maxMemSpinBox">
+ <property name="toolTip">
+ <string>The maximum amount of memory Minecraft is allowed to use.</string>
+ </property>
+ <property name="suffix">
+ <string> MB</string>
+ </property>
+ <property name="minimum">
+ <number>512</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>128</number>
+ </property>
+ <property name="value">
+ <number>1024</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelMinMem">
+ <property name="text">
+ <string>Minimum memory allocation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelMaxMem">
+ <property name="text">
+ <string>Maximum memory allocation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="minMemSpinBox">
+ <property name="toolTip">
+ <string>The amount of memory Minecraft is started with.</string>
+ </property>
+ <property name="suffix">
+ <string> MB</string>
+ </property>
+ <property name="minimum">
+ <number>256</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>128</number>
+ </property>
+ <property name="value">
+ <number>256</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="permGenSpinBox">
+ <property name="toolTip">
+ <string>The amount of memory available to store loaded Java classes.</string>
+ </property>
+ <property name="suffix">
+ <string> MB</string>
+ </property>
+ <property name="minimum">
+ <number>64</number>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ <property name="singleStep">
+ <number>8</number>
+ </property>
+ <property name="value">
+ <number>64</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelPermGen">
+ <property name="text">
+ <string>PermGen:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="javaSettingsGroupBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Java Settings</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="4">
+ <widget class="QPushButton" name="javaTestBtn">
+ <property name="text">
+ <string>Test</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelJavaPath">
+ <property name="text">
+ <string>Java path:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="labelJVMArgs">
+ <property name="text">
+ <string>JVM arguments:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2" colspan="3">
+ <widget class="QLineEdit" name="jvmArgsTextBox"/>
+ </item>
+ <item row="0" column="2" colspan="3">
+ <widget class="QLineEdit" name="javaPathTextBox"/>
+ </item>
+ <item row="2" column="3">
+ <widget class="QPushButton" name="javaBrowseBtn">
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QPushButton" name="javaDetectBtn">
+ <property name="text">
+ <string>Auto-detect...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="customCommandsGroupBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Custom Commands</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="preLaunchCmdTextBox"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelPostExitCmd">
+ <property name="text">
+ <string>Post-exit command:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelPreLaunchCmd">
+ <property name="text">
+ <string>Pre-launch command:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="postExitCmdTextBox"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelCustomCmdsDescription">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>settingsTabs</tabstop>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>windowSizeGroupBox</tabstop>
+ <tabstop>maximizedCheckBox</tabstop>
+ <tabstop>windowWidthSpinBox</tabstop>
+ <tabstop>windowHeightSpinBox</tabstop>
+ <tabstop>consoleSettingsBox</tabstop>
+ <tabstop>showConsoleCheck</tabstop>
+ <tabstop>autoCloseConsoleCheck</tabstop>
+ <tabstop>memoryGroupBox</tabstop>
+ <tabstop>minMemSpinBox</tabstop>
+ <tabstop>maxMemSpinBox</tabstop>
+ <tabstop>permGenSpinBox</tabstop>
+ <tabstop>javaSettingsGroupBox</tabstop>
+ <tabstop>jvmArgsTextBox</tabstop>
+ <tabstop>customCommandsGroupBox</tabstop>
+ <tabstop>preLaunchCmdTextBox</tabstop>
+ <tabstop>postExitCmdTextBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/LegacyModEditDialog.cpp b/gui/dialogs/LegacyModEditDialog.cpp
new file mode 100644
index 00000000..66d53ee1
--- /dev/null
+++ b/gui/dialogs/LegacyModEditDialog.cpp
@@ -0,0 +1,393 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "LegacyModEditDialog.h"
+#include "ModEditDialogCommon.h"
+#include "VersionSelectDialog.h"
+#include "ProgressDialog.h"
+#include "ui_LegacyModEditDialog.h"
+#include "logic/ModList.h"
+#include "logic/lists/ForgeVersionList.h"
+#include "gui/Platform.h"
+
+#include <pathutils.h>
+#include <QFileDialog>
+//#include <QMessageBox>
+#include <QDebug>
+#include <QEvent>
+#include <QKeyEvent>
+
+LegacyModEditDialog::LegacyModEditDialog(LegacyInstance *inst, QWidget *parent)
+ : QDialog(parent), ui(new Ui::LegacyModEditDialog), m_inst(inst)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+
+ // Jar mods
+ {
+ ensureFolderPathExists(m_inst->jarModsDir());
+ m_jarmods = m_inst->jarModList();
+ ui->jarModsTreeView->setModel(m_jarmods.get());
+#ifndef Q_OS_LINUX
+ // FIXME: internal DnD causes segfaults later
+ ui->jarModsTreeView->setDragDropMode(QAbstractItemView::DragDrop);
+ // FIXME: DnD is glitched with contiguous (we move only first item in selection)
+ ui->jarModsTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
+#endif
+ ui->jarModsTreeView->installEventFilter(this);
+ m_jarmods->startWatching();
+ auto smodel = ui->jarModsTreeView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ SLOT(jarCurrent(QModelIndex, QModelIndex)));
+ }
+ // Core mods
+ {
+ ensureFolderPathExists(m_inst->coreModsDir());
+ m_coremods = m_inst->coreModList();
+ ui->coreModsTreeView->setModel(m_coremods.get());
+ ui->coreModsTreeView->installEventFilter(this);
+ m_coremods->startWatching();
+ auto smodel = ui->coreModsTreeView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ SLOT(coreCurrent(QModelIndex, QModelIndex)));
+ }
+ // Loader mods
+ {
+ ensureFolderPathExists(m_inst->loaderModsDir());
+ m_mods = m_inst->loaderModList();
+ ui->loaderModTreeView->setModel(m_mods.get());
+ ui->loaderModTreeView->installEventFilter(this);
+ m_mods->startWatching();
+ auto smodel = ui->loaderModTreeView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ SLOT(loaderCurrent(QModelIndex, QModelIndex)));
+ }
+ // texture packs
+ {
+ ensureFolderPathExists(m_inst->texturePacksDir());
+ m_texturepacks = m_inst->texturePackList();
+ ui->texPackTreeView->setModel(m_texturepacks.get());
+ ui->texPackTreeView->installEventFilter(this);
+ m_texturepacks->startWatching();
+ }
+}
+
+LegacyModEditDialog::~LegacyModEditDialog()
+{
+ m_mods->stopWatching();
+ m_coremods->stopWatching();
+ m_jarmods->stopWatching();
+ m_texturepacks->stopWatching();
+ delete ui;
+}
+
+bool LegacyModEditDialog::coreListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ on_rmCoreBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addCoreBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(ui->coreModsTreeView, keyEvent);
+}
+
+bool LegacyModEditDialog::jarListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Up:
+ {
+ if (keyEvent->modifiers() & Qt::ControlModifier)
+ {
+ on_moveJarUpBtn_clicked();
+ return true;
+ }
+ break;
+ }
+ case Qt::Key_Down:
+ {
+ if (keyEvent->modifiers() & Qt::ControlModifier)
+ {
+ on_moveJarDownBtn_clicked();
+ return true;
+ }
+ break;
+ }
+ case Qt::Key_Delete:
+ on_rmJarBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addJarBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(ui->jarModsTreeView, keyEvent);
+}
+
+bool LegacyModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ on_rmModBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addModBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(ui->loaderModTreeView, keyEvent);
+}
+
+bool LegacyModEditDialog::texturePackListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ on_rmTexPackBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addTexPackBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(ui->texPackTreeView, keyEvent);
+}
+
+bool LegacyModEditDialog::eventFilter(QObject *obj, QEvent *ev)
+{
+ if (ev->type() != QEvent::KeyPress)
+ {
+ return QDialog::eventFilter(obj, ev);
+ }
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
+ if (obj == ui->jarModsTreeView)
+ return jarListFilter(keyEvent);
+ if (obj == ui->coreModsTreeView)
+ return coreListFilter(keyEvent);
+ if (obj == ui->loaderModTreeView)
+ return loaderListFilter(keyEvent);
+ if (obj == ui->texPackTreeView)
+ return texturePackListFilter(keyEvent);
+ return QDialog::eventFilter(obj, ev);
+}
+
+void LegacyModEditDialog::on_addCoreBtn_clicked()
+{
+ //: Title of core mod selection dialog
+ QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Core Mods"));
+ for (auto filename : fileNames)
+ {
+ m_coremods->stopWatching();
+ m_coremods->installMod(QFileInfo(filename));
+ m_coremods->startWatching();
+ }
+}
+void LegacyModEditDialog::on_addForgeBtn_clicked()
+{
+ VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
+ vselect.setFilter(1, m_inst->intendedVersionId());
+ if (vselect.exec() && vselect.selectedVersion())
+ {
+ ForgeVersionPtr forge =
+ std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
+ if (!forge)
+ return;
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename);
+ if (entry->stale)
+ {
+ NetJob *fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forge->universal_url, entry));
+ ProgressDialog dlg(this);
+ dlg.exec(fjob);
+ if (dlg.result() == QDialog::Accepted)
+ {
+ m_jarmods->stopWatching();
+ m_jarmods->installMod(QFileInfo(entry->getFullPath()));
+ m_jarmods->startWatching();
+ }
+ else
+ {
+ // failed to download forge :/
+ }
+ }
+ else
+ {
+ m_jarmods->stopWatching();
+ m_jarmods->installMod(QFileInfo(entry->getFullPath()));
+ m_jarmods->startWatching();
+ }
+ }
+}
+void LegacyModEditDialog::on_addJarBtn_clicked()
+{
+ //: Title of jar mod selection dialog
+ QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Jar Mods"));
+ for (auto filename : fileNames)
+ {
+ m_jarmods->stopWatching();
+ m_jarmods->installMod(QFileInfo(filename));
+ m_jarmods->startWatching();
+ }
+}
+void LegacyModEditDialog::on_addModBtn_clicked()
+{
+ //: Title of regular mod selection dialog
+ QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Loader Mods"));
+ for (auto filename : fileNames)
+ {
+ m_mods->stopWatching();
+ m_mods->installMod(QFileInfo(filename));
+ m_mods->startWatching();
+ }
+}
+void LegacyModEditDialog::on_addTexPackBtn_clicked()
+{
+ //: Title of texture pack selection dialog
+ QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Texture Packs"));
+ for (auto filename : fileNames)
+ {
+ m_texturepacks->stopWatching();
+ m_texturepacks->installMod(QFileInfo(filename));
+ m_texturepacks->startWatching();
+ }
+}
+
+void LegacyModEditDialog::on_moveJarDownBtn_clicked()
+{
+ int first, last;
+ auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+
+ m_jarmods->moveModsDown(first, last);
+}
+void LegacyModEditDialog::on_moveJarUpBtn_clicked()
+{
+ int first, last;
+ auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_jarmods->moveModsUp(first, last);
+}
+void LegacyModEditDialog::on_rmCoreBtn_clicked()
+{
+ int first, last;
+ auto list = ui->coreModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_coremods->stopWatching();
+ m_coremods->deleteMods(first, last);
+ m_coremods->startWatching();
+}
+void LegacyModEditDialog::on_rmJarBtn_clicked()
+{
+ int first, last;
+ auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_jarmods->stopWatching();
+ m_jarmods->deleteMods(first, last);
+ m_jarmods->startWatching();
+}
+void LegacyModEditDialog::on_rmModBtn_clicked()
+{
+ int first, last;
+ auto list = ui->loaderModTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_mods->stopWatching();
+ m_mods->deleteMods(first, last);
+ m_mods->startWatching();
+}
+void LegacyModEditDialog::on_rmTexPackBtn_clicked()
+{
+ int first, last;
+ auto list = ui->texPackTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_texturepacks->stopWatching();
+ m_texturepacks->deleteMods(first, last);
+ m_texturepacks->startWatching();
+}
+void LegacyModEditDialog::on_viewCoreBtn_clicked()
+{
+ openDirInDefaultProgram(m_inst->coreModsDir(), true);
+}
+void LegacyModEditDialog::on_viewModBtn_clicked()
+{
+ openDirInDefaultProgram(m_inst->loaderModsDir(), true);
+}
+void LegacyModEditDialog::on_viewTexPackBtn_clicked()
+{
+ openDirInDefaultProgram(m_inst->texturePacksDir(), true);
+}
+
+void LegacyModEditDialog::on_buttonBox_rejected()
+{
+ close();
+}
+
+void LegacyModEditDialog::jarCurrent(QModelIndex current, QModelIndex previous)
+{
+ if (!current.isValid())
+ {
+ ui->jarMIFrame->clear();
+ return;
+ }
+ int row = current.row();
+ Mod &m = m_jarmods->operator[](row);
+ ui->jarMIFrame->updateWithMod(m);
+}
+
+void LegacyModEditDialog::coreCurrent(QModelIndex current, QModelIndex previous)
+{
+ if (!current.isValid())
+ {
+ ui->coreMIFrame->clear();
+ return;
+ }
+ int row = current.row();
+ Mod &m = m_coremods->operator[](row);
+ ui->coreMIFrame->updateWithMod(m);
+}
+
+void LegacyModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous)
+{
+ if (!current.isValid())
+ {
+ ui->loaderMIFrame->clear();
+ return;
+ }
+ int row = current.row();
+ Mod &m = m_mods->operator[](row);
+ ui->loaderMIFrame->updateWithMod(m);
+}
diff --git a/gui/dialogs/LegacyModEditDialog.h b/gui/dialogs/LegacyModEditDialog.h
new file mode 100644
index 00000000..d5582aef
--- /dev/null
+++ b/gui/dialogs/LegacyModEditDialog.h
@@ -0,0 +1,78 @@
+/* Copyright 2013 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 <QDialog>
+#include "logic/LegacyInstance.h"
+#include <logic/net/NetJob.h>
+
+namespace Ui
+{
+class LegacyModEditDialog;
+}
+
+class LegacyModEditDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit LegacyModEditDialog(LegacyInstance *inst, QWidget *parent = 0);
+ ~LegacyModEditDialog();
+
+private
+slots:
+
+ void on_addJarBtn_clicked();
+ void on_rmJarBtn_clicked();
+ void on_addForgeBtn_clicked();
+ void on_moveJarUpBtn_clicked();
+ void on_moveJarDownBtn_clicked();
+
+ void on_addCoreBtn_clicked();
+ void on_rmCoreBtn_clicked();
+ void on_viewCoreBtn_clicked();
+
+ void on_addModBtn_clicked();
+ void on_rmModBtn_clicked();
+ void on_viewModBtn_clicked();
+
+ void on_addTexPackBtn_clicked();
+ void on_rmTexPackBtn_clicked();
+ void on_viewTexPackBtn_clicked();
+
+ // Questionable: SettingsDialog doesn't need this for some reason?
+ void on_buttonBox_rejected();
+
+ void jarCurrent(QModelIndex current, QModelIndex previous);
+ void coreCurrent(QModelIndex current, QModelIndex previous);
+ void loaderCurrent(QModelIndex current, QModelIndex previous);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *ev);
+ bool jarListFilter(QKeyEvent *ev);
+ bool coreListFilter(QKeyEvent *ev);
+ bool loaderListFilter(QKeyEvent *ev);
+ bool texturePackListFilter(QKeyEvent *ev);
+
+private:
+ Ui::LegacyModEditDialog *ui;
+ std::shared_ptr<ModList> m_mods;
+ std::shared_ptr<ModList> m_coremods;
+ std::shared_ptr<ModList> m_jarmods;
+ std::shared_ptr<ModList> m_texturepacks;
+ LegacyInstance *m_inst;
+ NetJobPtr forgeJob;
+};
diff --git a/gui/dialogs/LegacyModEditDialog.ui b/gui/dialogs/LegacyModEditDialog.ui
new file mode 100644
index 00000000..0662c712
--- /dev/null
+++ b/gui/dialogs/LegacyModEditDialog.ui
@@ -0,0 +1,321 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LegacyModEditDialog</class>
+ <widget class="QDialog" name="LegacyModEditDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>540</width>
+ <height>420</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Edit Mods</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="jarTab">
+ <attribute name="title">
+ <string>Jar Mods</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="ModListView" name="jarModsTreeView">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOn</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="jarModsButtonBox">
+ <item>
+ <widget class="QPushButton" name="addJarBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmJarBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addForgeBtn">
+ <property name="text">
+ <string>MCForge</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="jarModsButtonSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveJarUpBtn">
+ <property name="text">
+ <string>Move &amp;Up</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="moveJarDownBtn">
+ <property name="text">
+ <string>Move &amp;Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MCModInfoFrame" name="jarMIFrame">
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="coreTab">
+ <attribute name="title">
+ <string>Core Mods</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="ModListView" name="coreModsTreeView">
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DropOnly</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="coreModsButtonBox">
+ <item>
+ <widget class="QPushButton" name="addCoreBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmCoreBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="coreModsButtonSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewCoreBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MCModInfoFrame" name="coreMIFrame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="modTab">
+ <attribute name="title">
+ <string>Loader Mods</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="ModListView" name="loaderModTreeView">
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DropOnly</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="mlModsButtonBox">
+ <item>
+ <widget class="QPushButton" name="addModBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmModBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="mlModsButtonSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewModBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MCModInfoFrame" name="loaderMIFrame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="texPackTab">
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <attribute name="title">
+ <string>Texture Packs</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="ModListView" name="texPackTreeView">
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DropOnly</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="texturePacksButtonBox">
+ <item>
+ <widget class="QPushButton" name="addTexPackBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmTexPackBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="texturePacksButtonSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewTexPackBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ModListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/ModListView.h</header>
+ </customwidget>
+ <customwidget>
+ <class>MCModInfoFrame</class>
+ <extends>QFrame</extends>
+ <header>gui/widgets/MCModInfoFrame.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/LwjglSelectDialog.cpp b/gui/dialogs/LwjglSelectDialog.cpp
new file mode 100644
index 00000000..046a4e2e
--- /dev/null
+++ b/gui/dialogs/LwjglSelectDialog.cpp
@@ -0,0 +1,72 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "LwjglSelectDialog.h"
+#include "ui_LwjglSelectDialog.h"
+#include "gui/Platform.h"
+
+#include "logic/lists/LwjglVersionList.h"
+
+LWJGLSelectDialog::LWJGLSelectDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::LWJGLSelectDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ ui->labelStatus->setVisible(false);
+ auto lwjgllist = MMC->lwjgllist();
+ ui->lwjglListView->setModel(lwjgllist.get());
+
+ connect(lwjgllist.get(), SIGNAL(loadingStateUpdated(bool)),
+ SLOT(loadingStateUpdated(bool)));
+ connect(lwjgllist.get(), SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString)));
+ loadingStateUpdated(lwjgllist->isLoading());
+}
+
+LWJGLSelectDialog::~LWJGLSelectDialog()
+{
+ delete ui;
+}
+
+QString LWJGLSelectDialog::selectedVersion() const
+{
+ return MMC->lwjgllist()
+ ->data(ui->lwjglListView->selectionModel()->currentIndex(), Qt::DisplayRole)
+ .toString();
+}
+
+void LWJGLSelectDialog::on_refreshButton_clicked()
+{
+ if (!MMC->lwjgllist()->isLoading())
+ MMC->lwjgllist()->loadList();
+}
+
+void LWJGLSelectDialog::loadingStateUpdated(bool loading)
+{
+ setEnabled(!loading);
+ if (loading)
+ {
+ ui->labelStatus->setText(tr("Loading LWJGL version list..."));
+ ui->labelStatus->setStyleSheet("QLabel { color: black; }");
+ }
+ ui->labelStatus->setVisible(loading);
+}
+
+void LWJGLSelectDialog::loadingFailed(QString error)
+{
+ ui->labelStatus->setText(error);
+ ui->labelStatus->setStyleSheet("QLabel { color: red; }");
+ ui->labelStatus->setVisible(true);
+}
diff --git a/gui/dialogs/LwjglSelectDialog.h b/gui/dialogs/LwjglSelectDialog.h
new file mode 100644
index 00000000..2724cbe8
--- /dev/null
+++ b/gui/dialogs/LwjglSelectDialog.h
@@ -0,0 +1,44 @@
+/* Copyright 2013 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 <QDialog>
+
+namespace Ui
+{
+class LWJGLSelectDialog;
+}
+
+class LWJGLSelectDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit LWJGLSelectDialog(QWidget *parent = 0);
+ ~LWJGLSelectDialog();
+
+ QString selectedVersion() const;
+
+private
+slots:
+ void on_refreshButton_clicked();
+
+ void loadingStateUpdated(bool loading);
+ void loadingFailed(QString error);
+
+private:
+ Ui::LWJGLSelectDialog *ui;
+};
diff --git a/gui/dialogs/LwjglSelectDialog.ui b/gui/dialogs/LwjglSelectDialog.ui
new file mode 100644
index 00000000..0287ec8f
--- /dev/null
+++ b/gui/dialogs/LwjglSelectDialog.ui
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LWJGLSelectDialog</class>
+ <widget class="QDialog" name="LWJGLSelectDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Manage Lwjgl Versions</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="labelStatus">
+ <property name="text">
+ <string>Status label...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListView" name="lwjglListView"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="refreshButton">
+ <property name="text">
+ <string>&amp;Refresh</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>LWJGLSelectDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>LWJGLSelectDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/dialogs/ModEditDialogCommon.cpp b/gui/dialogs/ModEditDialogCommon.cpp
new file mode 100644
index 00000000..eee42e5e
--- /dev/null
+++ b/gui/dialogs/ModEditDialogCommon.cpp
@@ -0,0 +1,57 @@
+/* Copyright 2013 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 "ModEditDialogCommon.h"
+#include "CustomMessageBox.h"
+#include <QDesktopServices>
+#include <QMessageBox>
+#include <QString>
+#include <QUrl>
+bool lastfirst(QModelIndexList &list, int &first, int &last)
+{
+ if (!list.size())
+ return false;
+ first = last = list[0].row();
+ for (auto item : list)
+ {
+ int row = item.row();
+ if (row < first)
+ first = row;
+ if (row > last)
+ last = row;
+ }
+ return true;
+}
+
+void showWebsiteForMod(QWidget *parentDlg, Mod &m)
+{
+ QString url = m.homeurl();
+ if (url.size())
+ {
+ // catch the cases where the protocol is missing
+ if (!url.startsWith("http"))
+ {
+ url = "http://" + url;
+ }
+ QDesktopServices::openUrl(url);
+ }
+ else
+ {
+ CustomMessageBox::selectable(
+ parentDlg, QObject::tr("How sad!"),
+ QObject::tr("The mod author didn't provide a website link for this mod."),
+ QMessageBox::Warning);
+ }
+}
diff --git a/gui/dialogs/ModEditDialogCommon.h b/gui/dialogs/ModEditDialogCommon.h
new file mode 100644
index 00000000..a226d5a9
--- /dev/null
+++ b/gui/dialogs/ModEditDialogCommon.h
@@ -0,0 +1,22 @@
+/* Copyright 2013 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 <QAbstractItemModel>
+#include <logic/Mod.h>
+
+bool lastfirst(QModelIndexList &list, int &first, int &last);
+
+void showWebsiteForMod(QWidget *parentDlg, Mod &m); \ No newline at end of file
diff --git a/gui/dialogs/NewInstanceDialog.cpp b/gui/dialogs/NewInstanceDialog.cpp
new file mode 100644
index 00000000..c7b273af
--- /dev/null
+++ b/gui/dialogs/NewInstanceDialog.cpp
@@ -0,0 +1,125 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "NewInstanceDialog.h"
+#include "ui_NewInstanceDialog.h"
+
+#include "logic/InstanceFactory.h"
+#include "logic/BaseVersion.h"
+#include "logic/icons/IconList.h"
+#include "logic/lists/MinecraftVersionList.h"
+#include "logic/tasks/Task.h"
+
+#include "gui/Platform.h"
+#include "VersionSelectDialog.h"
+#include "ProgressDialog.h"
+#include "IconPickerDialog.h"
+
+#include <QLayout>
+#include <QPushButton>
+
+NewInstanceDialog::NewInstanceDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::NewInstanceDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ resize(minimumSizeHint());
+ layout()->setSizeConstraint(QLayout::SetFixedSize);
+ /*
+ if (!MinecraftVersionList::getMainList().isLoaded())
+ {
+ TaskDialog *taskDlg = new TaskDialog(this);
+ Task *loadTask = MinecraftVersionList::getMainList().getLoadTask();
+ loadTask->setParent(taskDlg);
+ taskDlg->exec(loadTask);
+ }
+ */
+ setSelectedVersion(MMC->minecraftlist()->getLatestStable());
+ InstIconKey = "infinity";
+ ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
+}
+
+NewInstanceDialog::~NewInstanceDialog()
+{
+ delete ui;
+}
+
+void NewInstanceDialog::updateDialogState()
+{
+ ui->buttonBox->button(QDialogButtonBox::Ok)
+ ->setEnabled(!instName().isEmpty() && m_selectedVersion);
+}
+
+void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version)
+{
+ m_selectedVersion = version;
+
+ if (m_selectedVersion)
+ {
+ ui->versionTextBox->setText(version->name());
+ }
+ else
+ {
+ ui->versionTextBox->setText("");
+ }
+
+ updateDialogState();
+}
+
+QString NewInstanceDialog::instName() const
+{
+ return ui->instNameTextBox->text();
+}
+
+QString NewInstanceDialog::iconKey() const
+{
+ return InstIconKey;
+}
+
+BaseVersionPtr NewInstanceDialog::selectedVersion() const
+{
+ return m_selectedVersion;
+}
+
+void NewInstanceDialog::on_btnChangeVersion_clicked()
+{
+ VersionSelectDialog vselect(MMC->minecraftlist().get(), tr("Change Minecraft version"),
+ this);
+ vselect.exec();
+ if (vselect.result() == QDialog::Accepted)
+ {
+ BaseVersionPtr version = vselect.selectedVersion();
+ if (version)
+ setSelectedVersion(version);
+ }
+}
+
+void NewInstanceDialog::on_iconButton_clicked()
+{
+ IconPickerDialog dlg(this);
+ dlg.exec(InstIconKey);
+
+ if (dlg.result() == QDialog::Accepted)
+ {
+ InstIconKey = dlg.selectedIconKey;
+ ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
+ }
+}
+
+void NewInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
+{
+ updateDialogState();
+}
diff --git a/gui/dialogs/NewInstanceDialog.h b/gui/dialogs/NewInstanceDialog.h
new file mode 100644
index 00000000..4357c28d
--- /dev/null
+++ b/gui/dialogs/NewInstanceDialog.h
@@ -0,0 +1,55 @@
+/* Copyright 2013 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 <QDialog>
+#include "logic/BaseVersion.h"
+
+namespace Ui
+{
+class NewInstanceDialog;
+}
+
+class NewInstanceDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit NewInstanceDialog(QWidget *parent = 0);
+ ~NewInstanceDialog();
+
+ void updateDialogState();
+
+ void setSelectedVersion(BaseVersionPtr version);
+
+ void loadVersionList();
+
+ QString instName() const;
+ QString iconKey() const;
+ BaseVersionPtr selectedVersion() const;
+
+private
+slots:
+ void on_btnChangeVersion_clicked();
+ void on_iconButton_clicked();
+ void on_instNameTextBox_textChanged(const QString &arg1);
+
+private:
+ Ui::NewInstanceDialog *ui;
+
+ BaseVersionPtr m_selectedVersion;
+ QString InstIconKey;
+};
diff --git a/gui/dialogs/NewInstanceDialog.ui b/gui/dialogs/NewInstanceDialog.ui
new file mode 100644
index 00000000..00544463
--- /dev/null
+++ b/gui/dialogs/NewInstanceDialog.ui
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NewInstanceDialog</class>
+ <widget class="QDialog" name="NewInstanceDialog">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>220</width>
+ <height>234</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>New Instance</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../../graphics.qrc">
+ <normaloff>:/icons/toolbar/new</normaloff>:/icons/toolbar/new</iconset>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="iconBtnLayout">
+ <item>
+ <spacer name="iconBtnLeftSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="iconButton">
+ <property name="icon">
+ <iconset resource="../../graphics.qrc">
+ <normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>80</width>
+ <height>80</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="iconBtnRightSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="instNameTextBox">
+ <property name="placeholderText">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelVersion">
+ <property name="text">
+ <string>Version:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="versionTextBox">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="btnChangeVersion">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../../graphics.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>NewInstanceDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>NewInstanceDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
new file mode 100644
index 00000000..27315c69
--- /dev/null
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -0,0 +1,367 @@
+/* Copyright 2013 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 "MultiMC.h"
+
+#include <pathutils.h>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QDebug>
+#include <QEvent>
+#include <QKeyEvent>
+#include <QDesktopServices>
+
+#include "OneSixModEditDialog.h"
+#include "ModEditDialogCommon.h"
+#include "ui_OneSixModEditDialog.h"
+
+#include "gui/Platform.h"
+#include "gui/dialogs/CustomMessageBox.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+
+#include "gui/dialogs/ProgressDialog.h"
+
+#include "logic/ModList.h"
+#include "logic/OneSixVersion.h"
+#include "logic/EnabledItemFilter.h"
+#include "logic/lists/ForgeVersionList.h"
+#include "logic/ForgeInstaller.h"
+#include "logic/LiteLoaderInstaller.h"
+
+OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
+ : QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ // libraries!
+
+ m_version = m_inst->getFullVersion();
+ if (m_version)
+ {
+ main_model = new EnabledItemFilter(this);
+ main_model->setActive(true);
+ main_model->setSourceModel(m_version.get());
+ ui->libraryTreeView->setModel(main_model);
+ ui->libraryTreeView->installEventFilter(this);
+ ui->mainClassEdit->setText(m_version->mainClass);
+ updateVersionControls();
+ }
+ else
+ {
+ disableVersionControls();
+ }
+ // Loader mods
+ {
+ ensureFolderPathExists(m_inst->loaderModsDir());
+ m_mods = m_inst->loaderModList();
+ ui->loaderModTreeView->setModel(m_mods.get());
+ ui->loaderModTreeView->installEventFilter(this);
+ m_mods->startWatching();
+ auto smodel = ui->loaderModTreeView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ SLOT(loaderCurrent(QModelIndex, QModelIndex)));
+ }
+ // resource packs
+ {
+ ensureFolderPathExists(m_inst->resourcePacksDir());
+ m_resourcepacks = m_inst->resourcePackList();
+ ui->resPackTreeView->setModel(m_resourcepacks.get());
+ ui->resPackTreeView->installEventFilter(this);
+ m_resourcepacks->startWatching();
+ }
+}
+
+OneSixModEditDialog::~OneSixModEditDialog()
+{
+ m_mods->stopWatching();
+ m_resourcepacks->stopWatching();
+ delete ui;
+}
+
+void OneSixModEditDialog::updateVersionControls()
+{
+ bool customVersion = m_inst->versionIsCustom();
+ ui->customizeBtn->setEnabled(!customVersion);
+ ui->revertBtn->setEnabled(customVersion);
+ ui->forgeBtn->setEnabled(true);
+ ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply());
+ ui->customEditorBtn->setEnabled(customVersion);
+}
+
+void OneSixModEditDialog::disableVersionControls()
+{
+ ui->customizeBtn->setEnabled(false);
+ ui->revertBtn->setEnabled(false);
+ ui->forgeBtn->setEnabled(false);
+ ui->liteloaderBtn->setEnabled(false);
+ ui->customEditorBtn->setEnabled(false);
+}
+
+void OneSixModEditDialog::on_customizeBtn_clicked()
+{
+ if (m_inst->customizeVersion())
+ {
+ m_version = m_inst->getFullVersion();
+ main_model->setSourceModel(m_version.get());
+ updateVersionControls();
+ }
+}
+
+void OneSixModEditDialog::on_revertBtn_clicked()
+{
+ auto response = CustomMessageBox::selectable(
+ this, tr("Revert?"), tr("Do you want to revert the "
+ "version of this instance to its original configuration?"),
+ QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec();
+ if (response == QMessageBox::Yes)
+ {
+ if (m_inst->revertCustomVersion())
+ {
+ m_version = m_inst->getFullVersion();
+ main_model->setSourceModel(m_version.get());
+ updateVersionControls();
+ }
+ }
+}
+
+void OneSixModEditDialog::on_customEditorBtn_clicked()
+{
+ if (m_inst->versionIsCustom())
+ {
+ if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json"))
+ {
+ QMessageBox::warning(this, tr("Error"),
+ tr("Unable to open custom.json, check the settings"));
+ }
+ }
+}
+
+void OneSixModEditDialog::on_forgeBtn_clicked()
+{
+ VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
+ vselect.setFilter(1, m_inst->currentVersionId());
+ vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
+ m_inst->currentVersionId());
+ if (vselect.exec() && vselect.selectedVersion())
+ {
+ if (m_inst->versionIsCustom())
+ {
+ auto reply = QMessageBox::question(
+ this, tr("Revert?"),
+ tr("This will revert any "
+ "changes you did to the version up to this point. Is that "
+ "OK?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (reply == QMessageBox::Yes)
+ {
+ m_inst->revertCustomVersion();
+ m_inst->customizeVersion();
+ {
+ m_version = m_inst->getFullVersion();
+ main_model->setSourceModel(m_version.get());
+ updateVersionControls();
+ }
+ }
+ else
+ return;
+ }
+ else
+ {
+ m_inst->customizeVersion();
+ m_version = m_inst->getFullVersion();
+ main_model->setSourceModel(m_version.get());
+ updateVersionControls();
+ }
+ ForgeVersionPtr forgeVersion =
+ std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
+ if (!forgeVersion)
+ return;
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
+ if (entry->stale)
+ {
+ NetJob *fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
+ ProgressDialog dlg(this);
+ dlg.exec(fjob);
+ if (dlg.result() == QDialog::Accepted)
+ {
+ // install
+ QString forgePath = entry->getFullPath();
+ ForgeInstaller forge(forgePath, forgeVersion->universal_url);
+ if (!forge.apply(m_version))
+ {
+ // failure notice
+ }
+ }
+ else
+ {
+ // failed to download forge :/
+ }
+ }
+ else
+ {
+ // install
+ QString forgePath = entry->getFullPath();
+ ForgeInstaller forge(forgePath, forgeVersion->universal_url);
+ if (!forge.apply(m_version))
+ {
+ // failure notice
+ }
+ }
+ }
+}
+
+void OneSixModEditDialog::on_liteloaderBtn_clicked()
+{
+ LiteLoaderInstaller liteloader(m_inst->intendedVersionId());
+ if (!liteloader.canApply())
+ {
+ QMessageBox::critical(
+ this, tr("LiteLoader"),
+ tr("There is no information available on how to install LiteLoader "
+ "into this version of Minecraft"));
+ return;
+ }
+ if (!m_inst->versionIsCustom())
+ {
+ m_inst->customizeVersion();
+ m_version = m_inst->getFullVersion();
+ main_model->setSourceModel(m_version.get());
+ updateVersionControls();
+ }
+ if (!liteloader.apply(m_version))
+ {
+ QMessageBox::critical(this, tr("LiteLoader"),
+ tr("For reasons unknown, the LiteLoader installation failed. "
+ "Check your MultiMC log files for details."));
+ }
+}
+
+bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ on_rmModBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addModBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(ui->loaderModTreeView, keyEvent);
+}
+
+bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent)
+{
+ switch (keyEvent->key())
+ {
+ case Qt::Key_Delete:
+ on_rmResPackBtn_clicked();
+ return true;
+ case Qt::Key_Plus:
+ on_addResPackBtn_clicked();
+ return true;
+ default:
+ break;
+ }
+ return QDialog::eventFilter(ui->resPackTreeView, keyEvent);
+}
+
+bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev)
+{
+ if (ev->type() != QEvent::KeyPress)
+ {
+ return QDialog::eventFilter(obj, ev);
+ }
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
+ if (obj == ui->loaderModTreeView)
+ return loaderListFilter(keyEvent);
+ if (obj == ui->resPackTreeView)
+ return resourcePackListFilter(keyEvent);
+ return QDialog::eventFilter(obj, ev);
+}
+
+void OneSixModEditDialog::on_buttonBox_rejected()
+{
+ close();
+}
+
+void OneSixModEditDialog::on_addModBtn_clicked()
+{
+ QStringList fileNames = QFileDialog::getOpenFileNames(
+ this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods"));
+ for (auto filename : fileNames)
+ {
+ m_mods->stopWatching();
+ m_mods->installMod(QFileInfo(filename));
+ m_mods->startWatching();
+ }
+}
+void OneSixModEditDialog::on_rmModBtn_clicked()
+{
+ int first, last;
+ auto list = ui->loaderModTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_mods->stopWatching();
+ m_mods->deleteMods(first, last);
+ m_mods->startWatching();
+}
+void OneSixModEditDialog::on_viewModBtn_clicked()
+{
+ openDirInDefaultProgram(m_inst->loaderModsDir(), true);
+}
+
+void OneSixModEditDialog::on_addResPackBtn_clicked()
+{
+ QStringList fileNames = QFileDialog::getOpenFileNames(
+ this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs"));
+ for (auto filename : fileNames)
+ {
+ m_resourcepacks->stopWatching();
+ m_resourcepacks->installMod(QFileInfo(filename));
+ m_resourcepacks->startWatching();
+ }
+}
+void OneSixModEditDialog::on_rmResPackBtn_clicked()
+{
+ int first, last;
+ auto list = ui->resPackTreeView->selectionModel()->selectedRows();
+
+ if (!lastfirst(list, first, last))
+ return;
+ m_resourcepacks->stopWatching();
+ m_resourcepacks->deleteMods(first, last);
+ m_resourcepacks->startWatching();
+}
+void OneSixModEditDialog::on_viewResPackBtn_clicked()
+{
+ openDirInDefaultProgram(m_inst->resourcePacksDir(), true);
+}
+
+void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous)
+{
+ if (!current.isValid())
+ {
+ ui->frame->clear();
+ return;
+ }
+ int row = current.row();
+ Mod &m = m_mods->operator[](row);
+ ui->frame->updateWithMod(m);
+}
diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h
new file mode 100644
index 00000000..2510c59c
--- /dev/null
+++ b/gui/dialogs/OneSixModEditDialog.h
@@ -0,0 +1,69 @@
+/* Copyright 2013 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 <QDialog>
+
+#include <logic/OneSixInstance.h>
+
+class EnabledItemFilter;
+namespace Ui
+{
+class OneSixModEditDialog;
+}
+
+class OneSixModEditDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit OneSixModEditDialog(OneSixInstance *inst, QWidget *parent = 0);
+ virtual ~OneSixModEditDialog();
+
+private
+slots:
+ void on_addModBtn_clicked();
+ void on_rmModBtn_clicked();
+ void on_viewModBtn_clicked();
+
+ void on_addResPackBtn_clicked();
+ void on_rmResPackBtn_clicked();
+ void on_viewResPackBtn_clicked();
+ // Questionable: SettingsDialog doesn't need this for some reason?
+ void on_buttonBox_rejected();
+ void on_forgeBtn_clicked();
+ void on_liteloaderBtn_clicked();
+ void on_customizeBtn_clicked();
+ void on_revertBtn_clicked();
+ void on_customEditorBtn_clicked();
+ void updateVersionControls();
+ void disableVersionControls();
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *ev);
+ bool loaderListFilter(QKeyEvent *ev);
+ bool resourcePackListFilter(QKeyEvent *ev);
+
+private:
+ Ui::OneSixModEditDialog *ui;
+ std::shared_ptr<OneSixVersion> m_version;
+ std::shared_ptr<ModList> m_mods;
+ std::shared_ptr<ModList> m_resourcepacks;
+ EnabledItemFilter *main_model;
+ OneSixInstance *m_inst;
+public
+slots:
+ void loaderCurrent(QModelIndex current, QModelIndex previous);
+};
diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui
new file mode 100644
index 00000000..899e0cbf
--- /dev/null
+++ b/gui/dialogs/OneSixModEditDialog.ui
@@ -0,0 +1,340 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OneSixModEditDialog</class>
+ <widget class="QDialog" name="OneSixModEditDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>555</width>
+ <height>463</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Manage Mods</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <widget class="QWidget" name="libTab">
+ <attribute name="title">
+ <string>Version</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_10">
+ <item>
+ <widget class="ModListView" name="libraryTreeView">
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOn</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Main Class:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="mainClassEdit">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QPushButton" name="forgeBtn">
+ <property name="toolTip">
+ <string>Replace any current custom version with Minecraft Forge</string>
+ </property>
+ <property name="text">
+ <string>Install Forge</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="liteloaderBtn">
+ <property name="text">
+ <string>Install LiteLoader</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="customizeBtn">
+ <property name="toolTip">
+ <string>Create an customized copy of the base version</string>
+ </property>
+ <property name="text">
+ <string>Customize</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="revertBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Revert to original base version</string>
+ </property>
+ <property name="text">
+ <string>Revert</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addLibraryBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Add new libraries</string>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeLibraryBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Remove selected libraries</string>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="customEditorBtn">
+ <property name="text">
+ <string>Open custom.json</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="modTab">
+ <attribute name="title">
+ <string>Loader Mods</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="ModListView" name="loaderModTreeView">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DropOnly</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QPushButton" name="addModBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmModBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewModBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MCModInfoFrame" name="frame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="resPackTab">
+ <attribute name="title">
+ <string>Resource Packs</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="ModListView" name="resPackTreeView">
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DropOnly</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QPushButton" name="addResPackBtn">
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="rmResPackBtn">
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="viewResPackBtn">
+ <property name="text">
+ <string>&amp;View Folder</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ModListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/ModListView.h</header>
+ </customwidget>
+ <customwidget>
+ <class>MCModInfoFrame</class>
+ <extends>QFrame</extends>
+ <header>gui/widgets/MCModInfoFrame.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/ProgressDialog.cpp b/gui/dialogs/ProgressDialog.cpp
new file mode 100644
index 00000000..ba14cca2
--- /dev/null
+++ b/gui/dialogs/ProgressDialog.cpp
@@ -0,0 +1,125 @@
+/* Copyright 2013 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 "ProgressDialog.h"
+#include "ui_ProgressDialog.h"
+
+#include <QKeyEvent>
+
+#include "logic/tasks/Task.h"
+#include "gui/Platform.h"
+
+ProgressDialog::ProgressDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ProgressDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ setSkipButton(false);
+ changeProgress(0, 100);
+}
+
+void ProgressDialog::setSkipButton(bool present, QString label)
+{
+ ui->skipButton->setEnabled(present);
+ ui->skipButton->setVisible(present);
+ ui->skipButton->setText(label);
+ updateSize();
+}
+
+void ProgressDialog::on_skipButton_clicked(bool checked)
+{
+ Q_UNUSED(checked);
+ task->abort();
+}
+
+ProgressDialog::~ProgressDialog()
+{
+ delete ui;
+}
+
+void ProgressDialog::updateSize()
+{
+ resize(QSize(480, minimumSizeHint().height()));
+}
+
+int ProgressDialog::exec(ProgressProvider *task)
+{
+ this->task = task;
+
+ // Connect signals.
+ connect(task, SIGNAL(started()), SLOT(onTaskStarted()));
+ connect(task, SIGNAL(failed(QString)), SLOT(onTaskFailed(QString)));
+ connect(task, SIGNAL(succeeded()), SLOT(onTaskSucceeded()));
+ connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &)));
+ connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64)));
+
+ // if this didn't connect to an already running task, invoke start
+ if(!task->isRunning())
+ task->start();
+ if(task->isRunning())
+ return QDialog::exec();
+ else
+ return 0;
+}
+
+ProgressProvider *ProgressDialog::getTask()
+{
+ return task;
+}
+
+void ProgressDialog::onTaskStarted()
+{
+}
+
+void ProgressDialog::onTaskFailed(QString failure)
+{
+ reject();
+}
+
+void ProgressDialog::onTaskSucceeded()
+{
+ accept();
+}
+
+void ProgressDialog::changeStatus(const QString &status)
+{
+ ui->statusLabel->setText(status);
+ updateSize();
+}
+
+void ProgressDialog::changeProgress(qint64 current, qint64 total)
+{
+ ui->taskProgressBar->setMaximum(total);
+ ui->taskProgressBar->setValue(current);
+}
+
+void ProgressDialog::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_Escape)
+ return;
+ QDialog::keyPressEvent(e);
+}
+
+void ProgressDialog::closeEvent(QCloseEvent *e)
+{
+ if (task && task->isRunning())
+ {
+ e->ignore();
+ }
+ else
+ {
+ QDialog::closeEvent(e);
+ }
+}
diff --git a/gui/dialogs/ProgressDialog.h b/gui/dialogs/ProgressDialog.h
new file mode 100644
index 00000000..fe63a826
--- /dev/null
+++ b/gui/dialogs/ProgressDialog.h
@@ -0,0 +1,64 @@
+/* Copyright 2013 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 <QDialog>
+
+class ProgressProvider;
+
+namespace Ui
+{
+class ProgressDialog;
+}
+
+class ProgressDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ProgressDialog(QWidget *parent = 0);
+ ~ProgressDialog();
+
+ void updateSize();
+
+ int exec(ProgressProvider *task);
+ void setSkipButton(bool present, QString label = QString());
+
+ ProgressProvider *getTask();
+
+public
+slots:
+ void onTaskStarted();
+ void onTaskFailed(QString failure);
+ void onTaskSucceeded();
+
+ void changeStatus(const QString &status);
+ void changeProgress(qint64 current, qint64 total);
+
+
+private
+slots:
+ void on_skipButton_clicked(bool checked);
+
+protected:
+ virtual void keyPressEvent(QKeyEvent *e);
+ virtual void closeEvent(QCloseEvent *e);
+
+private:
+ Ui::ProgressDialog *ui;
+
+ ProgressProvider *task;
+};
diff --git a/gui/dialogs/ProgressDialog.ui b/gui/dialogs/ProgressDialog.ui
new file mode 100644
index 00000000..04b8fef3
--- /dev/null
+++ b/gui/dialogs/ProgressDialog.ui
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ProgressDialog</class>
+ <widget class="QDialog" name="ProgressDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>400</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>600</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Please wait...</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="statusLabel">
+ <property name="text">
+ <string>Task Status...</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QProgressBar" name="taskProgressBar">
+ <property name="value">
+ <number>24</number>
+ </property>
+ <property name="textVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QPushButton" name="skipButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Skip</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp
new file mode 100644
index 00000000..ef363f02
--- /dev/null
+++ b/gui/dialogs/SettingsDialog.cpp
@@ -0,0 +1,505 @@
+/* Copyright 2013 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 "MultiMC.h"
+
+#include "gui/dialogs/SettingsDialog.h"
+#include "ui_SettingsDialog.h"
+
+#include "gui/Platform.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "gui/dialogs/CustomMessageBox.h"
+
+#include "logic/JavaUtils.h"
+#include "logic/NagUtils.h"
+#include "logic/lists/JavaVersionList.h"
+#include <logic/JavaChecker.h>
+
+#include "logic/updater/UpdateChecker.h"
+
+#include <settingsobject.h>
+#include <pathutils.h>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QDir>
+
+SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SettingsDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ ui->sortingModeGroup->setId(ui->sortByNameBtn, Sort_Name);
+ ui->sortingModeGroup->setId(ui->sortLastLaunchedBtn, Sort_LastLaunch);
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+ ui->jsonEditorTextBox->setClearButtonEnabled(true);
+#endif
+
+ restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
+
+ loadSettings(MMC->settings().get());
+ updateCheckboxStuff();
+
+ QObject::connect(MMC->updateChecker().get(), &UpdateChecker::channelListLoaded, this, &SettingsDialog::refreshUpdateChannelList);
+
+ if (MMC->updateChecker()->hasChannels())
+ {
+ refreshUpdateChannelList();
+ }
+ else
+ {
+ MMC->updateChecker()->updateChanList();
+ }
+ connect(ui->proxyGroup, SIGNAL(buttonClicked(int)), SLOT(proxyChanged(int)));
+}
+
+SettingsDialog::~SettingsDialog()
+{
+ delete ui;
+}
+void SettingsDialog::showEvent(QShowEvent *ev)
+{
+ QDialog::showEvent(ev);
+}
+
+void SettingsDialog::closeEvent(QCloseEvent *ev)
+{
+ MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
+
+ QDialog::closeEvent(ev);
+}
+
+void SettingsDialog::updateCheckboxStuff()
+{
+ ui->windowWidthSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
+ ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
+ ui->proxyAddrBox->setEnabled(!ui->proxyNoneBtn->isChecked() && !ui->proxyDefaultBtn->isChecked());
+ ui->proxyAuthBox->setEnabled(!ui->proxyNoneBtn->isChecked() && !ui->proxyDefaultBtn->isChecked());
+}
+
+void SettingsDialog::on_ftbLauncherBrowseBtn_clicked()
+{
+ QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Launcher Directory"),
+ ui->ftbLauncherBox->text());
+ QString cooked_dir = NormalizePath(raw_dir);
+
+ // do not allow current dir - it's dirty. Do not allow dirs that don't exist
+ if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
+ {
+ ui->ftbLauncherBox->setText(cooked_dir);
+ }
+}
+
+void SettingsDialog::on_ftbBrowseBtn_clicked()
+{
+ QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Directory"),
+ ui->ftbBox->text());
+ QString cooked_dir = NormalizePath(raw_dir);
+
+ // do not allow current dir - it's dirty. Do not allow dirs that don't exist
+ if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
+ {
+ ui->ftbBox->setText(cooked_dir);
+ }
+}
+
+void SettingsDialog::on_instDirBrowseBtn_clicked()
+{
+ QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Instance Directory"),
+ ui->instDirTextBox->text());
+ QString cooked_dir = NormalizePath(raw_dir);
+
+ // do not allow current dir - it's dirty. Do not allow dirs that don't exist
+ if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
+ {
+ ui->instDirTextBox->setText(cooked_dir);
+ }
+}
+void SettingsDialog::on_iconsDirBrowseBtn_clicked()
+{
+ QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Icons Directory"),
+ ui->iconsDirTextBox->text());
+ QString cooked_dir = NormalizePath(raw_dir);
+
+ // do not allow current dir - it's dirty. Do not allow dirs that don't exist
+ if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
+ {
+ ui->iconsDirTextBox->setText(cooked_dir);
+ }
+}
+
+void SettingsDialog::on_modsDirBrowseBtn_clicked()
+{
+ QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Mods Directory"),
+ ui->modsDirTextBox->text());
+ QString cooked_dir = NormalizePath(raw_dir);
+
+ // do not allow current dir - it's dirty. Do not allow dirs that don't exist
+ if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
+ {
+ ui->modsDirTextBox->setText(cooked_dir);
+ }
+}
+
+void SettingsDialog::on_lwjglDirBrowseBtn_clicked()
+{
+ QString raw_dir = QFileDialog::getExistingDirectory(this, tr("LWJGL Directory"),
+ ui->lwjglDirTextBox->text());
+ QString cooked_dir = NormalizePath(raw_dir);
+
+ // do not allow current dir - it's dirty. Do not allow dirs that don't exist
+ if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists())
+ {
+ ui->lwjglDirTextBox->setText(cooked_dir);
+ }
+}
+
+void SettingsDialog::on_jsonEditorBrowseBtn_clicked()
+{
+ QString raw_file = QFileDialog::getOpenFileName(
+ this, tr("JSON Editor"),
+ ui->jsonEditorTextBox->text().isEmpty()
+ #if defined(Q_OS_LINUX)
+ ? QString("/usr/bin")
+ #else
+ ? QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).first()
+ #endif
+ : ui->jsonEditorTextBox->text());
+ QString cooked_file = NormalizePath(raw_file);
+
+ if (cooked_file.isEmpty())
+ {
+ return;
+ }
+
+ // it has to exist and be an executable
+ if (QFileInfo(cooked_file).exists() &&
+ QFileInfo(cooked_file).isExecutable())
+ {
+ ui->jsonEditorTextBox->setText(cooked_file);
+ }
+ else
+ {
+ QMessageBox::warning(this, tr("Invalid"), tr("The file chosen does not seem to be an executable"));
+ }
+}
+
+void SettingsDialog::on_maximizedCheckBox_clicked(bool checked)
+{
+ Q_UNUSED(checked);
+ updateCheckboxStuff();
+}
+
+void SettingsDialog::on_buttonBox_accepted()
+{
+ applySettings(MMC->settings().get());
+
+ // Apply proxy settings
+ MMC->updateProxySettings();
+
+ MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
+}
+
+void SettingsDialog::on_buttonBox_rejected()
+{
+ MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
+}
+
+void SettingsDialog::proxyChanged(int)
+{
+ updateCheckboxStuff();
+}
+
+void SettingsDialog::refreshUpdateChannelList()
+{
+ // Stop listening for selection changes. It's going to change a lot while we update it and we don't need to update the
+ // description label constantly.
+ QObject::disconnect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChannelSelectionChanged(int)));
+
+ QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList();
+ ui->updateChannelComboBox->clear();
+ int selection = -1;
+ for (int i = 0; i < channelList.count(); i++)
+ {
+ UpdateChecker::ChannelListEntry entry = channelList.at(i);
+
+ // When it comes to selection, we'll rely on the indexes of a channel entry being the same in the
+ // combo box as it is in the update checker's channel list.
+ // This probably isn't very safe, but the channel list doesn't change often enough (or at all) for
+ // this to be a big deal. Hope it doesn't break...
+ ui->updateChannelComboBox->addItem(entry.name);
+
+ // If the update channel we just added was the selected one, set the current index in the combo box to it.
+ if (entry.id == m_currentUpdateChannel)
+ {
+ QLOG_DEBUG() << "Selected index" << i << "channel id" << m_currentUpdateChannel;
+ selection = i;
+ }
+ }
+
+ ui->updateChannelComboBox->setCurrentIndex(selection);
+
+ // Start listening for selection changes again and update the description label.
+ QObject::connect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChannelSelectionChanged(int)));
+ refreshUpdateChannelDesc();
+
+ // Now that we've updated the channel list, we can enable the combo box.
+ // It starts off disabled so that if the channel list hasn't been loaded, it will be disabled.
+ ui->updateChannelComboBox->setEnabled(true);
+}
+
+void SettingsDialog::updateChannelSelectionChanged(int index)
+{
+ refreshUpdateChannelDesc();
+}
+
+void SettingsDialog::refreshUpdateChannelDesc()
+{
+ // Get the channel list.
+ QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList();
+ int selectedIndex = ui->updateChannelComboBox->currentIndex();
+ if(selectedIndex < 0)
+ {
+ return;
+ }
+ if (selectedIndex < channelList.count())
+ {
+ // Find the channel list entry with the given index.
+ UpdateChecker::ChannelListEntry selected = channelList.at(selectedIndex);
+
+ // Set the description text.
+ ui->updateChannelDescLabel->setText(selected.description);
+
+ // Set the currently selected channel ID.
+ m_currentUpdateChannel = selected.id;
+ }
+}
+
+void SettingsDialog::applySettings(SettingsObject *s)
+{
+ // Language
+ s->set("Language", ui->languageBox->itemData(ui->languageBox->currentIndex()).toLocale().bcp47Name());
+
+ // Updates
+ s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
+ s->set("UpdateChannel", m_currentUpdateChannel);
+
+ // FTB
+ s->set("TrackFTBInstances", ui->trackFtbBox->isChecked());
+ s->set("FTBLauncherRoot", ui->ftbLauncherBox->text());
+ s->set("FTBRoot", ui->ftbBox->text());
+
+ // Folders
+ // TODO: Offer to move instances to new instance folder.
+ s->set("InstanceDir", ui->instDirTextBox->text());
+ s->set("CentralModsDir", ui->modsDirTextBox->text());
+ s->set("LWJGLDir", ui->lwjglDirTextBox->text());
+ s->set("IconsDir", ui->iconsDirTextBox->text());
+
+ // Editors
+ QString jsonEditor = ui->jsonEditorTextBox->text();
+ if (!jsonEditor.isEmpty() && (!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable()))
+ {
+ QString found = QStandardPaths::findExecutable(jsonEditor);
+ if (!found.isEmpty())
+ {
+ jsonEditor = found;
+ }
+ }
+ s->set("JsonEditor", jsonEditor);
+
+ // Console
+ s->set("ShowConsole", ui->showConsoleCheck->isChecked());
+ s->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
+
+ // Window Size
+ s->set("LaunchMaximized", ui->maximizedCheckBox->isChecked());
+ s->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
+ s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
+
+ // Proxy
+ QString proxyType = "None";
+ if (ui->proxyDefaultBtn->isChecked()) proxyType = "Default";
+ else if (ui->proxyNoneBtn->isChecked()) proxyType = "None";
+ else if (ui->proxySOCKS5Btn->isChecked()) proxyType = "SOCKS5";
+ else if (ui->proxyHTTPBtn->isChecked()) proxyType = "HTTP";
+
+ s->set("ProxyType", proxyType);
+ s->set("ProxyAddr", ui->proxyAddrEdit->text());
+ s->set("ProxyPort", ui->proxyPortEdit->value());
+ s->set("ProxyUser", ui->proxyUserEdit->text());
+ s->set("ProxyPass", ui->proxyPassEdit->text());
+
+ // Memory
+ s->set("MinMemAlloc", ui->minMemSpinBox->value());
+ s->set("MaxMemAlloc", ui->maxMemSpinBox->value());
+ s->set("PermGen", ui->permGenSpinBox->value());
+
+ // Java Settings
+ s->set("JavaPath", ui->javaPathTextBox->text());
+ s->set("JvmArgs", ui->jvmArgsTextBox->text());
+ NagUtils::checkJVMArgs(s->get("JvmArgs").toString(), this->parentWidget());
+
+ // Custom Commands
+ s->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text());
+ s->set("PostExitCommand", ui->postExitCmdTextBox->text());
+
+ auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId();
+ switch (sortMode)
+ {
+ case Sort_LastLaunch:
+ s->set("InstSortMode", "LastLaunch");
+ break;
+ case Sort_Name:
+ default:
+ s->set("InstSortMode", "Name");
+ break;
+ }
+
+ s->set("PostExitCommand", ui->postExitCmdTextBox->text());
+}
+
+void SettingsDialog::loadSettings(SettingsObject *s)
+{
+ // Language
+ ui->languageBox->clear();
+ ui->languageBox->addItem(tr("English"), QLocale(QLocale::English));
+ foreach(const QString & lang,
+ QDir(MMC->root() + "/translations").entryList(QStringList() << "*.qm", QDir::Files))
+ {
+ QLocale locale(lang.section(QRegExp("[_\.]"), 1));
+ ui->languageBox->addItem(
+ QLocale::languageToString(locale.language()),
+ locale);
+ }
+ ui->languageBox->setCurrentIndex(ui->languageBox->findData(QLocale(s->get("Language").toString())));
+
+ // Updates
+ ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
+ m_currentUpdateChannel = s->get("UpdateChannel").toString();
+
+ // FTB
+ ui->trackFtbBox->setChecked(s->get("TrackFTBInstances").toBool());
+ ui->ftbLauncherBox->setText(s->get("FTBLauncherRoot").toString());
+ ui->ftbBox->setText(s->get("FTBRoot").toString());
+
+ // Folders
+ ui->instDirTextBox->setText(s->get("InstanceDir").toString());
+ ui->modsDirTextBox->setText(s->get("CentralModsDir").toString());
+ ui->lwjglDirTextBox->setText(s->get("LWJGLDir").toString());
+ ui->iconsDirTextBox->setText(s->get("IconsDir").toString());
+
+ // Editors
+ ui->jsonEditorTextBox->setText(s->get("JsonEditor").toString());
+
+ // Console
+ ui->showConsoleCheck->setChecked(s->get("ShowConsole").toBool());
+ ui->autoCloseConsoleCheck->setChecked(s->get("AutoCloseConsole").toBool());
+
+ // Window Size
+ ui->maximizedCheckBox->setChecked(s->get("LaunchMaximized").toBool());
+ ui->windowWidthSpinBox->setValue(s->get("MinecraftWinWidth").toInt());
+ ui->windowHeightSpinBox->setValue(s->get("MinecraftWinHeight").toInt());
+
+ // Memory
+ ui->minMemSpinBox->setValue(s->get("MinMemAlloc").toInt());
+ ui->maxMemSpinBox->setValue(s->get("MaxMemAlloc").toInt());
+ ui->permGenSpinBox->setValue(s->get("PermGen").toInt());
+
+ QString sortMode = s->get("InstSortMode").toString();
+
+ if (sortMode == "LastLaunch")
+ {
+ ui->sortLastLaunchedBtn->setChecked(true);
+ }
+ else
+ {
+ ui->sortByNameBtn->setChecked(true);
+ }
+
+ // Proxy
+ QString proxyType = s->get("ProxyType").toString();
+ if (proxyType == "Default") ui->proxyDefaultBtn->setChecked(true);
+ else if (proxyType == "None") ui->proxyNoneBtn->setChecked(true);
+ else if (proxyType == "SOCKS5") ui->proxySOCKS5Btn->setChecked(true);
+ else if (proxyType == "HTTP") ui->proxyHTTPBtn->setChecked(true);
+
+ ui->proxyAddrEdit->setText(s->get("ProxyAddr").toString());
+ ui->proxyPortEdit->setValue(s->get("ProxyPort").value<qint16>());
+ ui->proxyUserEdit->setText(s->get("ProxyUser").toString());
+ ui->proxyPassEdit->setText(s->get("ProxyPass").toString());
+
+ // Java Settings
+ ui->javaPathTextBox->setText(s->get("JavaPath").toString());
+ ui->jvmArgsTextBox->setText(s->get("JvmArgs").toString());
+
+ // Custom Commands
+ ui->preLaunchCmdTextBox->setText(s->get("PreLaunchCommand").toString());
+ ui->postExitCmdTextBox->setText(s->get("PostExitCommand").toString());
+}
+
+void SettingsDialog::on_javaDetectBtn_clicked()
+{
+ JavaVersionPtr java;
+
+ VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true);
+ vselect.setResizeOn(2);
+ vselect.exec();
+
+ if (vselect.result() == QDialog::Accepted && vselect.selectedVersion())
+ {
+ java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
+ ui->javaPathTextBox->setText(java->path);
+ }
+}
+
+void SettingsDialog::on_javaBrowseBtn_clicked()
+{
+ QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
+ if (!dir.isNull())
+ {
+ ui->javaPathTextBox->setText(dir);
+ }
+}
+
+void SettingsDialog::on_javaTestBtn_clicked()
+{
+ checker.reset(new JavaChecker());
+ connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
+ SLOT(checkFinished(JavaCheckResult)));
+ checker->path = ui->javaPathTextBox->text();
+ checker->performCheck();
+}
+
+void SettingsDialog::checkFinished(JavaCheckResult result)
+{
+ if (result.valid)
+ {
+ QString text;
+ text += "Java test succeeded!\n";
+ if (result.is_64bit)
+ text += "Using 64bit java.\n";
+ text += "\n";
+ text += "Platform reported: " + result.realPlatform + "\n";
+ text += "Java version reported: " + result.javaVersion;
+ QMessageBox::information(this, tr("Java test success"), text);
+ }
+ else
+ {
+ QMessageBox::warning(
+ this, tr("Java test failure"),
+ tr("The specified java binary didn't work. You should use the auto-detect feature, "
+ "or set the path to the java executable."));
+ }
+}
diff --git a/gui/dialogs/SettingsDialog.h b/gui/dialogs/SettingsDialog.h
new file mode 100644
index 00000000..d7bbbeb3
--- /dev/null
+++ b/gui/dialogs/SettingsDialog.h
@@ -0,0 +1,99 @@
+/* Copyright 2013 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 <memory>
+#include <QDialog>
+
+#include "logic/JavaChecker.h"
+
+class SettingsObject;
+
+namespace Ui
+{
+class SettingsDialog;
+}
+
+class SettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsDialog(QWidget *parent = 0);
+ ~SettingsDialog();
+
+ void updateCheckboxStuff();
+
+ void applySettings(SettingsObject *s);
+ void loadSettings(SettingsObject *s);
+
+protected:
+ virtual void showEvent(QShowEvent *ev);
+ virtual void closeEvent(QCloseEvent *ev);
+
+private
+slots:
+ void on_ftbLauncherBrowseBtn_clicked();
+
+ void on_ftbBrowseBtn_clicked();
+
+ void on_instDirBrowseBtn_clicked();
+
+ void on_modsDirBrowseBtn_clicked();
+
+ void on_lwjglDirBrowseBtn_clicked();
+
+
+ void on_jsonEditorBrowseBtn_clicked();
+
+ void on_iconsDirBrowseBtn_clicked();
+
+ void on_maximizedCheckBox_clicked(bool checked);
+
+ void on_buttonBox_accepted();
+
+ void on_buttonBox_rejected();
+
+ void on_javaDetectBtn_clicked();
+
+ void on_javaTestBtn_clicked();
+
+ void on_javaBrowseBtn_clicked();
+
+ void checkFinished(JavaCheckResult result);
+
+ /*!
+ * Updates the list of update channels in the combo box.
+ */
+ void refreshUpdateChannelList();
+
+ /*!
+ * Updates the channel description label.
+ */
+ void refreshUpdateChannelDesc();
+
+ void updateChannelSelectionChanged(int index);
+ void proxyChanged(int);
+
+private:
+ Ui::SettingsDialog *ui;
+ std::shared_ptr<JavaChecker> checker;
+
+ /*!
+ * Stores the currently selected update channel.
+ */
+ QString m_currentUpdateChannel;
+};
diff --git a/gui/dialogs/SettingsDialog.ui b/gui/dialogs/SettingsDialog.ui
new file mode 100644
index 00000000..54e7db7a
--- /dev/null
+++ b/gui/dialogs/SettingsDialog.ui
@@ -0,0 +1,944 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>545</width>
+ <height>609</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../../graphics.qrc">
+ <normaloff>:/icons/toolbar/settings</normaloff>:/icons/toolbar/settings</iconset>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="mainLayout">
+ <item>
+ <widget class="QTabWidget" name="settingsTabs">
+ <property name="tabShape">
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="featuresTab">
+ <attribute name="title">
+ <string>Features</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_9">
+ <item>
+ <widget class="QGroupBox" name="updateSettingsBox">
+ <property name="title">
+ <string>Update Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QCheckBox" name="autoUpdateCheckBox">
+ <property name="text">
+ <string>Check for updates when MultiMC starts?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="updateChannelLabel">
+ <property name="text">
+ <string>Update Channel:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="updateChannelComboBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="updateChannelDescLabel">
+ <property name="text">
+ <string>No channel selected.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>FTB</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="2" column="2">
+ <widget class="QPushButton" name="ftbLauncherBrowseBtn">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>28</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Launcher:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="ftbLauncherBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QCheckBox" name="trackFtbBox">
+ <property name="text">
+ <string>Track FTB instances</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="ftbBox"/>
+ </item>
+ <item row="3" column="2">
+ <widget class="QPushButton" name="ftbBrowseBtn">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>28</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Files:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="foldersBox">
+ <property name="title">
+ <string>Folders</string>
+ </property>
+ <layout class="QGridLayout" name="foldersBoxLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelInstDir">
+ <property name="text">
+ <string>Instances:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="instDirTextBox"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QToolButton" name="instDirBrowseBtn">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelModsDir">
+ <property name="text">
+ <string>Mods:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="modsDirTextBox"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="lwjglDirTextBox"/>
+ </item>
+ <item row="1" column="2">
+ <widget class="QToolButton" name="modsDirBrowseBtn">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelLWJGLDir">
+ <property name="text">
+ <string>LWJGL:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QToolButton" name="lwjglDirBrowseBtn">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="iconsDirTextBox"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="labelIconsDir">
+ <property name="text">
+ <string>Icons:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QToolButton" name="iconsDirBrowseBtn">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="generalTab">
+ <attribute name="title">
+ <string>User Interface</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Language (needs restart):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="languageBox"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="sortingModeBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Sorting Mode</string>
+ </property>
+ <layout class="QHBoxLayout" name="sortingModeBoxLayout">
+ <item>
+ <widget class="QRadioButton" name="sortLastLaunchedBtn">
+ <property name="text">
+ <string>By last launched</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">sortingModeGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="sortByNameBtn">
+ <property name="text">
+ <string>By name</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">sortingModeGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="editorsBox">
+ <property name="title">
+ <string>External Editors (leave empty for system default)</string>
+ </property>
+ <layout class="QGridLayout" name="foldersBoxLayout_2">
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="jsonEditorTextBox"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelJsonEditor">
+ <property name="text">
+ <string>JSON Editor:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QToolButton" name="jsonEditorBrowseBtn">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="generalTabSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="minecraftTab">
+ <attribute name="title">
+ <string>Minecraft</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="windowSizeGroupBox">
+ <property name="title">
+ <string>Window Size</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QCheckBox" name="maximizedCheckBox">
+ <property name="text">
+ <string>Start Minecraft maximized?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayoutWindowSize">
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelWindowHeight">
+ <property name="text">
+ <string>Window height:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelWindowWidth">
+ <property name="text">
+ <string>Window width:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="windowWidthSpinBox">
+ <property name="minimum">
+ <number>854</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>854</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="windowHeightSpinBox">
+ <property name="minimum">
+ <number>480</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="value">
+ <number>480</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="consoleSettingsBox">
+ <property name="title">
+ <string>Console Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="showConsoleCheck">
+ <property name="text">
+ <string>Show console while the game is running?</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoCloseConsoleCheck">
+ <property name="text">
+ <string>Automatically close console when the game quits?</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerMinecraft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="javaTab">
+ <attribute name="title">
+ <string>Java</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QGroupBox" name="memoryGroupBox">
+ <property name="title">
+ <string>Memory</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="maxMemSpinBox">
+ <property name="toolTip">
+ <string>The maximum amount of memory Minecraft is allowed to use.</string>
+ </property>
+ <property name="suffix">
+ <string> MB</string>
+ </property>
+ <property name="minimum">
+ <number>512</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>128</number>
+ </property>
+ <property name="value">
+ <number>1024</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelMinMem">
+ <property name="text">
+ <string>Minimum memory allocation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelMaxMem">
+ <property name="text">
+ <string>Maximum memory allocation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="minMemSpinBox">
+ <property name="toolTip">
+ <string>The amount of memory Minecraft is started with.</string>
+ </property>
+ <property name="suffix">
+ <string> MB</string>
+ </property>
+ <property name="minimum">
+ <number>256</number>
+ </property>
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ <property name="singleStep">
+ <number>128</number>
+ </property>
+ <property name="value">
+ <number>256</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelPermGen">
+ <property name="text">
+ <string>PermGen:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="permGenSpinBox">
+ <property name="toolTip">
+ <string>The amount of memory available to store loaded Java classes.</string>
+ </property>
+ <property name="suffix">
+ <string> MB</string>
+ </property>
+ <property name="minimum">
+ <number>64</number>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ <property name="singleStep">
+ <number>8</number>
+ </property>
+ <property name="value">
+ <number>64</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="javaSettingsGroupBox">
+ <property name="title">
+ <string>Java Settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelJavaPath">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Java path:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="javaDetectBtn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Auto-detect...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="javaTestBtn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Test</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelJVMArgs">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>JVM arguments:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="javaPathTextBox"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="javaBrowseBtn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>28</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1" colspan="2">
+ <widget class="QLineEdit" name="jvmArgsTextBox"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="customCommandsGroupBox">
+ <property name="title">
+ <string>Custom Commands</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelPostExitCmd">
+ <property name="text">
+ <string>Post-exit command:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelPreLaunchCmd">
+ <property name="text">
+ <string>Pre-launch command:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="preLaunchCmdTextBox"/>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="postExitCmdTextBox"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelCustomCmdsDescription">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="networkTab">
+ <property name="toolTip">
+ <string>Network settings.</string>
+ </property>
+ <attribute name="title">
+ <string>Network</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QGroupBox" name="proxySettingsBox">
+ <property name="title">
+ <string>Proxy</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <item>
+ <widget class="QGroupBox" name="proxyTypeBox">
+ <property name="title">
+ <string>Type</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QRadioButton" name="proxyDefaultBtn">
+ <property name="toolTip">
+ <string>Uses your system's default proxy settings.</string>
+ </property>
+ <property name="text">
+ <string>Default</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">proxyGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="proxyNoneBtn">
+ <property name="text">
+ <string>None</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">proxyGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="proxySOCKS5Btn">
+ <property name="text">
+ <string>SOCKS5</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">proxyGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="proxyHTTPBtn">
+ <property name="text">
+ <string>HTTP</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">proxyGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="proxyAddrBox">
+ <property name="title">
+ <string>Address and Port</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="proxyAddrEdit">
+ <property name="placeholderText">
+ <string>127.0.0.1</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="proxyPortEdit">
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::PlusMinus</enum>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>8080</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="proxyAuthBox">
+ <property name="title">
+ <string>Authentication</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="proxyUserEdit"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="proxyUsernameLabel">
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="proxyPasswordLabel">
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="proxyPassEdit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QLabel" name="proxyPlainTextWarningLabel">
+ <property name="text">
+ <string>Note: Proxy username and password are stored in plain text inside MultiMC's configuration file!</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>buttonBox</tabstop>
+ <tabstop>sortLastLaunchedBtn</tabstop>
+ <tabstop>sortByNameBtn</tabstop>
+ <tabstop>jsonEditorTextBox</tabstop>
+ <tabstop>jsonEditorBrowseBtn</tabstop>
+ <tabstop>maximizedCheckBox</tabstop>
+ <tabstop>windowWidthSpinBox</tabstop>
+ <tabstop>windowHeightSpinBox</tabstop>
+ <tabstop>showConsoleCheck</tabstop>
+ <tabstop>autoCloseConsoleCheck</tabstop>
+ <tabstop>minMemSpinBox</tabstop>
+ <tabstop>maxMemSpinBox</tabstop>
+ <tabstop>permGenSpinBox</tabstop>
+ <tabstop>javaPathTextBox</tabstop>
+ <tabstop>javaBrowseBtn</tabstop>
+ <tabstop>javaDetectBtn</tabstop>
+ <tabstop>javaTestBtn</tabstop>
+ <tabstop>jvmArgsTextBox</tabstop>
+ <tabstop>preLaunchCmdTextBox</tabstop>
+ <tabstop>postExitCmdTextBox</tabstop>
+ <tabstop>settingsTabs</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../../graphics.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>257</x>
+ <y>410</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>325</x>
+ <y>410</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <buttongroups>
+ <buttongroup name="proxyGroup"/>
+ <buttongroup name="sortingModeGroup"/>
+ </buttongroups>
+</ui>
diff --git a/gui/dialogs/UpdateDialog.cpp b/gui/dialogs/UpdateDialog.cpp
new file mode 100644
index 00000000..c56798b4
--- /dev/null
+++ b/gui/dialogs/UpdateDialog.cpp
@@ -0,0 +1,28 @@
+#include "UpdateDialog.h"
+#include "ui_UpdateDialog.h"
+#include "gui/Platform.h"
+
+UpdateDialog::UpdateDialog(QWidget *parent) : QDialog(parent), ui(new Ui::UpdateDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+}
+
+UpdateDialog::~UpdateDialog()
+{
+}
+
+void UpdateDialog::on_btnUpdateLater_clicked()
+{
+ reject();
+}
+
+void UpdateDialog::on_btnUpdateNow_clicked()
+{
+ done(UPDATE_NOW);
+}
+
+void UpdateDialog::on_btnUpdateOnExit_clicked()
+{
+ done(UPDATE_ONEXIT);
+}
diff --git a/gui/dialogs/UpdateDialog.h b/gui/dialogs/UpdateDialog.h
new file mode 100644
index 00000000..c13eb6bf
--- /dev/null
+++ b/gui/dialogs/UpdateDialog.h
@@ -0,0 +1,46 @@
+/* Copyright 2013 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 <QDialog>
+
+namespace Ui
+{
+class UpdateDialog;
+}
+
+enum UpdateAction
+{
+ UPDATE_LATER = QDialog::Rejected,
+ UPDATE_NOW = QDialog::Accepted,
+ UPDATE_ONEXIT = 2
+};
+
+class UpdateDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit UpdateDialog(QWidget *parent = 0);
+ ~UpdateDialog();
+
+private:
+ Ui::UpdateDialog *ui;
+public slots:
+ void on_btnUpdateNow_clicked();
+ void on_btnUpdateOnExit_clicked();
+ void on_btnUpdateLater_clicked();
+};
diff --git a/gui/dialogs/UpdateDialog.ui b/gui/dialogs/UpdateDialog.ui
new file mode 100644
index 00000000..1fe65e62
--- /dev/null
+++ b/gui/dialogs/UpdateDialog.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UpdateDialog</class>
+ <widget class="QDialog" name="UpdateDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>350</width>
+ <height>260</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MultiMC Update</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="../../graphics.qrc">
+ <normaloff>:/icons/toolbar/checkupdate</normaloff>:/icons/toolbar/checkupdate</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>A new MultiMC update is available!</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnUpdateNow">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Update now</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnUpdateOnExit">
+ <property name="text">
+ <string>Update after MultiMC closes</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnUpdateLater">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Don't update yet</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../../graphics.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/gui/dialogs/VersionSelectDialog.cpp b/gui/dialogs/VersionSelectDialog.cpp
new file mode 100644
index 00000000..0f379f56
--- /dev/null
+++ b/gui/dialogs/VersionSelectDialog.cpp
@@ -0,0 +1,116 @@
+/* Copyright 2013 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 "VersionSelectDialog.h"
+#include "ui_VersionSelectDialog.h"
+
+#include <QHeaderView>
+
+#include <QDebug>
+
+#include <gui/dialogs/ProgressDialog.h>
+#include "gui/Platform.h"
+
+#include <logic/BaseVersion.h>
+#include <logic/lists/BaseVersionList.h>
+#include <logic/tasks/Task.h>
+
+VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent,
+ bool cancelable)
+ : QDialog(parent), ui(new Ui::VersionSelectDialog)
+{
+ MultiMCPlatform::fixWM_CLASS(this);
+ ui->setupUi(this);
+ setWindowModality(Qt::WindowModal);
+ setWindowTitle(title);
+
+ m_vlist = vlist;
+
+ m_proxyModel = new QSortFilterProxyModel(this);
+ m_proxyModel->setSourceModel(vlist);
+
+ ui->listView->setModel(m_proxyModel);
+ ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+ ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
+
+ if (!cancelable)
+ {
+ ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+ }
+}
+
+void VersionSelectDialog::setEmptyString(QString emptyString)
+{
+ ui->listView->setEmptyString(emptyString);
+}
+
+VersionSelectDialog::~VersionSelectDialog()
+{
+ delete ui;
+}
+
+void VersionSelectDialog::setResizeOn(int column)
+{
+ ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents);
+ resizeOnColumn = column;
+ ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
+}
+
+int VersionSelectDialog::exec()
+{
+ QDialog::open();
+ if (!m_vlist->isLoaded())
+ loadList();
+ return QDialog::exec();
+}
+
+void VersionSelectDialog::loadList()
+{
+ ProgressDialog *taskDlg = new ProgressDialog(this);
+ Task *loadTask = m_vlist->getLoadTask();
+ loadTask->setParent(taskDlg);
+ taskDlg->exec(loadTask);
+}
+
+BaseVersionPtr VersionSelectDialog::selectedVersion() const
+{
+ auto currentIndex = ui->listView->selectionModel()->currentIndex();
+ auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole);
+ return variant.value<BaseVersionPtr>();
+}
+
+void VersionSelectDialog::on_refreshButton_clicked()
+{
+ loadList();
+}
+
+void VersionSelectDialog::setFilter(int column, QString filter)
+{
+ m_proxyModel->setFilterKeyColumn(column);
+ m_proxyModel->setFilterFixedString(filter);
+ /*
+ QStringList filteredTypes;
+ if (!ui->filterSnapshotsCheckbox->isChecked())
+ filteredTypes += "Snapshot";
+ if (!ui->filterMCNostalgiaCheckbox->isChecked())
+ filteredTypes += "Nostalgia";
+
+ QString regexStr = "^.*$";
+ if (filteredTypes.length() > 0)
+ regexStr = QString("^((?!%1).)*$").arg(filteredTypes.join('|'));
+
+ QLOG_DEBUG() << "Filter:" << regexStr;
+ */
+}
diff --git a/gui/dialogs/VersionSelectDialog.h b/gui/dialogs/VersionSelectDialog.h
new file mode 100644
index 00000000..61fa8ab6
--- /dev/null
+++ b/gui/dialogs/VersionSelectDialog.h
@@ -0,0 +1,62 @@
+/* Copyright 2013 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 <QDialog>
+#include <QSortFilterProxyModel>
+
+#include "logic/BaseVersion.h"
+
+class BaseVersionList;
+
+namespace Ui
+{
+class VersionSelectDialog;
+}
+
+class VersionSelectDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent = 0,
+ bool cancelable = true);
+ ~VersionSelectDialog();
+
+ virtual int exec();
+
+ //! Starts a task that loads the list.
+ void loadList();
+
+ BaseVersionPtr selectedVersion() const;
+
+ void setFilter(int column, QString filter);
+ void setEmptyString(QString emptyString);
+ void setResizeOn(int column);
+
+private
+slots:
+ void on_refreshButton_clicked();
+
+private:
+ Ui::VersionSelectDialog *ui;
+
+ BaseVersionList *m_vlist;
+
+ QSortFilterProxyModel *m_proxyModel;
+
+ int resizeOnColumn = 0;
+};
diff --git a/gui/dialogs/VersionSelectDialog.ui b/gui/dialogs/VersionSelectDialog.ui
new file mode 100644
index 00000000..07e9e73e
--- /dev/null
+++ b/gui/dialogs/VersionSelectDialog.ui
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VersionSelectDialog</class>
+ <widget class="QDialog" name="VersionSelectDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Choose Version</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="VersionListView" name="listView">
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <attribute name="headerCascadingSectionResizes">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="headerStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="refreshButton">
+ <property name="toolTip">
+ <string>Reloads the version list.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Refresh</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>VersionListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/VersionListView.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>VersionSelectDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>257</x>
+ <y>290</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>VersionSelectDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>325</x>
+ <y>290</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/gui/widgets/Common.cpp b/gui/widgets/Common.cpp
new file mode 100644
index 00000000..9b730d6c
--- /dev/null
+++ b/gui/widgets/Common.cpp
@@ -0,0 +1,27 @@
+#include "Common.h"
+
+// Origin: Qt
+QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed)
+{
+ QStringList lines;
+ height = 0;
+ widthUsed = 0;
+ textLayout.beginLayout();
+ QString str = textLayout.text();
+ while (true)
+ {
+ QTextLine line = textLayout.createLine();
+ if (!line.isValid())
+ break;
+ if (line.textLength() == 0)
+ break;
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ lines.append(str.mid(line.textStart(), line.textLength()));
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+ return lines;
+}
diff --git a/gui/widgets/Common.h b/gui/widgets/Common.h
new file mode 100644
index 00000000..fc46e08f
--- /dev/null
+++ b/gui/widgets/Common.h
@@ -0,0 +1,6 @@
+#pragma once
+#include <QStringList>
+#include <QTextLayout>
+
+QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed); \ No newline at end of file
diff --git a/gui/widgets/InstanceDelegate.cpp b/gui/widgets/InstanceDelegate.cpp
new file mode 100644
index 00000000..33da7130
--- /dev/null
+++ b/gui/widgets/InstanceDelegate.cpp
@@ -0,0 +1,231 @@
+/* Copyright 2013 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 "InstanceDelegate.h"
+#include <QPainter>
+#include <QTextOption>
+#include <QTextLayout>
+#include <QApplication>
+#include <QtCore/qmath.h>
+#include "Common.h"
+#define QFIXED_MAX (INT_MAX / 256)
+
+ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
+{
+}
+
+void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option,
+ const QRect &rect)
+{
+ if ((option.state & QStyle::State_Selected))
+ painter->fillRect(rect, option.palette.brush(QPalette::Highlight));
+ else
+ {
+ QColor backgroundColor = option.palette.color(QPalette::Background);
+ backgroundColor.setAlpha(160);
+ painter->fillRect(rect, QBrush(backgroundColor));
+ }
+}
+
+void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
+{
+ if (!(option.state & QStyle::State_HasFocus))
+ return;
+ QStyleOptionFocusRect opt;
+ opt.direction = option.direction;
+ opt.fontMetrics = option.fontMetrics;
+ opt.palette = option.palette;
+ opt.rect = rect;
+ // opt.state = option.state | QStyle::State_KeyboardFocusChange |
+ // QStyle::State_Item;
+ auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base;
+ opt.backgroundColor = option.palette.color(col);
+ // Apparently some widget styles expect this hint to not be set
+ painter->setRenderHint(QPainter::Antialiasing, false);
+
+ QStyle *style = option.widget ? option.widget->style() : QApplication::style();
+
+ style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget);
+
+ painter->setRenderHint(QPainter::Antialiasing);
+}
+
+static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option)
+{
+ QStyle *style = option->widget ? option->widget->style() : QApplication::style();
+ QTextOption textOption;
+ textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ QTextLayout textLayout;
+ textLayout.setTextOption(textOption);
+ textLayout.setFont(option->font);
+ textLayout.setText(option->text);
+ const int textMargin =
+ style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1;
+ QRect bounds(0, 0, 100 - 2 * textMargin, 600);
+ qreal height = 0, widthUsed = 0;
+ viewItemTextLayout(textLayout, bounds.width(), height, widthUsed);
+ const QSize size(qCeil(widthUsed), qCeil(height));
+ return QSize(size.width() + 2 * textMargin, size.height());
+}
+
+void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionViewItemV4 opt = option;
+ initStyleOption(&opt, index);
+ painter->save();
+ painter->setClipRect(opt.rect);
+
+ opt.features |= QStyleOptionViewItem::WrapText;
+ opt.text = index.data().toString();
+ opt.textElideMode = Qt::ElideRight;
+ opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter;
+
+ QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
+
+ // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize);
+ const int iconSize = 48;
+ QRect iconbox = opt.rect;
+ const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1;
+ QRect textRect = opt.rect;
+ QRect textHighlightRect = textRect;
+ // clip the decoration on top, remove width padding
+ textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0);
+
+ textHighlightRect.adjust(0, iconSize + 5, 0, 0);
+
+ // draw background
+ {
+ // FIXME: unused
+ // QSize textSize = viewItemTextSize ( &opt );
+ QPalette::ColorGroup cg;
+ QStyleOptionViewItemV4 opt2(opt);
+
+ if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled))
+ {
+ if (!(opt.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ else
+ cg = QPalette::Normal;
+ }
+ else
+ {
+ cg = QPalette::Disabled;
+ }
+ opt2.palette.setCurrentColorGroup(cg);
+
+ // fill in background, if any
+ if (opt.backgroundBrush.style() != Qt::NoBrush)
+ {
+ QPointF oldBO = painter->brushOrigin();
+ painter->setBrushOrigin(opt.rect.topLeft());
+ painter->fillRect(opt.rect, opt.backgroundBrush);
+ painter->setBrushOrigin(oldBO);
+ }
+
+ if (opt.showDecorationSelected)
+ {
+ drawSelectionRect(painter, opt2, opt.rect);
+ drawFocusRect(painter, opt2, opt.rect);
+ // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) );
+ }
+ else
+ {
+
+ // if ( opt.state & QStyle::State_Selected )
+ {
+ // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt,
+ // opt.widget );
+ // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg,
+ // QPalette::Highlight ) );
+ drawSelectionRect(painter, opt2, textHighlightRect);
+ drawFocusRect(painter, opt2, textHighlightRect);
+ }
+ }
+ }
+
+ // draw the icon
+ {
+ QIcon::Mode mode = QIcon::Normal;
+ if (!(opt.state & QStyle::State_Enabled))
+ mode = QIcon::Disabled;
+ else if (opt.state & QStyle::State_Selected)
+ mode = QIcon::Selected;
+ QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
+
+ iconbox.setHeight(iconSize);
+ opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state);
+ }
+ // set the text colors
+ QPalette::ColorGroup cg =
+ opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ if (opt.state & QStyle::State_Selected)
+ {
+ painter->setPen(opt.palette.color(cg, QPalette::HighlightedText));
+ }
+ else
+ {
+ painter->setPen(opt.palette.color(cg, QPalette::Text));
+ }
+
+ // draw the text
+ QTextOption textOption;
+ textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ textOption.setTextDirection(opt.direction);
+ textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment));
+ QTextLayout textLayout;
+ textLayout.setTextOption(textOption);
+ textLayout.setFont(opt.font);
+ textLayout.setText(opt.text);
+
+ qreal width, height;
+ viewItemTextLayout(textLayout, textRect.width(), height, width);
+
+ const int lineCount = textLayout.lineCount();
+
+ const QRect layoutRect = QStyle::alignedRect(
+ opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect);
+ const QPointF position = layoutRect.topLeft();
+ for (int i = 0; i < lineCount; ++i)
+ {
+ const QTextLine line = textLayout.lineAt(i);
+ line.draw(painter, position);
+ }
+
+ painter->restore();
+}
+
+QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionViewItemV4 opt = option;
+ initStyleOption(&opt, index);
+ opt.features |= QStyleOptionViewItem::WrapText;
+ opt.text = index.data().toString();
+ opt.textElideMode = Qt::ElideRight;
+ opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter;
+
+ QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
+ const int textMargin =
+ style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1;
+ int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables
+ QSize szz = viewItemTextSize(&opt);
+ height += szz.height();
+ // FIXME: maybe the icon items could scale and keep proportions?
+ QSize sz(100, height);
+ return sz;
+}
diff --git a/gui/widgets/InstanceDelegate.h b/gui/widgets/InstanceDelegate.h
new file mode 100644
index 00000000..6f924405
--- /dev/null
+++ b/gui/widgets/InstanceDelegate.h
@@ -0,0 +1,27 @@
+/* Copyright 2013 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 <QStyledItemDelegate>
+
+class ListViewDelegate : public QStyledItemDelegate
+{
+public:
+ explicit ListViewDelegate ( QObject* parent = 0 );
+protected:
+ void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
+ QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
+};
diff --git a/gui/widgets/LabeledToolButton.cpp b/gui/widgets/LabeledToolButton.cpp
new file mode 100644
index 00000000..fe3cac45
--- /dev/null
+++ b/gui/widgets/LabeledToolButton.cpp
@@ -0,0 +1,86 @@
+/* Copyright 2013 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 <QLabel>
+#include <QVBoxLayout>
+#include <QResizeEvent>
+#include <QStyleOption>
+#include "LabeledToolButton.h"
+#include <QApplication>
+
+/*
+ *
+ * Tool Button with a label on it, instead of the normal text rendering
+ *
+ */
+
+LabeledToolButton::LabeledToolButton(QWidget * parent)
+ : QToolButton(parent)
+ , m_label(new QLabel(this))
+{
+ //QToolButton::setText(" ");
+ m_label->setWordWrap(true);
+ m_label->setMouseTracking(false);
+ m_label->setAlignment(Qt::AlignCenter);
+ m_label->setTextInteractionFlags(Qt::NoTextInteraction);
+ // somehow, this makes word wrap work in the QLabel. yay.
+ m_label->setMinimumWidth(100);
+}
+
+QString LabeledToolButton::text() const
+{
+ return m_label->text();
+}
+
+void LabeledToolButton::setText(const QString & text)
+{
+ m_label->setText(text);
+}
+
+/*!
+ \reimp
+*/
+QSize LabeledToolButton::sizeHint() const
+{
+ /*
+ Q_D(const QToolButton);
+ if (d->sizeHint.isValid())
+ return d->sizeHint;
+ */
+ ensurePolished();
+
+ int w = 0, h = 0;
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ QSize sz =m_label->sizeHint();
+ w = sz.width();
+ h = sz.height();
+
+ opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
+ if (popupMode() == MenuButtonPopup)
+ w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
+
+ QSize rawSize = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this);
+ QSize sizeHint = rawSize.expandedTo(QApplication::globalStrut());
+ return sizeHint;
+}
+
+
+
+void LabeledToolButton::resizeEvent(QResizeEvent * event)
+{
+ m_label->setGeometry(QRect(4, 4, width()-8, height()-8));
+ QWidget::resizeEvent(event);
+}
diff --git a/gui/widgets/LabeledToolButton.h b/gui/widgets/LabeledToolButton.h
new file mode 100644
index 00000000..b417f814
--- /dev/null
+++ b/gui/widgets/LabeledToolButton.h
@@ -0,0 +1,37 @@
+/* Copyright 2013 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 <QPushButton>
+#include <QToolButton>
+
+class QLabel;
+
+class LabeledToolButton : public QToolButton
+{
+ Q_OBJECT
+
+ QLabel * m_label;
+
+public:
+ LabeledToolButton(QWidget * parent = 0);
+
+ QString text() const;
+ void setText(const QString & text);
+ virtual QSize sizeHint() const;
+protected:
+ void resizeEvent(QResizeEvent * event);
+};
diff --git a/gui/widgets/MCModInfoFrame.cpp b/gui/widgets/MCModInfoFrame.cpp
new file mode 100644
index 00000000..abcea6c6
--- /dev/null
+++ b/gui/widgets/MCModInfoFrame.cpp
@@ -0,0 +1,111 @@
+/* Copyright 2013 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 <QMessageBox>
+#include <QtGui>
+
+#include "MCModInfoFrame.h"
+#include "ui_MCModInfoFrame.h"
+#include "gui/dialogs/CustomMessageBox.h"
+
+void MCModInfoFrame::updateWithMod(Mod &m)
+{
+ if(m.type() == m.MOD_FOLDER)
+ {
+ clear();
+ return;
+ }
+
+ QString text = "";
+ QString name = "";
+ if(m.name().isEmpty()) name = m.mmc_id();
+ else name = m.name();
+
+ if(m.homeurl().isEmpty()) text = name;
+ else text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>";
+ if(!m.authors().isEmpty()) text += " by " + m.authors();
+
+ setModText(text);
+
+ if(m.description().isEmpty())
+ {
+ setModDescription(tr("No description provided in mcmod.info"));
+ }
+ else
+ {
+ setModDescription(m.description());
+ }
+}
+
+void MCModInfoFrame::clear()
+{
+ setModText(tr("Select a mod to view title and authors..."));
+ setModDescription(tr("Select a mod to view description..."));
+}
+
+MCModInfoFrame::MCModInfoFrame(QWidget *parent) :
+ QFrame(parent),
+ ui(new Ui::MCModInfoFrame)
+{
+ ui->setupUi(this);
+}
+
+MCModInfoFrame::~MCModInfoFrame()
+{
+ delete ui;
+}
+
+void MCModInfoFrame::setModText(QString text)
+{
+ ui->label_ModText->setText(text);
+}
+
+void MCModInfoFrame::setModDescription(QString text)
+{
+ ui->label_ModDescription->setToolTip("");
+ QString intermediatetext = text.trimmed();
+ bool prev(false);
+ QChar rem('\n');
+ QString finaltext;
+ finaltext.reserve(intermediatetext.size());
+ foreach(const QChar& c, intermediatetext)
+ {
+ if(c == rem && prev){
+ continue;
+ }
+ prev = c == rem;
+ finaltext += c;
+ }
+ QString labeltext;
+ labeltext.reserve(300);
+ if(finaltext.length() > 290)
+ {
+ ui->label_ModDescription->setOpenExternalLinks(false);
+ ui->label_ModDescription->setTextFormat(Qt::TextFormat::RichText);
+ desc = text;
+ labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
+ QObject::connect(ui->label_ModDescription, &QLabel::linkActivated, this, &MCModInfoFrame::modDescEllipsisHandler);
+ }
+ else
+ {
+ ui->label_ModDescription->setTextFormat(Qt::TextFormat::PlainText);
+ labeltext.append(finaltext);
+ }
+ ui->label_ModDescription->setText(labeltext);
+}
+void MCModInfoFrame::modDescEllipsisHandler(const QString &link)
+{
+ CustomMessageBox::selectable(this, tr(""), desc)->show();
+}
diff --git a/gui/widgets/MCModInfoFrame.h b/gui/widgets/MCModInfoFrame.h
new file mode 100644
index 00000000..54c5d674
--- /dev/null
+++ b/gui/widgets/MCModInfoFrame.h
@@ -0,0 +1,46 @@
+/* Copyright 2013 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 <QFrame>
+#include "logic/Mod.h"
+
+namespace Ui
+{
+class MCModInfoFrame;
+}
+
+class MCModInfoFrame : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit MCModInfoFrame(QWidget *parent = 0);
+ ~MCModInfoFrame();
+
+ void setModText(QString text);
+ void setModDescription(QString text);
+
+ void updateWithMod(Mod &m);
+ void clear();
+
+public slots:
+ void modDescEllipsisHandler(const QString& link );
+
+private:
+ Ui::MCModInfoFrame *ui;
+ QString desc;
+};
diff --git a/gui/widgets/MCModInfoFrame.ui b/gui/widgets/MCModInfoFrame.ui
new file mode 100644
index 00000000..60e0a65c
--- /dev/null
+++ b/gui/widgets/MCModInfoFrame.ui
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MCModInfoFrame</class>
+ <widget class="QFrame" name="MCModInfoFrame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>527</width>
+ <height>113</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_ModText">
+ <property name="text">
+ <string>Select a mod to view title and authors...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_ModDescription">
+ <property name="text">
+ <string>Select a mod to view description...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/gui/widgets/ModListView.cpp b/gui/widgets/ModListView.cpp
new file mode 100644
index 00000000..358e6331
--- /dev/null
+++ b/gui/widgets/ModListView.cpp
@@ -0,0 +1,62 @@
+/* Copyright 2013 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 "ModListView.h"
+#include <QHeaderView>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QDrag>
+#include <QRect>
+
+ModListView::ModListView ( QWidget* parent )
+ :QTreeView ( parent )
+{
+ setAllColumnsShowFocus ( true );
+ setExpandsOnDoubleClick ( false );
+ setRootIsDecorated ( false );
+ setSortingEnabled ( false );
+ setAlternatingRowColors ( true );
+ setSelectionMode ( QAbstractItemView::ContiguousSelection );
+ setHeaderHidden ( false );
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn );
+ setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
+ setDropIndicatorShown(true);
+ setDragEnabled(true);
+ setDragDropMode(QAbstractItemView::DropOnly);
+ viewport()->setAcceptDrops(true);
+}
+
+void ModListView::setModel ( QAbstractItemModel* model )
+{
+ QTreeView::setModel ( model );
+ auto head = header();
+ head->setStretchLastSection(false);
+ // HACK: this is true for the checkbox column of mod lists
+ auto string = model->headerData(0,head->orientation()).toString();
+ if(!string.size())
+ {
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+ else
+ {
+ head->setSectionResizeMode(0, QHeaderView::Stretch);
+ for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+}
diff --git a/gui/widgets/ModListView.h b/gui/widgets/ModListView.h
new file mode 100644
index 00000000..b26fa26b
--- /dev/null
+++ b/gui/widgets/ModListView.h
@@ -0,0 +1,27 @@
+/* Copyright 2013 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 <QTreeView>
+
+class Mod;
+
+class ModListView: public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit ModListView ( QWidget* parent = 0 );
+ virtual void setModel ( QAbstractItemModel* model );
+};
diff --git a/gui/widgets/VersionListView.cpp b/gui/widgets/VersionListView.cpp
new file mode 100644
index 00000000..b7f45f27
--- /dev/null
+++ b/gui/widgets/VersionListView.cpp
@@ -0,0 +1,150 @@
+/* Copyright 2013 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 <QHeaderView>
+#include <QApplication>
+#include <QMouseEvent>
+#include <QDrag>
+#include <QPainter>
+#include "VersionListView.h"
+#include "Common.h"
+
+VersionListView::VersionListView(QWidget *parent)
+ :QTreeView ( parent )
+{
+ m_emptyString = tr("No versions are currently available.");
+}
+
+void VersionListView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ if(!m_itemCount)
+ viewport()->update();
+ m_itemCount += end-start+1;
+ QTreeView::rowsInserted(parent, start, end);
+}
+
+
+void VersionListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ m_itemCount -= end-start+1;
+ if(!m_itemCount)
+ viewport()->update();
+ QTreeView::rowsInserted(parent, start, end);
+}
+
+void VersionListView::setModel(QAbstractItemModel *model)
+{
+ m_itemCount = model->rowCount();
+ if(!m_itemCount)
+ viewport()->update();
+ QTreeView::setModel(model);
+}
+
+void VersionListView::reset()
+{
+ if(model())
+ {
+ m_itemCount = model()->rowCount();
+ }
+ viewport()->update();
+ QTreeView::reset();
+}
+
+void VersionListView::setEmptyString(QString emptyString)
+{
+ m_emptyString = emptyString;
+ if(!m_itemCount)
+ {
+ viewport()->update();
+ }
+}
+
+void VersionListView::paintEvent(QPaintEvent *event)
+{
+ if(m_itemCount)
+ {
+ QTreeView::paintEvent(event);
+ }
+ else
+ {
+ paintInfoLabel(event);
+ }
+}
+
+void VersionListView::paintInfoLabel(QPaintEvent *event)
+{
+ int scrollInterval = 500;
+
+ //calculate the rect for the overlay
+ QPainter painter(viewport());
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ const QChar letter = 'Q';
+ QFont font("sans", 20);
+ font.setBold(true);
+
+ QRect bounds = viewport()->geometry();
+ bounds.moveTop(0);
+ QTextLayout layout(m_emptyString, font);
+ qreal height = 0.0;
+ qreal widthUsed = 0.0;
+ QStringList lines = viewItemTextLayout(layout, bounds.width() - 20, height, widthUsed);
+ QRect rect (0,0, widthUsed, height);
+ rect.setWidth(rect.width()+20);
+ rect.setHeight(rect.height()+20);
+ rect.moveCenter(bounds.center());
+ //check if we are allowed to draw in our area
+ if (!event->rect().intersects(rect)) {
+ return;
+ }
+ //draw the letter of the topmost item semitransparent in the middle
+ QColor background = QApplication::palette().color(QPalette::Foreground);
+ QColor foreground = QApplication::palette().color(QPalette::Base);
+ /*
+ background.setAlpha(128 - scrollFade);
+ foreground.setAlpha(128 - scrollFade);
+ */
+ painter.setBrush(QBrush(background));
+ painter.setPen(foreground);
+ painter.drawRoundedRect(rect, 5.0, 5.0);
+ foreground.setAlpha(190);
+ painter.setPen(foreground);
+ painter.setFont(font);
+ painter.drawText(rect, Qt::AlignCenter, lines.join("\n"));
+
+}
+
+/*
+void ModListView::setModel ( QAbstractItemModel* model )
+{
+ QTreeView::setModel ( model );
+ auto head = header();
+ head->setStretchLastSection(false);
+ // HACK: this is true for the checkbox column of mod lists
+ auto string = model->headerData(0,head->orientation()).toString();
+ if(!string.size())
+ {
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+ else
+ {
+ head->setSectionResizeMode(0, QHeaderView::Stretch);
+ for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+}
+*/ \ No newline at end of file
diff --git a/gui/widgets/VersionListView.h b/gui/widgets/VersionListView.h
new file mode 100644
index 00000000..af9b1f6a
--- /dev/null
+++ b/gui/widgets/VersionListView.h
@@ -0,0 +1,43 @@
+/* Copyright 2013 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 <QTreeView>
+
+class Mod;
+
+class VersionListView : public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit VersionListView(QWidget *parent = 0);
+ virtual void paintEvent(QPaintEvent *event) override;
+ void setEmptyString(QString emptyString);
+ virtual void setModel ( QAbstractItemModel* model );
+
+public slots:
+ virtual void reset() override;
+
+protected slots:
+ virtual void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) override;
+ virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
+
+private: /* methods */
+ void paintInfoLabel(QPaintEvent *event);
+
+private: /* variables */
+ int m_itemCount = 0;
+ QString m_emptyString;
+};
diff --git a/install_prereqs.cmake.in b/install_prereqs.cmake.in
new file mode 100644
index 00000000..3ae8302d
--- /dev/null
+++ b/install_prereqs.cmake.in
@@ -0,0 +1,19 @@
+FILE(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
+function(gp_resolved_file_type_override resolved_file type_var)
+ if(resolved_file MATCHES "^/usr/lib/libQt")
+ message("resolving ${resolved_file} as other")
+ set(${type_var} other PARENT_SCOPE)
+ elseif(resolved_file MATCHES "^/usr/lib(.+)?/libxcb")
+ message("resolving ${resolved_file} as other")
+ set(${type_var} other PARENT_SCOPE)
+ endif()
+endfunction()
+
+set(gp_tool "@CMAKE_GP_TOOL@")
+set(gp_cmd_paths ${gp_cmd_paths}
+ "@CMAKE_GP_CMD_PATHS@"
+)
+
+include(BundleUtilities)
+fixup_bundle("@APPS@" "${QTPLUGINS}" "@DIRS@")
+
diff --git a/logger/QsDebugOutput.cpp b/logger/QsDebugOutput.cpp
new file mode 100644
index 00000000..d68cd5e9
--- /dev/null
+++ b/logger/QsDebugOutput.cpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsDebugOutput.h"
+#include <QString>
+#include <QtGlobal>
+
+#if defined(Q_OS_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+void QsDebugOutput::output(const QString &message)
+{
+ OutputDebugStringW(reinterpret_cast<const WCHAR *>(message.utf16()));
+ OutputDebugStringW(L"\n");
+}
+#elif defined(Q_OS_SYMBIAN)
+#include <e32debug.h>
+void QsDebugOutput::output(const QString &message)
+{
+ TPtrC8 symbianMessage(reinterpret_cast<const TUint8 *>(qPrintable(message)));
+ RDebug::RawPrint(symbianMessage);
+}
+#elif defined(Q_OS_UNIX)
+#include <cstdio>
+void QsDebugOutput::output(const QString &message)
+{
+ fprintf(stderr, "%s\n", qPrintable(message));
+ fflush(stderr);
+}
+#endif
diff --git a/logger/QsDebugOutput.h b/logger/QsDebugOutput.h
new file mode 100644
index 00000000..8c759a6d
--- /dev/null
+++ b/logger/QsDebugOutput.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+class QString;
+
+class QsDebugOutput
+{
+public:
+ static void output(const QString &a_message);
+};
diff --git a/logger/QsLog.cpp b/logger/QsLog.cpp
new file mode 100644
index 00000000..87d7a412
--- /dev/null
+++ b/logger/QsLog.cpp
@@ -0,0 +1,142 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLog.h"
+#include "QsLogDest.h"
+#include <QMutex>
+#include <QList>
+#include <QDateTime>
+#include <QtGlobal>
+#include <cassert>
+#include <cstdlib>
+#include <stdexcept>
+
+namespace QsLogging
+{
+typedef QList<Destination *> DestinationList;
+
+static const char *LevelStrings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
+ "UNKNOWN"};
+
+// not using Qt::ISODate because we need the milliseconds too
+static const QString fmtDateTime("hhhh:mm:ss.zzz");
+
+static const char *LevelToText(Level theLevel)
+{
+ if (theLevel > FatalLevel)
+ {
+ assert(!"bad log level");
+ return LevelStrings[UnknownLevel];
+ }
+ return LevelStrings[theLevel];
+}
+
+class LoggerImpl
+{
+public:
+ LoggerImpl() : level(InfoLevel)
+ {
+ }
+ QMutex logMutex;
+ Level level;
+ DestinationList destList;
+};
+
+Logger::Logger() : d(new LoggerImpl)
+{
+}
+
+Logger::~Logger()
+{
+ delete d;
+}
+
+void Logger::addDestination(Destination *destination)
+{
+ assert(destination);
+ d->destList.push_back(destination);
+}
+
+void Logger::setLoggingLevel(Level newLevel)
+{
+ d->level = newLevel;
+}
+
+Level Logger::loggingLevel() const
+{
+ return d->level;
+}
+
+//! creates the complete log message and passes it to the logger
+void Logger::Helper::writeToLog()
+{
+ const char *const levelName = LevelToText(level);
+ const QString completeMessage(QString("%1\t%2").arg(levelName, 5).arg(buffer));
+
+ Logger &logger = Logger::instance();
+ QMutexLocker lock(&logger.d->logMutex);
+ logger.write(completeMessage);
+}
+
+Logger::Helper::Helper(Level logLevel) : level(logLevel), qtDebug(&buffer)
+{
+}
+
+Logger::Helper::~Helper()
+{
+ try
+ {
+ writeToLog();
+ }
+ catch (std::exception &e)
+ {
+ // you shouldn't throw exceptions from a sink
+ Q_UNUSED(e);
+ assert(!"exception in logger helper destructor");
+ throw;
+ }
+}
+
+//! sends the message to all the destinations
+void Logger::write(const QString &message)
+{
+ for (DestinationList::iterator it = d->destList.begin(), endIt = d->destList.end();
+ it != endIt; ++it)
+ {
+ if (!(*it))
+ {
+ assert(!"null log destination");
+ continue;
+ }
+ (*it)->write(message);
+ }
+}
+
+void Logger::removeDestination(Destination* destination)
+{
+ d->destList.removeAll(destination);
+}
+
+} // end namespace
diff --git a/logger/QsLog.h b/logger/QsLog.h
new file mode 100644
index 00000000..6c96423c
--- /dev/null
+++ b/logger/QsLog.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <QDebug>
+#include <QString>
+
+namespace QsLogging
+{
+class Destination;
+enum Level
+{
+ TraceLevel = 0,
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ UnknownLevel
+};
+
+class LoggerImpl; // d pointer
+class Logger
+{
+public:
+ static Logger &instance()
+ {
+ static Logger staticLog;
+ return staticLog;
+ }
+
+ //! Adds a log message destination. Don't add null destinations.
+ void addDestination(Destination *destination);
+ //! Removes the given destination from the logger.
+ void removeDestination(Destination* destination);
+ //! Logging at a level < 'newLevel' will be ignored
+ void setLoggingLevel(Level newLevel);
+ //! The default level is INFO
+ Level loggingLevel() const;
+
+ //! The helper forwards the streaming to QDebug and builds the final
+ //! log message.
+ class Helper
+ {
+ public:
+ explicit Helper(Level logLevel);
+ ~Helper();
+ QDebug &stream()
+ {
+ return qtDebug;
+ }
+
+ private:
+ void writeToLog();
+
+ Level level;
+ QString buffer;
+ QDebug qtDebug;
+ };
+
+private:
+ Logger();
+ Logger(const Logger &);
+ Logger &operator=(const Logger &);
+ ~Logger();
+
+ void write(const QString &message);
+
+ LoggerImpl *d;
+};
+
+} // end namespace
+
+#define QLOG_TRACE() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::TraceLevel) \
+ QsLogging::Logger::Helper(QsLogging::TraceLevel).stream()
+#define QLOG_DEBUG() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::DebugLevel) \
+ QsLogging::Logger::Helper(QsLogging::DebugLevel).stream()
+#define QLOG_INFO() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::InfoLevel) \
+ QsLogging::Logger::Helper(QsLogging::InfoLevel).stream()
+#define QLOG_WARN() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::WarnLevel) \
+ QsLogging::Logger::Helper(QsLogging::WarnLevel).stream()
+#define QLOG_ERROR() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::ErrorLevel) \
+ QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream()
+#define QLOG_FATAL() QsLogging::Logger::Helper(QsLogging::FatalLevel).stream()
+
+/*
+#define QLOG_TRACE() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::TraceLevel) \
+ QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_DEBUG() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::DebugLevel) \
+ QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_INFO() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::InfoLevel) \
+ QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_WARN() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::WarnLevel) \
+ QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_ERROR() \
+ if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::ErrorLevel) \
+ QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_FATAL() \
+ QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() << __FILE__ << '@' << __LINE__
+*/
diff --git a/logger/QsLogDest.cpp b/logger/QsLogDest.cpp
new file mode 100644
index 00000000..4a47060e
--- /dev/null
+++ b/logger/QsLogDest.cpp
@@ -0,0 +1,104 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLogDest.h"
+#include "QsDebugOutput.h"
+#include "QsLog.h"
+#include <QFile>
+#include <QTextStream>
+#include <QString>
+
+namespace QsLogging
+{
+
+Destination::~Destination()
+{
+ Logger::instance().removeDestination(this);
+ QsDebugOutput::output("Removed logger destination.");
+}
+
+//! file message sink
+class FileDestination : public Destination
+{
+public:
+ FileDestination(const QString &filePath);
+ virtual void write(const QString &message);
+
+private:
+ QFile mFile;
+ QTextStream mOutputStream;
+};
+
+FileDestination::FileDestination(const QString &filePath)
+{
+ mFile.setFileName(filePath);
+ mFile.open(QFile::WriteOnly | QFile::Text |
+ QFile::Truncate); // fixme: should throw on failure
+ mOutputStream.setDevice(&mFile);
+}
+
+void FileDestination::write(const QString &message)
+{
+ mOutputStream << message << endl;
+ mOutputStream.flush();
+}
+
+//! debugger sink
+class DebugOutputDestination : public Destination
+{
+public:
+ virtual void write(const QString &message);
+};
+
+void DebugOutputDestination::write(const QString &message)
+{
+ QsDebugOutput::output(message);
+}
+
+class QDebugDestination : public Destination
+{
+public:
+ virtual void write(const QString &message)
+ {
+ qDebug() << message;
+ };
+};
+
+DestinationPtr DestinationFactory::MakeFileDestination(const QString &filePath)
+{
+ return DestinationPtr(new FileDestination(filePath));
+}
+
+DestinationPtr DestinationFactory::MakeDebugOutputDestination()
+{
+ return DestinationPtr(new DebugOutputDestination);
+}
+
+DestinationPtr DestinationFactory::MakeQDebugDestination()
+{
+ return DestinationPtr(new QDebugDestination);
+}
+
+} // end namespace
diff --git a/logger/QsLogDest.h b/logger/QsLogDest.h
new file mode 100644
index 00000000..a8000022
--- /dev/null
+++ b/logger/QsLogDest.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <memory>
+
+class QString;
+
+namespace QsLogging
+{
+
+class Destination
+{
+public:
+ virtual ~Destination();
+ virtual void write(const QString &message) = 0;
+};
+typedef std::shared_ptr<Destination> DestinationPtr;
+
+//! Creates logging destinations/sinks. The caller will have ownership of
+//! the newly created destinations.
+class DestinationFactory
+{
+public:
+ static DestinationPtr MakeFileDestination(const QString &filePath);
+ static DestinationPtr MakeDebugOutputDestination();
+ static DestinationPtr MakeQDebugDestination();
+};
+
+} // end namespace
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
new file mode 100644
index 00000000..222004a3
--- /dev/null
+++ b/logic/BaseInstance.cpp
@@ -0,0 +1,268 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "BaseInstance.h"
+#include "BaseInstance_p.h"
+
+#include <QFileInfo>
+#include <QDir>
+#include "MultiMC.h"
+
+#include "inisettingsobject.h"
+#include "setting.h"
+#include "overridesetting.h"
+
+#include "pathutils.h"
+#include <cmdutils.h>
+#include "lists/MinecraftVersionList.h"
+#include "logic/icons/IconList.h"
+
+BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
+ SettingsObject *settings_obj, QObject *parent)
+ : QObject(parent), inst_d(d_in)
+{
+ I_D(BaseInstance);
+ d->m_settings = settings_obj;
+ d->m_rootDir = rootDir;
+
+ settings().registerSetting("name", "Unnamed Instance");
+ settings().registerSetting("iconKey", "default");
+ connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString)));
+ settings().registerSetting("notes", "");
+ settings().registerSetting("lastLaunchTime", 0);
+
+ /*
+ * custom base jar has no default. it is determined in code... see the accessor methods for
+ *it
+ *
+ * for instances that DO NOT have the CustomBaseJar setting (legacy instances),
+ * [.]minecraft/bin/mcbackup.jar is the default base jar
+ */
+ settings().registerSetting("UseCustomBaseJar", true);
+ settings().registerSetting("CustomBaseJar", "");
+
+ auto globalSettings = MMC->settings();
+
+ // Java Settings
+ settings().registerSetting("OverrideJava", false);
+ settings().registerOverride(globalSettings->getSetting("JavaPath"));
+ settings().registerOverride(globalSettings->getSetting("JvmArgs"));
+
+ // Custom Commands
+ settings().registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
+ settings().registerOverride(globalSettings->getSetting("PreLaunchCommand"));
+ settings().registerOverride(globalSettings->getSetting("PostExitCommand"));
+
+ // Window Size
+ settings().registerSetting("OverrideWindow", false);
+ settings().registerOverride(globalSettings->getSetting("LaunchMaximized"));
+ settings().registerOverride(globalSettings->getSetting("MinecraftWinWidth"));
+ settings().registerOverride(globalSettings->getSetting("MinecraftWinHeight"));
+
+ // Memory
+ settings().registerSetting("OverrideMemory", false);
+ settings().registerOverride(globalSettings->getSetting("MinMemAlloc"));
+ settings().registerOverride(globalSettings->getSetting("MaxMemAlloc"));
+ settings().registerOverride(globalSettings->getSetting("PermGen"));
+
+ // Console
+ settings().registerSetting("OverrideConsole", false);
+ settings().registerOverride(globalSettings->getSetting("ShowConsole"));
+ settings().registerOverride(globalSettings->getSetting("AutoCloseConsole"));
+ settings().registerOverride(globalSettings->getSetting("LogPrePostOutput"));
+}
+
+void BaseInstance::iconUpdated(QString key)
+{
+ if(iconKey() == key)
+ {
+ emit propertiesChanged(this);
+ }
+}
+
+void BaseInstance::nuke()
+{
+ QDir(instanceRoot()).removeRecursively();
+ emit nuked(this);
+}
+
+QString BaseInstance::id() const
+{
+ return QFileInfo(instanceRoot()).fileName();
+}
+
+QString BaseInstance::instanceType() const
+{
+ I_D(BaseInstance);
+ return d->m_settings->get("InstanceType").toString();
+}
+
+QString BaseInstance::instanceRoot() const
+{
+ I_D(BaseInstance);
+ return d->m_rootDir;
+}
+
+QString BaseInstance::minecraftRoot() const
+{
+ QFileInfo mcDir(PathCombine(instanceRoot(), "minecraft"));
+ QFileInfo dotMCDir(PathCombine(instanceRoot(), ".minecraft"));
+
+ if (dotMCDir.exists() && !mcDir.exists())
+ return dotMCDir.filePath();
+ else
+ return mcDir.filePath();
+}
+
+InstanceList *BaseInstance::instList() const
+{
+ if (parent()->inherits("InstanceList"))
+ return (InstanceList *)parent();
+ else
+ return NULL;
+}
+
+std::shared_ptr<BaseVersionList> BaseInstance::versionList() const
+{
+ return MMC->minecraftlist();
+}
+
+SettingsObject &BaseInstance::settings() const
+{
+ I_D(BaseInstance);
+ return *d->m_settings;
+}
+
+QString BaseInstance::baseJar() const
+{
+ I_D(BaseInstance);
+ bool customJar = d->m_settings->get("UseCustomBaseJar").toBool();
+ if (customJar)
+ {
+ return customBaseJar();
+ }
+ else
+ return defaultBaseJar();
+}
+
+QString BaseInstance::customBaseJar() const
+{
+ I_D(BaseInstance);
+ QString value = d->m_settings->get("CustomBaseJar").toString();
+ if (value.isNull() || value.isEmpty())
+ {
+ return defaultCustomBaseJar();
+ }
+ return value;
+}
+
+void BaseInstance::setCustomBaseJar(QString val)
+{
+ I_D(BaseInstance);
+ if (val.isNull() || val.isEmpty() || val == defaultCustomBaseJar())
+ d->m_settings->reset("CustomBaseJar");
+ else
+ d->m_settings->set("CustomBaseJar", val);
+}
+
+void BaseInstance::setShouldUseCustomBaseJar(bool val)
+{
+ I_D(BaseInstance);
+ d->m_settings->set("UseCustomBaseJar", val);
+}
+
+bool BaseInstance::shouldUseCustomBaseJar() const
+{
+ I_D(BaseInstance);
+ return d->m_settings->get("UseCustomBaseJar").toBool();
+}
+
+qint64 BaseInstance::lastLaunch() const
+{
+ I_D(BaseInstance);
+ return d->m_settings->get("lastLaunchTime").value<qint64>();
+}
+void BaseInstance::setLastLaunch(qint64 val)
+{
+ I_D(BaseInstance);
+ d->m_settings->set("lastLaunchTime", val);
+ emit propertiesChanged(this);
+}
+
+void BaseInstance::setGroupInitial(QString val)
+{
+ I_D(BaseInstance);
+ d->m_group = val;
+ emit propertiesChanged(this);
+}
+
+void BaseInstance::setGroupPost(QString val)
+{
+ setGroupInitial(val);
+ emit groupChanged();
+}
+
+QString BaseInstance::group() const
+{
+ I_D(BaseInstance);
+ return d->m_group;
+}
+
+void BaseInstance::setNotes(QString val)
+{
+ I_D(BaseInstance);
+ d->m_settings->set("notes", val);
+}
+QString BaseInstance::notes() const
+{
+ I_D(BaseInstance);
+ return d->m_settings->get("notes").toString();
+}
+
+void BaseInstance::setIconKey(QString val)
+{
+ I_D(BaseInstance);
+ d->m_settings->set("iconKey", val);
+ emit propertiesChanged(this);
+}
+QString BaseInstance::iconKey() const
+{
+ I_D(BaseInstance);
+ return d->m_settings->get("iconKey").toString();
+}
+
+void BaseInstance::setName(QString val)
+{
+ I_D(BaseInstance);
+ d->m_settings->set("name", val);
+ emit propertiesChanged(this);
+}
+
+QString BaseInstance::name() const
+{
+ I_D(BaseInstance);
+ return d->m_settings->get("name").toString();
+}
+
+QString BaseInstance::windowTitle() const
+{
+ return "MultiMC: " + name();
+}
+
+QStringList BaseInstance::extraArguments() const
+{
+ return Util::Commandline::splitArgs(settings().get("JvmArgs").toString());
+}
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
new file mode 100644
index 00000000..cd49f99b
--- /dev/null
+++ b/logic/BaseInstance.h
@@ -0,0 +1,200 @@
+/* Copyright 2013 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 <QObject>
+#include <QDateTime>
+
+#include <settingsobject.h>
+
+#include "inifile.h"
+#include "lists/BaseVersionList.h"
+#include "logic/auth/MojangAccount.h"
+
+class QDialog;
+class Task;
+class MinecraftProcess;
+class OneSixUpdate;
+class InstanceList;
+class BaseInstancePrivate;
+
+/*!
+ * \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 BaseInstance : public QObject
+{
+ Q_OBJECT
+protected:
+ /// no-touchy!
+ BaseInstance(BaseInstancePrivate *d, const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+
+public:
+ /// virtual destructor to make sure the destruction is COMPLETE
+ virtual ~BaseInstance() {};
+
+ /// 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;
+
+ /// get the type of this instance
+ QString instanceType() const;
+
+ /// Path to the instance's root directory.
+ QString instanceRoot() const;
+
+ /// Path to the instance's minecraft directory.
+ QString minecraftRoot() 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);
+
+ QStringList extraArguments() const;
+
+ virtual QString intendedVersionId() const = 0;
+ virtual bool setIntendedVersionId(QString version) = 0;
+
+ virtual bool versionIsCustom() = 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 Minecraft should be downloaded when the instance is launched.
+ */
+ virtual bool shouldUpdate() const = 0;
+ virtual void setShouldUpdate(bool val) = 0;
+
+ /// Get the curent base jar of this instance. By default, it's the
+ /// versions/$version/$version.jar
+ QString baseJar() const;
+
+ /// the default base jar of this instance
+ virtual QString defaultBaseJar() const = 0;
+ /// the default custom base jar of this instance
+ virtual QString defaultCustomBaseJar() const = 0;
+
+ /*!
+ * Whether or not custom base jar is used
+ */
+ bool shouldUseCustomBaseJar() const;
+ void setShouldUseCustomBaseJar(bool val);
+ /*!
+ * The value of the custom base jar
+ */
+ QString customBaseJar() const;
+ void setCustomBaseJar(QString val);
+
+ /**
+ * 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());
+
+ /*!
+ * \brief Gets the instance list that this instance is a part of.
+ * Returns NULL if this instance is not in a list
+ * (the parent is not an InstanceList).
+ * \return A pointer to the InstanceList containing this instance.
+ */
+ InstanceList *instList() const;
+
+ /*!
+ * \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<BaseVersionList> versionList() const;
+
+ /*!
+ * \brief Gets this instance's settings object.
+ * This settings object stores instance-specific settings.
+ * \return A pointer to this instance's settings object.
+ */
+ virtual SettingsObject &settings() const;
+
+ /// returns a valid update task
+ virtual std::shared_ptr<Task> doUpdate() = 0;
+
+ /// returns a valid minecraft process, ready for launch with the given account.
+ virtual MinecraftProcess *prepareForLaunch(AuthSessionPtr account) = 0;
+
+ /// do any necessary cleanups after the instance finishes. also runs before
+ /// 'prepareForLaunch'
+ virtual void cleanupAfterRun() = 0;
+
+ /// create a mod edit dialog for the instance
+ virtual QDialog *createModEditDialog(QWidget *parent) = 0;
+
+ /// is a particular action enabled with this instance selected?
+ virtual bool menuActionEnabled(QString action_name) const = 0;
+
+ virtual QString getStatusbarDescription() = 0;
+
+ /// FIXME: this really should be elsewhere...
+ virtual QString instanceConfigFolder() const = 0;
+
+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);
+
+protected slots:
+ void iconUpdated(QString key);
+
+protected:
+ std::shared_ptr<BaseInstancePrivate> inst_d;
+};
+
+// pointer for lazy people
+typedef std::shared_ptr<BaseInstance> InstancePtr;
diff --git a/logic/BaseInstance_p.h b/logic/BaseInstance_p.h
new file mode 100644
index 00000000..06581a34
--- /dev/null
+++ b/logic/BaseInstance_p.h
@@ -0,0 +1,29 @@
+/* Copyright 2013 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 <QString>
+#include <settingsobject.h>
+
+class BaseInstance;
+
+#define I_D(Class) Class##Private *const d = (Class##Private * const)inst_d.get()
+
+struct BaseInstancePrivate
+{
+ QString m_rootDir;
+ QString m_group;
+ SettingsObject *m_settings;
+}; \ No newline at end of file
diff --git a/logic/BaseVersion.h b/logic/BaseVersion.h
new file mode 100644
index 00000000..43f5942a
--- /dev/null
+++ b/logic/BaseVersion.h
@@ -0,0 +1,55 @@
+/* Copyright 2013 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 <memory>
+
+/*!
+ * An abstract base class for versions.
+ */
+struct 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<BaseVersion> BaseVersionPtr;
+
+Q_DECLARE_METATYPE(BaseVersionPtr) \ No newline at end of file
diff --git a/logic/EnabledItemFilter.cpp b/logic/EnabledItemFilter.cpp
new file mode 100644
index 00000000..c252a0ad
--- /dev/null
+++ b/logic/EnabledItemFilter.cpp
@@ -0,0 +1,43 @@
+/* Copyright 2013 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 "EnabledItemFilter.h"
+
+EnabledItemFilter::EnabledItemFilter(QObject *parent) : QSortFilterProxyModel(parent)
+{
+}
+
+void EnabledItemFilter::setActive(bool active)
+{
+ m_active = active;
+ invalidateFilter();
+}
+
+bool EnabledItemFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ if (!m_active)
+ return true;
+ QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
+ if (sourceModel()->flags(index) & Qt::ItemIsEnabled)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool EnabledItemFilter::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ return QSortFilterProxyModel::lessThan(left, right);
+}
diff --git a/logic/EnabledItemFilter.h b/logic/EnabledItemFilter.h
new file mode 100644
index 00000000..bf5e1e85
--- /dev/null
+++ b/logic/EnabledItemFilter.h
@@ -0,0 +1,32 @@
+/* Copyright 2013 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 <QSortFilterProxyModel>
+
+class EnabledItemFilter : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ EnabledItemFilter(QObject *parent = 0);
+ void setActive(bool active);
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+ bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
+
+private:
+ bool m_active = false;
+}; \ No newline at end of file
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
new file mode 100644
index 00000000..8d4c5b41
--- /dev/null
+++ b/logic/ForgeInstaller.cpp
@@ -0,0 +1,155 @@
+/* Copyright 2013 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 "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "net/HttpMetaCache.h"
+#include <quazip.h>
+#include <quazipfile.h>
+#include <pathutils.h>
+#include <QStringList>
+#include "MultiMC.h"
+
+ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
+{
+ std::shared_ptr<OneSixVersion> newVersion;
+ m_universal_url = universal_url;
+
+ 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;
+
+ // read the forge version info
+ {
+ newVersion = OneSixVersion::fromJson(versionInfoVal.toObject());
+ if (!newVersion)
+ return;
+ }
+
+ QJsonObject installObj = installVal.toObject();
+ QString libraryName = installObj.value("path").toString();
+ internalPath = installObj.value("filePath").toString();
+
+ // where do we put the library? decode the mojang path
+ OneSixLibrary lib(libraryName);
+ lib.finalize();
+
+ auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath());
+ finalPath = "libraries/" + lib.storagePath();
+ if (!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->stale = false;
+ cacheentry->md5sum = md5sum.result().toHex().constData();
+ MMC->metacache()->updateEntry(cacheentry);
+ }
+ file.close();
+
+ m_forge_version = newVersion;
+ realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
+}
+
+bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
+{
+ if (!m_forge_version)
+ return false;
+ to->externalUpdateStart();
+ int sliding_insert_window = 0;
+ {
+ // for each library in the version we are adding (except for the blacklisted)
+ QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
+ for (auto lib : m_forge_version->libraries)
+ {
+ QString libName = lib->name();
+ // WARNING: This could actually break.
+ // if this is the actual forge lib, set an absolute url for the download
+ if (libName.contains("minecraftforge"))
+ {
+ lib->setAbsoluteUrl(m_universal_url);
+ }
+ else if (libName.contains("scala"))
+ {
+ lib->setHint("forge-pack-xz");
+ }
+ if (blacklist.contains(libName))
+ continue;
+
+ // find an entry that matches this one
+ bool found = false;
+ for (auto tolib : to->libraries)
+ {
+ if (tolib->name() != libName)
+ continue;
+ found = true;
+ // replace lib
+ tolib = lib;
+ break;
+ }
+ if (!found)
+ {
+ // add lib
+ to->libraries.insert(sliding_insert_window, lib);
+ sliding_insert_window++;
+ }
+ }
+ to->mainClass = m_forge_version->mainClass;
+ to->minecraftArguments = m_forge_version->minecraftArguments;
+ to->processArguments = m_forge_version->processArguments;
+ }
+ to->externalUpdateFinish();
+ return to->toOriginalFile();
+}
diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h
new file mode 100644
index 00000000..0b9f9c77
--- /dev/null
+++ b/logic/ForgeInstaller.h
@@ -0,0 +1,36 @@
+/* Copyright 2013 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 <QString>
+#include <memory>
+
+class OneSixVersion;
+
+class ForgeInstaller
+{
+public:
+ ForgeInstaller(QString filename, QString universal_url);
+
+ bool apply(std::shared_ptr<OneSixVersion> to);
+
+private:
+ // the version, read from the installer
+ std::shared_ptr<OneSixVersion> m_forge_version;
+ QString internalPath;
+ QString finalPath;
+ QString realVersionId;
+ QString m_universal_url;
+};
diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp
new file mode 100644
index 00000000..1f1a5879
--- /dev/null
+++ b/logic/InstanceFactory.cpp
@@ -0,0 +1,196 @@
+/* Copyright 2013 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 "InstanceFactory.h"
+
+#include <QDir>
+#include <QFileInfo>
+
+#include "BaseInstance.h"
+#include "LegacyInstance.h"
+#include "LegacyFTBInstance.h"
+#include "OneSixInstance.h"
+#include "OneSixFTBInstance.h"
+#include "NostalgiaInstance.h"
+#include "BaseVersion.h"
+#include "MinecraftVersion.h"
+
+#include "inifile.h"
+#include <inisettingsobject.h>
+#include <setting.h>
+
+#include "pathutils.h"
+#include "logger/QsLog.h"
+
+InstanceFactory InstanceFactory::loader;
+
+InstanceFactory::InstanceFactory() : QObject(NULL)
+{
+}
+
+InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst,
+ const QString &instDir)
+{
+ auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
+
+ m_settings->registerSetting("InstanceType", "Legacy");
+
+ QString inst_type = m_settings->get("InstanceType").toString();
+
+ // FIXME: replace with a map lookup, where instance classes register their types
+ if (inst_type == "Legacy")
+ {
+ inst = new LegacyInstance(instDir, m_settings, this);
+ }
+ else if (inst_type == "OneSix")
+ {
+ inst = new OneSixInstance(instDir, m_settings, this);
+ }
+ else if (inst_type == "Nostalgia")
+ {
+ inst = new NostalgiaInstance(instDir, m_settings, this);
+ }
+ else if (inst_type == "LegacyFTB")
+ {
+ inst = new LegacyFTBInstance(instDir, m_settings, this);
+ }
+ else if (inst_type == "OneSixFTB")
+ {
+ inst = new OneSixFTBInstance(instDir, m_settings, this);
+ }
+ else
+ {
+ return InstanceFactory::UnknownLoadError;
+ }
+ return NoLoadError;
+}
+
+InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&inst,
+ BaseVersionPtr version,
+ const QString &instDir,
+ const InstType type)
+{
+ QDir rootDir(instDir);
+
+ QLOG_DEBUG() << instDir.toUtf8();
+ if (!rootDir.exists() && !rootDir.mkpath("."))
+ {
+ return InstanceFactory::CantCreateDir;
+ }
+ auto mcVer = std::dynamic_pointer_cast<MinecraftVersion>(version);
+ if (!mcVer)
+ return InstanceFactory::NoSuchVersion;
+
+ auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
+ m_settings->registerSetting("InstanceType", "Legacy");
+
+ if (type == NormalInst)
+ {
+ switch (mcVer->type)
+ {
+ case MinecraftVersion::Legacy:
+ m_settings->set("InstanceType", "Legacy");
+ inst = new LegacyInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ case MinecraftVersion::OneSix:
+ m_settings->set("InstanceType", "OneSix");
+ inst = new OneSixInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ case MinecraftVersion::Nostalgia:
+ m_settings->set("InstanceType", "Nostalgia");
+ inst = new NostalgiaInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ default:
+ {
+ delete m_settings;
+ return InstanceFactory::NoSuchVersion;
+ }
+ }
+ }
+ else if (type == FTBInstance)
+ {
+ switch (mcVer->type)
+ {
+ case MinecraftVersion::Legacy:
+ m_settings->set("InstanceType", "LegacyFTB");
+ inst = new LegacyFTBInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ case MinecraftVersion::OneSix:
+ m_settings->set("InstanceType", "OneSixFTB");
+ inst = new OneSixFTBInstance(instDir, m_settings, this);
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
+ break;
+ default:
+ {
+ delete m_settings;
+ return InstanceFactory::NoSuchVersion;
+ }
+ }
+ }
+ else
+ {
+ delete m_settings;
+ return InstanceFactory::NoSuchVersion;
+ }
+
+ // FIXME: really, how do you even know?
+ return InstanceFactory::NoCreateError;
+}
+
+InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&newInstance,
+ BaseInstance *&oldInstance,
+ const QString &instDir)
+{
+ QDir rootDir(instDir);
+
+ QLOG_DEBUG() << instDir.toUtf8();
+ if (!copyPath(oldInstance->instanceRoot(), instDir))
+ {
+ rootDir.removeRecursively();
+ return InstanceFactory::CantCreateDir;
+ }
+ auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg"));
+ m_settings->registerSetting("InstanceType", "Legacy");
+ QString inst_type = m_settings->get("InstanceType").toString();
+
+ if(inst_type == "OneSixFTB")
+ m_settings->set("InstanceType", "OneSix");
+ if(inst_type == "LegacyFTB")
+ m_settings->set("InstanceType", "Legacy");
+
+ auto error = loadInstance(newInstance, instDir);
+
+ switch (error)
+ {
+ case NoLoadError:
+ return NoCreateError;
+ case NotAnInstance:
+ rootDir.removeRecursively();
+ return CantCreateDir;
+ default:
+ case UnknownLoadError:
+ rootDir.removeRecursively();
+ return UnknownCreateError;
+ }
+}
diff --git a/logic/InstanceFactory.h b/logic/InstanceFactory.h
new file mode 100644
index 00000000..5ff4c7ec
--- /dev/null
+++ b/logic/InstanceFactory.h
@@ -0,0 +1,105 @@
+/* Copyright 2013 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 <QObject>
+#include <QMap>
+#include <QList>
+
+#include "BaseVersion.h"
+
+class BaseVersion;
+class BaseInstance;
+
+/*!
+ * The \bInstanceFactory\b is a singleton that manages loading and creating instances.
+ */
+class InstanceFactory : public QObject
+{
+ Q_OBJECT
+public:
+ /*!
+ * \brief Gets a reference to the instance loader.
+ */
+ static InstanceFactory &get()
+ {
+ return loader;
+ }
+
+ enum InstLoadError
+ {
+ NoLoadError = 0,
+ UnknownLoadError,
+ NotAnInstance
+ };
+
+ enum InstCreateError
+ {
+ NoCreateError = 0,
+ NoSuchVersion,
+ UnknownCreateError,
+ InstExists,
+ CantCreateDir
+ };
+
+ enum InstType
+ {
+ NormalInst,
+ FTBInstance
+ };
+
+ /*!
+ * \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.
+ * \param type The type of instance to create
+ * \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(BaseInstance *&inst, BaseVersionPtr version,
+ const QString &instDir, const InstType type = NormalInst);
+
+ /*!
+ * \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(BaseInstance *&newInstance, BaseInstance *&oldInstance,
+ const QString &instDir);
+
+ /*!
+ * \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(BaseInstance *&inst, const QString &instDir);
+
+private:
+ InstanceFactory();
+
+ static InstanceFactory loader;
+};
diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp
new file mode 100644
index 00000000..c0079d80
--- /dev/null
+++ b/logic/InstanceLauncher.cpp
@@ -0,0 +1,94 @@
+/* Copyright 2013 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 <iostream>
+
+#include "InstanceLauncher.h"
+#include "MultiMC.h"
+
+#include "gui/ConsoleWindow.h"
+#include "gui/dialogs/ProgressDialog.h"
+
+#include "logic/MinecraftProcess.h"
+#include "logic/lists/InstanceList.h"
+
+InstanceLauncher::InstanceLauncher(QString instId) : QObject(), instId(instId)
+{
+}
+
+void InstanceLauncher::onTerminated()
+{
+ std::cout << "Minecraft exited" << std::endl;
+ MMC->quit();
+}
+
+void InstanceLauncher::onLoginComplete()
+{
+ // TODO: Fix this.
+ /*
+ LoginTask *task = (LoginTask *)QObject::sender();
+ auto result = task->getResult();
+ auto instance = MMC->instances()->getInstanceById(instId);
+ proc = instance->prepareForLaunch(result);
+ if (!proc)
+ {
+ // FIXME: report error
+ return;
+ }
+ console = new ConsoleWindow(proc);
+ connect(console, SIGNAL(isClosing()), this, SLOT(onTerminated()));
+
+ proc->setLogin(result.username, result.session_id);
+ proc->launch();
+ */
+}
+
+void InstanceLauncher::doLogin(const QString &errorMsg)
+{
+ // FIXME: Use new account system here...
+ /*
+ LoginDialog *loginDlg = new LoginDialog(nullptr, errorMsg);
+ loginDlg->exec();
+ if (loginDlg->result() == QDialog::Accepted)
+ {
+ PasswordLogin uInfo{loginDlg->getUsername(), loginDlg->getPassword()};
+
+ ProgressDialog *tDialog = new ProgressDialog(nullptr);
+ LoginTask *loginTask = new LoginTask(uInfo, tDialog);
+ connect(loginTask, SIGNAL(succeeded()), SLOT(onLoginComplete()), Qt::QueuedConnection);
+ connect(loginTask, SIGNAL(failed(QString)), SLOT(doLogin(QString)),
+ Qt::QueuedConnection);
+ tDialog->exec(loginTask);
+ }
+ */
+ // onLoginComplete(LoginResponse("Offline","Offline", 1));
+}
+
+int InstanceLauncher::launch()
+{
+ std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
+ auto instance = MMC->instances()->getInstanceById(instId);
+ if (!instance)
+ {
+ std::cout << "Could not find instance requested. note that you have to specify the ID, "
+ "not the NAME" << std::endl;
+ return 1;
+ }
+
+ std::cout << "Logging in..." << std::endl;
+ doLogin("");
+
+ return MMC->exec();
+}
diff --git a/logic/InstanceLauncher.h b/logic/InstanceLauncher.h
new file mode 100644
index 00000000..107c069f
--- /dev/null
+++ b/logic/InstanceLauncher.h
@@ -0,0 +1,44 @@
+/* Copyright 2013 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 <QObject>
+
+class MinecraftProcess;
+class ConsoleWindow;
+
+// Commandline instance launcher
+class InstanceLauncher : public QObject
+{
+ Q_OBJECT
+
+private:
+ QString instId;
+ MinecraftProcess *proc;
+ ConsoleWindow *console;
+
+public:
+ InstanceLauncher(QString instId);
+
+private
+slots:
+ void onTerminated();
+ void onLoginComplete();
+ void doLogin(const QString &errorMsg);
+
+public:
+ int launch();
+};
diff --git a/logic/JavaChecker.cpp b/logic/JavaChecker.cpp
new file mode 100644
index 00000000..b87ee3d5
--- /dev/null
+++ b/logic/JavaChecker.cpp
@@ -0,0 +1,124 @@
+#include "JavaChecker.h"
+#include "MultiMC.h"
+#include <pathutils.h>
+#include <QFile>
+#include <QProcess>
+#include <QMap>
+#include <QTemporaryFile>
+
+JavaChecker::JavaChecker(QObject *parent) : QObject(parent)
+{
+}
+
+void JavaChecker::performCheck()
+{
+ QString checkerJar = PathCombine(MMC->bin(), "jars", "JavaCheck.jar");
+
+ QStringList args = {"-jar", checkerJar};
+
+ process.reset(new QProcess());
+ process->setArguments(args);
+ process->setProgram(path);
+ process->setProcessChannelMode(QProcess::SeparateChannels);
+ QLOG_DEBUG() << "Running java checker!";
+ QLOG_DEBUG() << "Java: " + path;
+ QLOG_DEBUG() << "Args: {" + 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(&killTimer, SIGNAL(timeout()), SLOT(timeout()));
+ killTimer.setSingleShot(true);
+ killTimer.start(5000);
+ process->start();
+}
+
+void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
+{
+ killTimer.stop();
+ QProcessPtr _process;
+ _process.swap(process);
+
+ JavaCheckResult result;
+ {
+ result.path = path;
+ result.id = id;
+ }
+ QLOG_DEBUG() << "Java checker finished with status " << status << " exit code " << exitcode;
+
+ if (status == QProcess::CrashExit || exitcode == 1)
+ {
+ QLOG_DEBUG() << "Java checker failed!";
+ emit checkFinished(result);
+ return;
+ }
+
+ bool success = true;
+ QString p_stdout = _process->readAllStandardOutput();
+ QLOG_DEBUG() << p_stdout;
+
+ QMap<QString, QString> results;
+ QStringList lines = p_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)
+ {
+ QLOG_DEBUG() << "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;
+ QLOG_DEBUG() << "Java checker succeeded.";
+ emit checkFinished(result);
+}
+
+void JavaChecker::error(QProcess::ProcessError err)
+{
+ if(err == QProcess::FailedToStart)
+ {
+ killTimer.stop();
+ QLOG_DEBUG() << "Java checker has failed to start.";
+ JavaCheckResult result;
+ {
+ result.path = path;
+ result.id = id;
+ }
+
+ emit checkFinished(result);
+ return;
+ }
+}
+
+void JavaChecker::timeout()
+{
+ // NO MERCY. NO ABUSE.
+ if(process)
+ {
+ QLOG_DEBUG() << "Java checker has been killed by timeout.";
+ process->kill();
+ }
+}
diff --git a/logic/JavaChecker.h b/logic/JavaChecker.h
new file mode 100644
index 00000000..e19895f7
--- /dev/null
+++ b/logic/JavaChecker.h
@@ -0,0 +1,42 @@
+#pragma once
+#include <QProcess>
+#include <QTimer>
+#include <memory>
+
+class JavaChecker;
+
+
+struct JavaCheckResult
+{
+ QString path;
+ QString mojangPlatform;
+ QString realPlatform;
+ QString javaVersion;
+ bool valid = false;
+ bool is_64bit = false;
+ int id;
+};
+
+typedef std::shared_ptr<QProcess> QProcessPtr;
+typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
+class JavaChecker : public QObject
+{
+ Q_OBJECT
+public:
+ explicit JavaChecker(QObject *parent = 0);
+ void performCheck();
+
+ QString path;
+ int id;
+
+signals:
+ void checkFinished(JavaCheckResult result);
+private:
+ QProcessPtr process;
+ QTimer killTimer;
+public
+slots:
+ void timeout();
+ void finished(int exitcode, QProcess::ExitStatus);
+ void error(QProcess::ProcessError);
+};
diff --git a/logic/JavaCheckerJob.cpp b/logic/JavaCheckerJob.cpp
new file mode 100644
index 00000000..b0aea758
--- /dev/null
+++ b/logic/JavaCheckerJob.cpp
@@ -0,0 +1,47 @@
+/* Copyright 2013 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 "pathutils.h"
+#include "MultiMC.h"
+
+#include "logger/QsLog.h"
+
+void JavaCheckerJob::partFinished(JavaCheckResult result)
+{
+ num_finished++;
+ QLOG_INFO() << 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::start()
+{
+ QLOG_INFO() << 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/logic/JavaCheckerJob.h b/logic/JavaCheckerJob.h
new file mode 100644
index 00000000..132a92d4
--- /dev/null
+++ b/logic/JavaCheckerJob.h
@@ -0,0 +1,100 @@
+/* Copyright 2013 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 <QtNetwork>
+#include <QLabel>
+#include "JavaChecker.h"
+#include "logic/tasks/ProgressProvider.h"
+
+class JavaCheckerJob;
+typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
+
+class JavaCheckerJob : public ProgressProvider
+{
+ Q_OBJECT
+public:
+ explicit JavaCheckerJob(QString job_name) : ProgressProvider(), 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())
+ {
+ emit progress(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 void getProgress(qint64 &current, qint64 &total)
+ {
+ current = current_progress;
+ total = total_progress;
+ }
+ ;
+ virtual QString getStatus() const
+ {
+ return m_job_name;
+ }
+ ;
+ virtual bool isRunning() const
+ {
+ return m_running;
+ }
+ ;
+
+signals:
+ void started();
+ void progress(int current, int total);
+ void finished(QList<JavaCheckResult>);
+public
+slots:
+ virtual void start();
+ // FIXME: implement
+ virtual void abort() {};
+private
+slots:
+ void partFinished(JavaCheckResult result);
+
+private:
+ QString m_job_name;
+ QList<JavaCheckerPtr> javacheckers;
+ QList<JavaCheckResult> javaresults;
+ qint64 current_progress = 0;
+ qint64 total_progress = 0;
+ int num_finished = 0;
+ bool m_running = false;
+};
diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp
new file mode 100644
index 00000000..cf47df6f
--- /dev/null
+++ b/logic/JavaUtils.cpp
@@ -0,0 +1,208 @@
+/* Copyright 2013 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 <QStringList>
+#include <QString>
+#include <QDir>
+#include <QMessageBox>
+
+#include <setting.h>
+#include <pathutils.h>
+
+#include "MultiMC.h"
+
+#include "JavaUtils.h"
+#include "logger/QsLog.h"
+#include "gui/dialogs/VersionSelectDialog.h"
+#include "JavaCheckerJob.h"
+#include "lists/JavaVersionList.h"
+
+JavaUtils::JavaUtils()
+{
+}
+
+JavaVersionPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch)
+{
+ JavaVersionPtr javaVersion(new JavaVersion());
+
+ javaVersion->id = id;
+ javaVersion->arch = arch;
+ javaVersion->path = path;
+
+ return javaVersion;
+}
+
+JavaVersionPtr JavaUtils::GetDefaultJava()
+{
+ JavaVersionPtr javaVersion(new JavaVersion());
+
+ javaVersion->id = "java";
+ javaVersion->arch = "unknown";
+ javaVersion->path = "java";
+
+ return javaVersion;
+}
+
+#if WINDOWS
+QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName)
+{
+ QList<JavaVersionPtr> 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.
+ JavaVersionPtr javaVersion(new JavaVersion());
+
+ javaVersion->id = subKeyName;
+ javaVersion->arch = archType;
+ javaVersion->path =
+ QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe");
+ javas.append(javaVersion);
+ }
+
+ RegCloseKey(newKey);
+ }
+ }
+ }
+ }
+
+ RegCloseKey(jreKey);
+ }
+
+ return javas;
+}
+
+QList<QString> JavaUtils::FindJavaPaths()
+{
+ QList<JavaVersionPtr> java_candidates;
+
+ QList<JavaVersionPtr> JRE64s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
+ QList<JavaVersionPtr> JDK64s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
+ QList<JavaVersionPtr> JRE32s = this->FindJavaFromRegistryKey(
+ KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
+ QList<JavaVersionPtr> 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/java.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/java.exe"));
+ java_candidates.append(JDK64s);
+ java_candidates.append(JRE32s);
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/java.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/java.exe"));
+ java_candidates.append(JDK32s);
+ java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
+
+ QList<QString> candidates;
+ for(JavaVersionPtr java_candidate : java_candidates)
+ {
+ if(!candidates.contains(java_candidate->path))
+ {
+ candidates.append(java_candidate->path);
+ }
+ }
+
+ return candidates;
+}
+
+#elif OSX
+QList<QString> JavaUtils::FindJavaPaths()
+{
+ QList<QString> javas;
+ javas.append(this->GetDefaultJava()->path);
+ javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java");
+ javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java");
+
+ return javas;
+}
+
+#elif LINUX
+QList<QString> JavaUtils::FindJavaPaths()
+{
+ QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\"";
+
+ QList<QString> javas;
+ javas.append(this->GetDefaultJava()->path);
+
+ return javas;
+}
+#else
+QList<QString> JavaUtils::FindJavaPaths()
+{
+ QLOG_INFO() << "Unknown operating system build - defaulting to \"java\"";
+
+ QList<QString> javas;
+ javas.append(this->GetDefaultJava()->path);
+
+ return javas;
+}
+#endif
diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h
new file mode 100644
index 00000000..22a68ef3
--- /dev/null
+++ b/logic/JavaUtils.h
@@ -0,0 +1,43 @@
+/* Copyright 2013 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 <QStringList>
+#include <QWidget>
+
+#include <osutils.h>
+#include "JavaCheckerJob.h"
+#include "JavaChecker.h"
+#include "lists/JavaVersionList.h"
+
+#if WINDOWS
+#include <windows.h>
+#endif
+
+class JavaUtils : public QObject
+{
+ Q_OBJECT
+public:
+ JavaUtils();
+
+ JavaVersionPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown");
+ QList<QString> FindJavaPaths();
+ JavaVersionPtr GetDefaultJava();
+
+#if WINDOWS
+ QList<JavaVersionPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName);
+#endif
+};
diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp
new file mode 100644
index 00000000..6c6bd10b
--- /dev/null
+++ b/logic/LegacyFTBInstance.cpp
@@ -0,0 +1,21 @@
+#include "LegacyFTBInstance.h"
+
+LegacyFTBInstance::LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
+ LegacyInstance(rootDir, settings, parent)
+{
+}
+
+QString LegacyFTBInstance::getStatusbarDescription()
+{
+ return "Legacy FTB: " + intendedVersionId();
+}
+
+bool LegacyFTBInstance::menuActionEnabled(QString action_name) const
+{
+ return false;
+}
+
+QString LegacyFTBInstance::id() const
+{
+ return "FTB/" + BaseInstance::id();
+}
diff --git a/logic/LegacyFTBInstance.h b/logic/LegacyFTBInstance.h
new file mode 100644
index 00000000..70f60535
--- /dev/null
+++ b/logic/LegacyFTBInstance.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "LegacyInstance.h"
+
+class LegacyFTBInstance : public LegacyInstance
+{
+ Q_OBJECT
+public:
+ explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+ virtual QString getStatusbarDescription();
+ virtual bool menuActionEnabled(QString action_name) const;
+ virtual QString id() const;
+};
diff --git a/logic/LegacyForge.cpp b/logic/LegacyForge.cpp
new file mode 100644
index 00000000..94212ae4
--- /dev/null
+++ b/logic/LegacyForge.cpp
@@ -0,0 +1,56 @@
+/* Copyright 2013 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 "LegacyForge.h"
+
+MinecraftForge::MinecraftForge(const QString &file) : Mod(file)
+{
+}
+
+bool MinecraftForge::FixVersionIfNeeded(QString newVersion)
+{/*
+ wxString reportedVersion = GetModVersion();
+ if(reportedVersion == "..." || reportedVersion.empty())
+ {
+ std::auto_ptr<wxFFileInputStream> in(new wxFFileInputStream("forge.zip"));
+ wxTempFileOutputStream out("forge.zip");
+ wxTextOutputStream textout(out);
+ wxZipInputStream inzip(*in);
+ wxZipOutputStream outzip(out);
+ std::auto_ptr<wxZipEntry> entry;
+ // preserve metadata
+ outzip.CopyArchiveMetaData(inzip);
+ // copy all entries
+ while (entry.reset(inzip.GetNextEntry()), entry.get() != NULL)
+ if (!outzip.CopyEntry(entry.release(), inzip))
+ return false;
+ // release last entry
+ in.reset();
+ outzip.PutNextEntry("forgeversion.properties");
+
+ wxStringTokenizer tokenizer(newVersion,".");
+ wxString verFile;
+ verFile << wxString("forge.major.number=") << tokenizer.GetNextToken() << "\n";
+ verFile << wxString("forge.minor.number=") << tokenizer.GetNextToken() << "\n";
+ verFile << wxString("forge.revision.number=") << tokenizer.GetNextToken() << "\n";
+ verFile << wxString("forge.build.number=") << tokenizer.GetNextToken() << "\n";
+ auto buf = verFile.ToUTF8();
+ outzip.Write(buf.data(), buf.length());
+ // check if we succeeded
+ return inzip.Eof() && outzip.Close() && out.Commit();
+ }
+ */
+ return true;
+}
diff --git a/logic/LegacyForge.h b/logic/LegacyForge.h
new file mode 100644
index 00000000..f4165ffa
--- /dev/null
+++ b/logic/LegacyForge.h
@@ -0,0 +1,25 @@
+/* Copyright 2013 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 "Mod.h"
+
+class MinecraftForge : public Mod
+{
+public:
+ MinecraftForge(const QString &file);
+ bool FixVersionIfNeeded(QString newVersion);
+};
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
new file mode 100644
index 00000000..a9f0d112
--- /dev/null
+++ b/logic/LegacyInstance.cpp
@@ -0,0 +1,282 @@
+/* Copyright 2013 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 <QFileInfo>
+#include <QDir>
+#include <QImage>
+#include <setting.h>
+#include <pathutils.h>
+#include <cmdutils.h>
+
+#include "MultiMC.h"
+
+#include "LegacyInstance.h"
+#include "LegacyInstance_p.h"
+
+#include "logic/MinecraftProcess.h"
+#include "logic/LegacyUpdate.h"
+#include "logic/icons/IconList.h"
+
+#include "gui/dialogs/LegacyModEditDialog.h"
+
+LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent)
+ : BaseInstance(new LegacyInstancePrivate(), rootDir, settings, parent)
+{
+ settings->registerSetting("NeedsRebuild", true);
+ settings->registerSetting("ShouldUpdate", false);
+ settings->registerSetting("JarVersion", "Unknown");
+ settings->registerSetting("LwjglVersion", "2.9.0");
+ settings->registerSetting("IntendedJarVersion", "");
+}
+
+std::shared_ptr<Task> LegacyInstance::doUpdate()
+{
+ // make sure the jar mods list is initialized by asking for it.
+ auto list = jarModList();
+ // create an update task
+ return std::shared_ptr<Task>(new LegacyUpdate(this, this));
+}
+
+MinecraftProcess *LegacyInstance::prepareForLaunch(AuthSessionPtr account)
+{
+ MinecraftProcess *proc = new MinecraftProcess(this);
+
+ QIcon icon = MMC->icons()->getIcon(iconKey());
+ auto pixmap = icon.pixmap(128, 128);
+ pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
+
+ // create the launch script
+ QString launchScript;
+ {
+ // window size
+ QString windowParams;
+ if (settings().get("LaunchMaximized").toBool())
+ windowParams = "max";
+ else
+ windowParams = QString("%1x%2")
+ .arg(settings().get("MinecraftWinWidth").toInt())
+ .arg(settings().get("MinecraftWinHeight").toInt());
+
+ QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion())
+ .absolutePath();
+ launchScript += "userName " + account->player_name + "\n";
+ launchScript += "sessionId " + account->session + "\n";
+ launchScript += "windowTitle " + windowTitle() + "\n";
+ launchScript += "windowParams " + windowParams + "\n";
+ launchScript += "lwjgl " + lwjgl + "\n";
+ launchScript += "launch legacy\n";
+ }
+ proc->setLaunchScript(launchScript);
+
+ // set the process work path
+ proc->setWorkdir(minecraftRoot());
+
+ return proc;
+}
+
+void LegacyInstance::cleanupAfterRun()
+{
+ // FIXME: delete the launcher and icons and whatnot.
+}
+
+std::shared_ptr<ModList> LegacyInstance::coreModList()
+{
+ I_D(LegacyInstance);
+ if (!d->core_mod_list)
+ {
+ d->core_mod_list.reset(new ModList(coreModsDir()));
+ }
+ d->core_mod_list->update();
+ return d->core_mod_list;
+}
+
+std::shared_ptr<ModList> LegacyInstance::jarModList()
+{
+ I_D(LegacyInstance);
+ if (!d->jar_mod_list)
+ {
+ auto list = new ModList(jarModsDir(), modListFile());
+ connect(list, SIGNAL(changed()), SLOT(jarModsChanged()));
+ d->jar_mod_list.reset(list);
+ }
+ d->jar_mod_list->update();
+ return d->jar_mod_list;
+}
+
+void LegacyInstance::jarModsChanged()
+{
+ QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt.";
+ setShouldRebuild(true);
+}
+
+std::shared_ptr<ModList> LegacyInstance::loaderModList()
+{
+ I_D(LegacyInstance);
+ if (!d->loader_mod_list)
+ {
+ d->loader_mod_list.reset(new ModList(loaderModsDir()));
+ }
+ d->loader_mod_list->update();
+ return d->loader_mod_list;
+}
+
+std::shared_ptr<ModList> LegacyInstance::texturePackList()
+{
+ I_D(LegacyInstance);
+ if (!d->texture_pack_list)
+ {
+ d->texture_pack_list.reset(new ModList(texturePacksDir()));
+ }
+ d->texture_pack_list->update();
+ return d->texture_pack_list;
+}
+
+QDialog *LegacyInstance::createModEditDialog(QWidget *parent)
+{
+ return new LegacyModEditDialog(this, parent);
+}
+
+QString LegacyInstance::jarModsDir() const
+{
+ return PathCombine(instanceRoot(), "instMods");
+}
+
+QString LegacyInstance::binDir() const
+{
+ return PathCombine(minecraftRoot(), "bin");
+}
+
+QString LegacyInstance::savesDir() const
+{
+ return PathCombine(minecraftRoot(), "saves");
+}
+
+QString LegacyInstance::loaderModsDir() const
+{
+ return PathCombine(minecraftRoot(), "mods");
+}
+
+QString LegacyInstance::coreModsDir() const
+{
+ return PathCombine(minecraftRoot(), "coremods");
+}
+
+QString LegacyInstance::resourceDir() const
+{
+ return PathCombine(minecraftRoot(), "resources");
+}
+QString LegacyInstance::texturePacksDir() const
+{
+ return PathCombine(minecraftRoot(), "texturepacks");
+}
+
+QString LegacyInstance::runnableJar() const
+{
+ return PathCombine(binDir(), "minecraft.jar");
+}
+
+QString LegacyInstance::modListFile() const
+{
+ return PathCombine(instanceRoot(), "modlist");
+}
+
+QString LegacyInstance::instanceConfigFolder() const
+{
+ return PathCombine(minecraftRoot(), "config");
+}
+
+bool LegacyInstance::shouldRebuild() const
+{
+ I_D(LegacyInstance);
+ return d->m_settings->get("NeedsRebuild").toBool();
+}
+
+void LegacyInstance::setShouldRebuild(bool val)
+{
+ I_D(LegacyInstance);
+ d->m_settings->set("NeedsRebuild", val);
+}
+
+QString LegacyInstance::currentVersionId() const
+{
+ I_D(LegacyInstance);
+ return d->m_settings->get("JarVersion").toString();
+}
+
+QString LegacyInstance::lwjglVersion() const
+{
+ I_D(LegacyInstance);
+ return d->m_settings->get("LwjglVersion").toString();
+}
+
+void LegacyInstance::setLWJGLVersion(QString val)
+{
+ I_D(LegacyInstance);
+ d->m_settings->set("LwjglVersion", val);
+}
+
+QString LegacyInstance::intendedVersionId() const
+{
+ I_D(LegacyInstance);
+ return d->m_settings->get("IntendedJarVersion").toString();
+}
+
+bool LegacyInstance::setIntendedVersionId(QString version)
+{
+ settings().set("IntendedJarVersion", version);
+ setShouldUpdate(true);
+ return true;
+}
+
+bool LegacyInstance::shouldUpdate() const
+{
+ QVariant var = settings().get("ShouldUpdate");
+ if (!var.isValid() || var.toBool() == false)
+ {
+ return intendedVersionId() != currentVersionId();
+ }
+ return true;
+}
+
+void LegacyInstance::setShouldUpdate(bool val)
+{
+ settings().set("ShouldUpdate", val);
+}
+
+QString LegacyInstance::defaultBaseJar() const
+{
+ return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
+}
+
+QString LegacyInstance::defaultCustomBaseJar() const
+{
+ return PathCombine(binDir(), "mcbackup.jar");
+}
+
+bool LegacyInstance::menuActionEnabled(QString action_name) const
+{
+ if (action_name == "actionChangeInstMCVersion")
+ return false;
+ return true;
+}
+
+QString LegacyInstance::getStatusbarDescription()
+{
+ if (shouldUpdate())
+ return "Legacy : " + currentVersionId() + " -> " + intendedVersionId();
+ else
+ return "Legacy : " + currentVersionId();
+}
diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h
new file mode 100644
index 00000000..636addeb
--- /dev/null
+++ b/logic/LegacyInstance.h
@@ -0,0 +1,94 @@
+/* Copyright 2013 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 "BaseInstance.h"
+
+class ModList;
+class Task;
+
+class LegacyInstance : public BaseInstance
+{
+ Q_OBJECT
+public:
+
+ explicit LegacyInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+
+ /// Path to the instance's minecraft.jar
+ QString runnableJar() const;
+
+ //! Path to the instance's modlist file.
+ QString modListFile() const;
+
+ ////// Mod Lists //////
+ std::shared_ptr<ModList> jarModList();
+ std::shared_ptr<ModList> coreModList();
+ std::shared_ptr<ModList> loaderModList();
+ std::shared_ptr<ModList> texturePackList();
+
+ ////// Directories //////
+ QString savesDir() const;
+ QString texturePacksDir() const;
+ QString jarModsDir() const;
+ QString binDir() const;
+ QString loaderModsDir() const;
+ QString coreModsDir() const;
+ QString resourceDir() const;
+ virtual QString instanceConfigFolder() const override;
+
+ /*!
+ * Whether or not the instance's minecraft.jar needs to be rebuilt.
+ * If this is true, when the instance launches, its jar mods will be
+ * re-added to a fresh minecraft.jar file.
+ */
+ bool shouldRebuild() const;
+ void setShouldRebuild(bool val);
+
+ virtual QString currentVersionId() const override;
+
+ //! The version of LWJGL that this instance uses.
+ QString lwjglVersion() const;
+ /// st the version of LWJGL libs this instance will use
+ void setLWJGLVersion(QString val);
+
+ virtual QString intendedVersionId() const override;
+ virtual bool setIntendedVersionId(QString version) override;
+ // the `version' of Legacy instances is defined by the launcher code.
+ // in contrast with OneSix, where `version' is described in a json file
+ virtual bool versionIsCustom() override
+ {
+ return false;
+ }
+
+ virtual bool shouldUpdate() const override;
+ virtual void setShouldUpdate(bool val) override;
+ virtual std::shared_ptr<Task> doUpdate() override;
+
+ virtual MinecraftProcess *prepareForLaunch(AuthSessionPtr account) override;
+ virtual void cleanupAfterRun() override;
+ virtual QDialog *createModEditDialog(QWidget *parent) override;
+
+ virtual QString defaultBaseJar() const override;
+ virtual QString defaultCustomBaseJar() const override;
+
+ bool menuActionEnabled(QString action_name) const;
+ virtual QString getStatusbarDescription() override;
+
+protected
+slots:
+ virtual void jarModsChanged();
+};
diff --git a/logic/LegacyInstance_p.h b/logic/LegacyInstance_p.h
new file mode 100644
index 00000000..ed97ccd3
--- /dev/null
+++ b/logic/LegacyInstance_p.h
@@ -0,0 +1,30 @@
+/* Copyright 2013 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 <QString>
+#include <settingsobject.h>
+#include <memory>
+
+#include "BaseInstance_p.h"
+#include "ModList.h"
+
+struct LegacyInstancePrivate : public BaseInstancePrivate
+{
+ std::shared_ptr<ModList> jar_mod_list;
+ std::shared_ptr<ModList> core_mod_list;
+ std::shared_ptr<ModList> loader_mod_list;
+ std::shared_ptr<ModList> texture_pack_list;
+};
diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp
new file mode 100644
index 00000000..5d82a76b
--- /dev/null
+++ b/logic/LegacyUpdate.cpp
@@ -0,0 +1,495 @@
+/* Copyright 2013 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 "LegacyUpdate.h"
+#include "lists/LwjglVersionList.h"
+#include "lists/MinecraftVersionList.h"
+#include "BaseInstance.h"
+#include "LegacyInstance.h"
+#include "MultiMC.h"
+#include "ModList.h"
+#include <pathutils.h>
+#include <quazip.h>
+#include <quazipfile.h>
+#include <JlCompress.h>
+#include "logger/QsLog.h"
+#include "logic/net/URLConstants.h"
+
+LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
+{
+}
+
+void LegacyUpdate::executeTask()
+{
+ /*
+ if(m_only_prepare)
+ {
+ // FIXME: think this through some more.
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+ if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
+ {
+ ModTheJar();
+ }
+ else
+ {
+ emitSucceeded();
+ }
+ }
+ else
+ {
+ */
+ lwjglStart();
+ //}
+}
+
+void LegacyUpdate::lwjglStart()
+{
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+
+ lwjglVersion = inst->lwjglVersion();
+ lwjglTargetPath = PathCombine(MMC->settings()->get("LWJGLDir").toString(), lwjglVersion);
+ lwjglNativesPath = PathCombine(lwjglTargetPath, "natives");
+
+ // if the 'done' file exists, we don't have to download this again
+ QFileInfo doneFile(PathCombine(lwjglTargetPath, "done"));
+ if (doneFile.exists())
+ {
+ jarStart();
+ return;
+ }
+
+ auto list = MMC->lwjgllist();
+ if (!list->isLoaded())
+ {
+ emitFailed("Too soon! Let the LWJGL list load :)");
+ return;
+ }
+
+ setStatus(tr("Downloading new LWJGL..."));
+ auto version = list->getVersion(lwjglVersion);
+ if (!version)
+ {
+ emitFailed("Game update failed: the selected LWJGL version is invalid.");
+ return;
+ }
+
+ QString url = version->url();
+ QUrl realUrl(url);
+ QString hostname = realUrl.host();
+ auto worker = MMC->qnam();
+ QNetworkRequest req(realUrl);
+ req.setRawHeader("Host", hostname.toLatin1());
+ req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+ QNetworkReply *rep = worker->get(req);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ connect(worker.get(), SIGNAL(finished(QNetworkReply *)),
+ SLOT(lwjglFinished(QNetworkReply *)));
+ // connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ // SLOT(downloadError(QNetworkReply::NetworkError)));
+}
+
+void LegacyUpdate::lwjglFinished(QNetworkReply *reply)
+{
+ if (m_reply.get() != reply)
+ {
+ return;
+ }
+ if (reply->error() != QNetworkReply::NoError)
+ {
+ emitFailed("Failed to download: " + reply->errorString() +
+ "\nSometimes you have to wait a bit if you download many LWJGL versions in "
+ "a row. YMMV");
+ return;
+ }
+ auto worker = MMC->qnam();
+ // Here i check if there is a cookie for me in the reply and extract it
+ QList<QNetworkCookie> cookies =
+ qvariant_cast<QList<QNetworkCookie>>(reply->header(QNetworkRequest::SetCookieHeader));
+ if (cookies.count() != 0)
+ {
+ // you must tell which cookie goes with which url
+ worker->cookieJar()->setCookiesFromUrl(cookies, QUrl("sourceforge.net"));
+ }
+
+ // here you can check for the 302 or whatever other header i need
+ QVariant newLoc = reply->header(QNetworkRequest::LocationHeader);
+ if (newLoc.isValid())
+ {
+ QString redirectedTo = reply->header(QNetworkRequest::LocationHeader).toString();
+ QUrl realUrl(redirectedTo);
+ QString hostname = realUrl.host();
+ QNetworkRequest req(redirectedTo);
+ req.setRawHeader("Host", hostname.toLatin1());
+ req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+ QNetworkReply *rep = worker->get(req);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ return;
+ }
+ QFile saveMe("lwjgl.zip");
+ saveMe.open(QIODevice::WriteOnly);
+ saveMe.write(m_reply->readAll());
+ saveMe.close();
+ setStatus(tr("Installing new LWJGL..."));
+ extractLwjgl();
+ jarStart();
+}
+void LegacyUpdate::extractLwjgl()
+{
+ // make sure the directories are there
+
+ bool success = ensureFolderPathExists(lwjglNativesPath);
+
+ if (!success)
+ {
+ emitFailed("Failed to extract the lwjgl libs - error when creating required folders.");
+ return;
+ }
+
+ QuaZip zip("lwjgl.zip");
+ if (!zip.open(QuaZip::mdUnzip))
+ {
+ emitFailed("Failed to extract the lwjgl libs - not a valid archive.");
+ return;
+ }
+
+ // and now we are going to access files inside it
+ QuaZipFile file(&zip);
+ const QString jarNames[] = {"jinput.jar", "lwjgl_util.jar", "lwjgl.jar"};
+ for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile())
+ {
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ zip.close();
+ emitFailed("Failed to extract the lwjgl libs - error while reading archive.");
+ return;
+ }
+ QuaZipFileInfo info;
+ QString name = file.getActualFileName();
+ if (name.endsWith('/'))
+ {
+ file.close();
+ continue;
+ }
+ QString destFileName;
+ // Look for the jars
+ for (int i = 0; i < 3; i++)
+ {
+ if (name.endsWith(jarNames[i]))
+ {
+ destFileName = PathCombine(lwjglTargetPath, jarNames[i]);
+ }
+ }
+ // Not found? look for the natives
+ if (destFileName.isEmpty())
+ {
+#ifdef Q_OS_WIN32
+ QString nativesDir = "windows";
+#else
+#ifdef Q_OS_MAC
+ QString nativesDir = "macosx";
+#else
+ QString nativesDir = "linux";
+#endif
+#endif
+ if (name.contains(nativesDir))
+ {
+ int lastSlash = name.lastIndexOf('/');
+ int lastBackSlash = name.lastIndexOf('\\');
+ if (lastSlash != -1)
+ name = name.mid(lastSlash + 1);
+ else if (lastBackSlash != -1)
+ name = name.mid(lastBackSlash + 1);
+ destFileName = PathCombine(lwjglNativesPath, name);
+ }
+ }
+ // Now if destFileName is still empty, go to the next file.
+ if (!destFileName.isEmpty())
+ {
+ setStatus(tr("Installing new LWJGL - extracting ") + name + "...");
+ QFile output(destFileName);
+ output.open(QIODevice::WriteOnly);
+ output.write(file.readAll()); // FIXME: wste of memory!?
+ output.close();
+ }
+ file.close(); // do not forget to close!
+ }
+ zip.close();
+ m_reply.reset();
+ QFile doneFile(PathCombine(lwjglTargetPath, "done"));
+ doneFile.open(QIODevice::WriteOnly);
+ doneFile.write("done.");
+ doneFile.close();
+}
+
+void LegacyUpdate::lwjglFailed()
+{
+ emitFailed("Bad stuff happened while trying to get the lwjgl libs...");
+}
+
+void LegacyUpdate::jarStart()
+{
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+ if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
+ {
+ ModTheJar();
+ return;
+ }
+
+ setStatus(tr("Checking for jar updates..."));
+ // Make directories
+ QDir binDir(inst->binDir());
+ if (!binDir.exists() && !binDir.mkpath("."))
+ {
+ emitFailed("Failed to create bin folder.");
+ return;
+ }
+
+ // Build a list of URLs that will need to be downloaded.
+ setStatus(tr("Downloading new minecraft.jar ..."));
+
+ QString version_id = inst->intendedVersionId();
+ QString localPath = version_id + "/" + version_id + ".jar";
+ QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath;
+
+ auto dljob = new NetJob("Minecraft.jar for version " + version_id);
+
+ auto metacache = MMC->metacache();
+ auto entry = metacache->resolveEntry("versions", localPath);
+ dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
+ connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished()));
+ connect(dljob, SIGNAL(failed()), SLOT(jarFailed()));
+ connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ legacyDownloadJob.reset(dljob);
+ legacyDownloadJob->start();
+}
+
+void LegacyUpdate::jarFinished()
+{
+ // process the jar
+ ModTheJar();
+}
+
+void LegacyUpdate::jarFailed()
+{
+ // bad, bad
+ emitFailed("Failed to download the minecraft jar. Try again later.");
+}
+
+bool LegacyUpdate::MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
+ MetainfAction metainf)
+{
+ setStatus(tr("Installing mods: Adding ") + from.fileName() + " ...");
+
+ 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 (filename.contains("META-INF") && metainf == LegacyUpdate::IgnoreMetainf)
+ {
+ QLOG_INFO() << "Skipping META-INF " << filename << " from " << from.fileName();
+ continue;
+ }
+ if (contained.contains(filename))
+ {
+ QLOG_INFO() << "Skipping already contained file " << filename << " from "
+ << from.fileName();
+ continue;
+ }
+ contained.insert(filename);
+ QLOG_INFO() << "Adding file " << filename << " from " << from.fileName();
+
+ if (!fileInsideMod.open(QIODevice::ReadOnly))
+ {
+ QLOG_ERROR() << "Failed to open " << filename << " from " << from.fileName();
+ return false;
+ }
+ /*
+ QuaZipFileInfo old_info;
+ fileInsideMod.getFileInfo(&old_info);
+ */
+ QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
+ /*
+ info_out.externalAttr = old_info.externalAttr;
+ */
+ if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
+ {
+ QLOG_ERROR() << "Failed to open " << filename << " in the jar";
+ fileInsideMod.close();
+ return false;
+ }
+ if (!JlCompress::copyData(fileInsideMod, zipOutFile))
+ {
+ zipOutFile.close();
+ fileInsideMod.close();
+ QLOG_ERROR() << "Failed to copy data of " << filename << " into the jar";
+ return false;
+ }
+ zipOutFile.close();
+ fileInsideMod.close();
+ }
+ return true;
+}
+
+void LegacyUpdate::ModTheJar()
+{
+ LegacyInstance *inst = (LegacyInstance *)m_inst;
+
+ if (!inst->shouldRebuild())
+ {
+ emitSucceeded();
+ return;
+ }
+
+ // Get the mod list
+ auto modList = inst->jarModList();
+
+ QFileInfo runnableJar(inst->runnableJar());
+ QFileInfo baseJar(inst->baseJar());
+ bool base_is_custom = inst->shouldUseCustomBaseJar();
+
+ // Nothing to do if there are no jar mods to install, no backup and just the mc jar
+ if (base_is_custom)
+ {
+ // yes, this can happen if the instance only has the runnable jar and not the base jar
+ // it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
+ // because that's not something mmc4 guarantees
+ if (runnableJar.isFile() && !baseJar.exists() && modList->empty())
+ {
+ inst->setShouldRebuild(false);
+ emitSucceeded();
+ return;
+ }
+
+ setStatus(tr("Installing mods: Backing up minecraft.jar ..."));
+ if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
+ {
+ emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
+ "be used on next run.");
+ inst->setShouldRebuild(true);
+ inst->setShouldUpdate(true);
+ inst->setShouldUseCustomBaseJar(false);
+ return;
+ }
+ }
+
+ if (!baseJar.exists())
+ {
+ emitFailed("The base jar " + baseJar.filePath() + " does not exist");
+ return;
+ }
+
+ if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
+ {
+ emitFailed("Failed to delete old minecraft.jar");
+ return;
+ }
+
+ // TaskStep(); // STEP 1
+ setStatus(tr("Installing mods: Opening minecraft.jar ..."));
+
+ QuaZip zipOut(runnableJar.filePath());
+ if (!zipOut.open(QuaZip::mdCreate))
+ {
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to open the minecraft.jar for modding");
+ return;
+ }
+ // Files already added to the jar.
+ // These files will be skipped.
+ QSet<QString> addedFiles;
+
+ // Modify the jar
+ setStatus(tr("Installing mods: Adding mod files..."));
+ for (int i = modList->size() - 1; i >= 0; i--)
+ {
+ auto &mod = modList->operator[](i);
+
+ // do not merge disabled mods.
+ if (!mod.enabled())
+ continue;
+
+ if (mod.type() == Mod::MOD_ZIPFILE)
+ {
+ if (!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf))
+ {
+ zipOut.close();
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to add " + mod.filename().fileName() + " to the jar.");
+ return;
+ }
+ }
+ else if (mod.type() == Mod::MOD_SINGLEFILE)
+ {
+ auto filename = mod.filename();
+ if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(),
+ filename.fileName()))
+ {
+ zipOut.close();
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to add " + filename.fileName() + " to the jar");
+ return;
+ }
+ addedFiles.insert(filename.fileName());
+ QLOG_INFO() << "Adding file " << filename.fileName() << " from "
+ << filename.absoluteFilePath();
+ }
+ 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 (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, true, addedFiles))
+ {
+ zipOut.close();
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to add " + filename.fileName() + " to the jar");
+ return;
+ }
+ QLOG_INFO() << "Adding folder " << filename.fileName() << " from "
+ << filename.absoluteFilePath();
+ }
+ }
+
+ if (!MergeZipFiles(&zipOut, baseJar, addedFiles, LegacyUpdate::IgnoreMetainf))
+ {
+ zipOut.close();
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to insert minecraft.jar contents.");
+ return;
+ }
+
+ // Recompress the jar
+ zipOut.close();
+ if (zipOut.getZipError() != 0)
+ {
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to finalize minecraft.jar!");
+ return;
+ }
+ inst->setShouldRebuild(false);
+ // inst->UpdateVersion(true);
+ emitSucceeded();
+ return;
+}
diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h
new file mode 100644
index 00000000..613eb1f9
--- /dev/null
+++ b/logic/LegacyUpdate.h
@@ -0,0 +1,75 @@
+/* Copyright 2013 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 <QObject>
+#include <QList>
+#include <QUrl>
+
+#include "logic/net/NetJob.h"
+#include "logic/tasks/Task.h"
+
+class MinecraftVersion;
+class BaseInstance;
+class QuaZip;
+class Mod;
+
+class LegacyUpdate : public Task
+{
+ Q_OBJECT
+public:
+ explicit LegacyUpdate(BaseInstance *inst, QObject *parent = 0);
+ virtual void executeTask();
+
+private
+slots:
+ void lwjglStart();
+ void lwjglFinished(QNetworkReply *);
+ void lwjglFailed();
+
+ void jarStart();
+ void jarFinished();
+ void jarFailed();
+
+ void extractLwjgl();
+
+ void ModTheJar();
+
+private:
+ enum MetainfAction
+ {
+ KeepMetainf, // the META-INF folder will be added from the merged jar
+ IgnoreMetainf // the META-INF from the merged jar will be ignored
+ };
+ bool MergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
+ MetainfAction metainf);
+
+private:
+
+ std::shared_ptr<QNetworkReply> m_reply;
+
+ // target version, determined during this task
+ // MinecraftVersion *targetVersion;
+ QString lwjglURL;
+ QString lwjglVersion;
+
+ QString lwjglTargetPath;
+ QString lwjglNativesPath;
+
+private:
+ NetJobPtr legacyDownloadJob;
+ BaseInstance *m_inst = nullptr;
+};
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp
new file mode 100644
index 00000000..07fffff3
--- /dev/null
+++ b/logic/LiteLoaderInstaller.cpp
@@ -0,0 +1,102 @@
+/* Copyright 2013 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 "LiteLoaderInstaller.h"
+
+#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+
+QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping;
+
+LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion)
+{
+ if (m_launcherWrapperVersionMapping.isEmpty())
+ {
+ m_launcherWrapperVersionMapping["1.6.2"] = "1.3";
+ m_launcherWrapperVersionMapping["1.6.4"] = "1.8";
+ //m_launcherWrapperVersionMapping["1.7.2"] = "1.8";
+ //m_launcherWrapperVersionMapping["1.7.4"] = "1.8";
+ }
+}
+
+bool LiteLoaderInstaller::canApply() const
+{
+ return m_launcherWrapperVersionMapping.contains(m_mcVersion);
+}
+
+bool LiteLoaderInstaller::apply(std::shared_ptr<OneSixVersion> to)
+{
+ to->externalUpdateStart();
+
+ applyLaunchwrapper(to);
+ applyLiteLoader(to);
+
+ to->mainClass = "net.minecraft.launchwrapper.Launch";
+ if (!to->minecraftArguments.contains(
+ " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"))
+ {
+ to->minecraftArguments.append(
+ " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker");
+ }
+
+ to->externalUpdateFinish();
+ return to->toOriginalFile();
+}
+
+void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr<OneSixVersion> to)
+{
+ const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion];
+
+ QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
+ while (it.hasNext())
+ {
+ it.next();
+ if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:"))
+ {
+ if (it.value()->version() >= intendedVersion)
+ {
+ return;
+ }
+ else
+ {
+ it.remove();
+ }
+ }
+ }
+
+ std::shared_ptr<OneSixLibrary> lib(new OneSixLibrary(
+ "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion]));
+ lib->finalize();
+ to->libraries.prepend(lib);
+}
+
+void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr<OneSixVersion> to)
+{
+ QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
+ while (it.hasNext())
+ {
+ it.next();
+ if (it.value()->rawName().startsWith("com.mumfrey:liteloader:"))
+ {
+ it.remove();
+ }
+ }
+
+ std::shared_ptr<OneSixLibrary> lib(
+ new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion));
+ lib->setBaseUrl("http://dl.liteloader.com/versions/");
+ lib->finalize();
+ to->libraries.prepend(lib);
+}
diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h
new file mode 100644
index 00000000..44b306d6
--- /dev/null
+++ b/logic/LiteLoaderInstaller.h
@@ -0,0 +1,39 @@
+/* Copyright 2013 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 <QString>
+#include <QMap>
+#include <memory>
+
+class OneSixVersion;
+
+class LiteLoaderInstaller
+{
+public:
+ LiteLoaderInstaller(const QString &mcVersion);
+
+ bool canApply() const;
+
+ bool apply(std::shared_ptr<OneSixVersion> to);
+
+private:
+ QString m_mcVersion;
+
+ void applyLaunchwrapper(std::shared_ptr<OneSixVersion> to);
+ void applyLiteLoader(std::shared_ptr<OneSixVersion> to);
+
+ static QMap<QString, QString> m_launcherWrapperVersionMapping;
+};
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
new file mode 100644
index 00000000..9c0a7074
--- /dev/null
+++ b/logic/MinecraftProcess.cpp
@@ -0,0 +1,374 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 "MultiMC.h"
+
+#include "MinecraftProcess.h"
+
+#include <QDataStream>
+#include <QFile>
+#include <QDir>
+#include <QProcessEnvironment>
+#include <QRegularExpression>
+
+#include "BaseInstance.h"
+
+#include "osutils.h"
+#include "pathutils.h"
+#include "cmdutils.h"
+
+#define IBUS "@im=ibus"
+
+// constructor
+MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
+{
+ connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
+ SLOT(finish(int, QProcess::ExitStatus)));
+
+ // prepare the process environment
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+
+#ifdef LINUX
+ // Strip IBus
+ // IBus is a Linux IME framework. For some reason, it breaks MC?
+ if (env.value("XMODIFIERS").contains(IBUS))
+ env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, ""));
+#endif
+
+ // export some infos
+ env.insert("INST_NAME", inst->name());
+ env.insert("INST_ID", inst->id());
+ env.insert("INST_DIR", QDir(inst->instanceRoot()).absolutePath());
+
+ this->setProcessEnvironment(env);
+ m_prepostlaunchprocess.setProcessEnvironment(env);
+
+ // std channels
+ connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr()));
+ connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut()));
+
+ // Log prepost launch command output (can be disabled.)
+ if (m_instance->settings().get("LogPrePostOutput").toBool())
+ {
+ connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError,
+ this, &MinecraftProcess::on_prepost_stdErr);
+ connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput,
+ this, &MinecraftProcess::on_prepost_stdOut);
+ }
+}
+
+void MinecraftProcess::setWorkdir(QString path)
+{
+ QDir mcDir(path);
+ this->setWorkingDirectory(mcDir.absolutePath());
+ m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath());
+}
+
+QString MinecraftProcess::censorPrivateInfo(QString in)
+{
+ if(!m_session)
+ return in;
+
+ if(m_session->session != "-")
+ in.replace(m_session->session, "<SESSION ID>");
+ in.replace(m_session->access_token, "<ACCESS TOKEN>");
+ in.replace(m_session->client_token, "<CLIENT TOKEN>");
+ in.replace(m_session->uuid, "<PROFILE ID>");
+ in.replace(m_session->player_name, "<PROFILE NAME>");
+
+ auto i = m_session->u.properties.begin();
+ while (i != m_session->u.properties.end())
+ {
+ in.replace(i.value(), "<" + i.key().toUpper() + ">");
+ ++i;
+ }
+
+ return in;
+}
+
+// console window
+MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLevel::Enum level)
+{
+ 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("Exception in thread") || line.contains(" at "))
+ level = MessageLevel::Fatal;
+ if (line.contains("[DEBUG]"))
+ level = MessageLevel::Debug;
+ return level;
+}
+
+MessageLevel::Enum MinecraftProcess::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 !![]!
+ else
+ return MessageLevel::Message;
+}
+
+void MinecraftProcess::logOutput(const QStringList &lines,
+ MessageLevel::Enum defaultLevel,
+ bool guessLevel, bool censor)
+{
+ for (int i = 0; i < lines.size(); ++i)
+ logOutput(lines[i], defaultLevel, guessLevel, censor);
+}
+
+void MinecraftProcess::logOutput(QString line,
+ MessageLevel::Enum defaultLevel,
+ bool guessLevel, bool censor)
+{
+ MessageLevel::Enum level = defaultLevel;
+
+ // Level prefix
+ int endmark = line.indexOf("]!");
+ if (line.startsWith("!![") && endmark != -1)
+ {
+ level = getLevel(line.left(endmark).mid(3));
+ line = line.mid(endmark + 2);
+ }
+ // Guess level
+ else if (guessLevel)
+ level = this->guessLevel(line, defaultLevel);
+
+ if (censor)
+ line = censorPrivateInfo(line);
+
+ emit log(line, level);
+}
+
+void MinecraftProcess::on_stdErr()
+{
+ QByteArray data = readAllStandardError();
+ QString str = m_err_leftover + QString::fromLocal8Bit(data);
+
+ QStringList lines = str.split("\n");
+ m_err_leftover = lines.takeLast();
+
+ logOutput(lines, MessageLevel::Error);
+}
+
+void MinecraftProcess::on_stdOut()
+{
+ QByteArray data = readAllStandardOutput();
+ QString str = m_out_leftover + QString::fromLocal8Bit(data);
+
+ QStringList lines = str.split("\n");
+ m_out_leftover = lines.takeLast();
+
+ logOutput(lines);
+}
+
+void MinecraftProcess::on_prepost_stdErr()
+{
+ QByteArray data = m_prepostlaunchprocess.readAllStandardError();
+ QString str = m_err_leftover + QString::fromLocal8Bit(data);
+
+ QStringList lines = str.split("\n");
+ m_err_leftover = lines.takeLast();
+
+ logOutput(lines, MessageLevel::PrePost, false, false);
+}
+
+void MinecraftProcess::on_prepost_stdOut()
+{
+ QByteArray data = m_prepostlaunchprocess.readAllStandardOutput();
+ QString str = m_out_leftover + QString::fromLocal8Bit(data);
+
+ QStringList lines = str.split("\n");
+ m_out_leftover = lines.takeLast();
+
+ logOutput(lines, MessageLevel::PrePost, false, false);
+}
+
+// exit handler
+void MinecraftProcess::finish(int code, ExitStatus status)
+{
+ // Flush console window
+ if (!m_err_leftover.isEmpty())
+ {
+ logOutput(m_err_leftover, MessageLevel::Error);
+ m_err_leftover.clear();
+ }
+ if (!m_out_leftover.isEmpty())
+ {
+ logOutput(m_out_leftover);
+ m_out_leftover.clear();
+ }
+
+ if (!killed)
+ {
+ if (status == NormalExit)
+ {
+ //: Message displayed on instance exit
+ emit log(tr("Minecraft exited with exitcode %1.").arg(code));
+ }
+ else
+ {
+ //: Message displayed on instance crashed
+ emit log(tr("Minecraft crashed with exitcode %1.").arg(code));
+ }
+ }
+ else
+ {
+ //: Message displayed after the instance exits due to kill request
+ emit log(tr("Minecraft was killed by user."), MessageLevel::Error);
+ }
+
+ m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
+
+ // run post-exit
+ QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
+ if (!postlaunch_cmd.isEmpty())
+ {
+ emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
+ m_prepostlaunchprocess.start(postlaunch_cmd);
+ m_prepostlaunchprocess.waitForFinished();
+ // Flush console window
+ if (!m_err_leftover.isEmpty())
+ {
+ logOutput(m_err_leftover, MessageLevel::PrePost);
+ m_err_leftover.clear();
+ }
+ if (!m_out_leftover.isEmpty())
+ {
+ logOutput(m_out_leftover, MessageLevel::PrePost);
+ m_out_leftover.clear();
+ }
+ if (m_prepostlaunchprocess.exitStatus() != NormalExit)
+ {
+ emit log(tr("Post-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
+ MessageLevel::Error);
+ emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
+ m_prepostlaunchprocess.exitStatus());
+ }
+ else
+ emit log(tr("Post-Launch command ran successfully.\n\n"));
+ }
+ m_instance->cleanupAfterRun();
+ emit ended(m_instance, code, status);
+}
+
+void MinecraftProcess::killMinecraft()
+{
+ killed = true;
+ kill();
+}
+
+void MinecraftProcess::launch()
+{
+ emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
+ emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
+
+ QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString();
+ if (!prelaunch_cmd.isEmpty())
+ {
+ // Launch
+ emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd));
+ m_prepostlaunchprocess.start(prelaunch_cmd);
+ // Wait
+ m_prepostlaunchprocess.waitForFinished();
+ // Flush console window
+ if (!m_err_leftover.isEmpty())
+ {
+ logOutput(m_err_leftover, MessageLevel::PrePost);
+ m_err_leftover.clear();
+ }
+ if (!m_out_leftover.isEmpty())
+ {
+ logOutput(m_out_leftover, MessageLevel::PrePost);
+ m_out_leftover.clear();
+ }
+ // Process return values
+ if (m_prepostlaunchprocess.exitStatus() != NormalExit)
+ {
+ emit log(tr("Pre-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
+ MessageLevel::Fatal);
+ m_instance->cleanupAfterRun();
+ emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
+ m_prepostlaunchprocess.exitStatus());
+ return;
+ }
+ else
+ emit log(tr("Pre-Launch command ran successfully.\n\n"));
+ }
+
+ m_instance->setLastLaunch();
+ auto &settings = m_instance->settings();
+
+ //////////// java arguments ////////////
+ QStringList args;
+ {
+ // custom args go first. we want to override them if we have our own here.
+ args.append(m_instance->extraArguments());
+
+ // OSX dock icon and name
+ #ifdef OSX
+ args << "-Xdock:icon=icon.png";
+ args << QString("-Xdock:name=\"%1\"").arg(m_instance->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());
+ args << QString("-XX:PermSize=%1m").arg(settings.get("PermGen").toInt());
+ if(!m_nativeFolder.isEmpty())
+ args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
+ args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
+ }
+
+ QString JavaPath = m_instance->settings().get("JavaPath").toString();
+ emit log("Java path is:\n" + JavaPath + "\n\n");
+ QString allArgs = args.join(", ");
+ emit log("Java Arguments:\n[" + censorPrivateInfo(allArgs) + "]\n\n");
+
+ // instantiate the launcher part
+ start(JavaPath, args);
+ if (!waitForStarted())
+ {
+ //: Error message displayed if instace can't start
+ emit log(tr("Could not launch minecraft!"), MessageLevel::Error);
+ m_instance->cleanupAfterRun();
+ emit launch_failed(m_instance);
+ return;
+ }
+ // send the launch script to the launcher part
+ QByteArray bytes = launchScript.toUtf8();
+ writeData(bytes.constData(), bytes.length());
+}
diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h
new file mode 100644
index 00000000..26214026
--- /dev/null
+++ b/logic/MinecraftProcess.h
@@ -0,0 +1,142 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <QProcess>
+#include <QString>
+#include "BaseInstance.h"
+
+/**
+ * @brief the MessageLevel Enum
+ * defines what level a message is
+ */
+namespace MessageLevel
+{
+enum Enum
+{
+ MultiMC, /**< MultiMC Messages */
+ Debug, /**< Debug Messages */
+ Info, /**< Info Messages */
+ Message, /**< Standard Messages */
+ Warning, /**< Warnings */
+ Error, /**< Errors */
+ Fatal, /**< Fatal Errors */
+ PrePost, /**< Pre/Post Launch command output */
+};
+}
+
+/**
+ * @file data/minecraftprocess.h
+ * @brief The MinecraftProcess class
+ */
+class MinecraftProcess : public QProcess
+{
+ Q_OBJECT
+public:
+ /**
+ * @brief MinecraftProcess constructor
+ * @param inst the Instance pointer to launch
+ */
+ MinecraftProcess(BaseInstance *inst);
+
+ /**
+ * @brief launch minecraft
+ */
+ void launch();
+
+ BaseInstance *instance()
+ {
+ return m_instance;
+ }
+
+ void setWorkdir(QString path);
+
+ void setLaunchScript(QString script)
+ {
+ launchScript = script;
+ }
+
+ void setNativeFolder(QString natives)
+ {
+ m_nativeFolder = natives;
+ }
+
+ void killMinecraft();
+
+ inline void setLogin(AuthSessionPtr session)
+ {
+ m_session = session;
+ }
+
+signals:
+ /**
+ * @brief emitted when Minecraft immediately fails to run
+ */
+ void launch_failed(BaseInstance *);
+
+ /**
+ * @brief emitted when the PreLaunchCommand fails
+ */
+ void prelaunch_failed(BaseInstance *, int code, QProcess::ExitStatus status);
+
+ /**
+ * @brief emitted when the PostLaunchCommand fails
+ */
+ void postlaunch_failed(BaseInstance *, int code, QProcess::ExitStatus status);
+
+ /**
+ * @brief emitted when mc has finished and the PostLaunchCommand was run
+ */
+ void ended(BaseInstance *, int code, QProcess::ExitStatus status);
+
+ /**
+ * @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);
+
+protected:
+ BaseInstance *m_instance = nullptr;
+ QString m_err_leftover;
+ QString m_out_leftover;
+ QProcess m_prepostlaunchprocess;
+ bool killed = false;
+ AuthSessionPtr m_session;
+ QString launchScript;
+ QString m_nativeFolder;
+
+protected
+slots:
+ void finish(int, QProcess::ExitStatus status);
+ void on_stdErr();
+ void on_stdOut();
+ void on_prepost_stdOut();
+ void on_prepost_stdErr();
+ void logOutput(const QStringList &lines,
+ MessageLevel::Enum defaultLevel = MessageLevel::Message,
+ bool guessLevel = true, bool censor = true);
+ void logOutput(QString line,
+ MessageLevel::Enum defaultLevel = MessageLevel::Message,
+ bool guessLevel = true, bool censor = true);
+
+private:
+ QString censorPrivateInfo(QString in);
+ MessageLevel::Enum guessLevel(const QString &message, MessageLevel::Enum defaultLevel);
+ MessageLevel::Enum getLevel(const QString &levelName);
+};
diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h
new file mode 100644
index 00000000..504381a8
--- /dev/null
+++ b/logic/MinecraftVersion.h
@@ -0,0 +1,89 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 "BaseVersion.h"
+#include <QStringList>
+
+struct MinecraftVersion : public BaseVersion
+{
+ /*!
+ * Gets the version's timestamp.
+ * This is primarily used for sorting versions in a list.
+ */
+ qint64 timestamp;
+
+ /// The URL that this version will be downloaded from. maybe.
+ QString download_url;
+
+ /// This version's type. Used internally to identify what kind of version this is.
+ enum VersionType
+ {
+ OneSix,
+ Legacy,
+ Nostalgia
+ } type;
+
+ /// is this the latest version?
+ bool is_latest = false;
+
+ /// is this a snapshot?
+ bool is_snapshot = false;
+
+ QString m_name;
+
+ QString m_descriptor;
+
+ virtual QString descriptor()
+ {
+ return m_descriptor;
+ }
+
+ virtual QString name()
+ {
+ return m_name;
+ }
+
+ virtual QString typeString() const
+ {
+ QStringList pre_final;
+ if (is_latest == true)
+ {
+ pre_final.append("Latest");
+ }
+ switch (type)
+ {
+ case OneSix:
+ pre_final.append("OneSix");
+ break;
+ case Legacy:
+ pre_final.append("Legacy");
+ break;
+ case Nostalgia:
+ pre_final.append("Nostalgia");
+ break;
+
+ default:
+ pre_final.append(QString("Type(%1)").arg(type));
+ break;
+ }
+ if (is_snapshot == true)
+ {
+ pre_final.append("Snapshot");
+ }
+ return pre_final.join(' ');
+ }
+};
diff --git a/logic/Mod.cpp b/logic/Mod.cpp
new file mode 100644
index 00000000..6732446d
--- /dev/null
+++ b/logic/Mod.cpp
@@ -0,0 +1,358 @@
+/* Copyright 2013 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 <QDir>
+#include <QString>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <quazip.h>
+#include <quazipfile.h>
+
+#include "Mod.h"
+#include <pathutils.h>
+#include <inifile.h>
+#include "logger/QsLog.h"
+
+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(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_description = firstObj.value("description").toString();
+ QJsonArray 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");
+ int version = val.toDouble();
+ if (version != 2)
+ {
+ QLOG_ERROR() << "BAD stuff happened to mod json:";
+ QLOG_ERROR() << contents;
+ return;
+ }
+ auto 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)
+ {
+ QLOG_DEBUG() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath();
+ success = QFile::copy(with.m_file.filePath(), m_file.filePath());
+ }
+ if (t == MOD_FOLDER)
+ {
+ success = copyPath(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)
+ {
+ 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/logic/Mod.h b/logic/Mod.h
new file mode 100644
index 00000000..2eb2b97a
--- /dev/null
+++ b/logic/Mod.h
@@ -0,0 +1,129 @@
+/* Copyright 2013 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 <QFileInfo>
+
+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
+ {
+ 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_description;
+ QString m_authors;
+ QString m_credits;
+
+ ModType m_type;
+};
diff --git a/logic/ModList.cpp b/logic/ModList.cpp
new file mode 100644
index 00000000..499623bf
--- /dev/null
+++ b/logic/ModList.cpp
@@ -0,0 +1,599 @@
+/* Copyright 2013 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 "LegacyInstance.h"
+#include <pathutils.h>
+#include <QMimeData>
+#include <QUrl>
+#include <QUuid>
+#include <QString>
+#include <QFileSystemWatcher>
+#include "logger/QsLog.h"
+
+ModList::ModList(const QString &dir, const QString &list_file)
+ : QAbstractListModel(), m_dir(dir), m_list_file(list_file)
+{
+ 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()
+{
+ is_watching = m_watcher->addPath(m_dir.absolutePath());
+ if (is_watching)
+ {
+ QLOG_INFO() << "Started watching " << m_dir.absolutePath();
+ }
+ else
+ {
+ QLOG_INFO() << "Failed to start watching " << m_dir.absolutePath();
+ }
+}
+
+void ModList::stopWatching()
+{
+ is_watching = !m_watcher->removePath(m_dir.absolutePath());
+ if (!is_watching)
+ {
+ QLOG_INFO() << "Stopped watching " << m_dir.absolutePath();
+ }
+ else
+ {
+ QLOG_INFO() << "Failed to stop watching " << m_dir.absolutePath();
+ }
+}
+
+bool ModList::update()
+{
+ if (!isValid())
+ return false;
+
+ QList<Mod> orderedMods;
+ QList<Mod> 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));
+ }
+ std::sort(newMods.begin(), newMods.end(), [](const Mod & left, const Mod & right)
+ { return left.name().localeAwareCompare(right.name()) <= 0; });
+ 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())
+ {
+ QLOG_INFO() << "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 QFileInfo &filename, int index)
+{
+ if (!filename.exists() || !filename.isReadable() || index < 0)
+ {
+ return false;
+ }
+ Mod m(filename);
+ 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();
+ emit changed();
+ 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 = PathCombine(m_dir.path(), filename.fileName());
+ if (!QFile::copy(filename.filePath(), newpath))
+ return false;
+ m.repath(newpath);
+ beginInsertRows(QModelIndex(), index, index);
+ mods.insert(index, m);
+ endInsertRows();
+ saveListFile();
+ emit changed();
+ return true;
+ }
+ else if (type == Mod::MOD_FOLDER)
+ {
+
+ QString from = filename.filePath();
+ QString to = PathCombine(m_dir.path(), filename.fileName());
+ if (!copyPath(from, to))
+ return false;
+ m.repath(to);
+ beginInsertRows(QModelIndex(), index, index);
+ mods.insert(index, m);
+ endInsertRows();
+ saveListFile();
+ emit changed();
+ 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 (index.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 (index.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 QString("Name");
+ case VersionColumn:
+ return QString("Version");
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case ActiveColumn:
+ return "Is the mod enabled?";
+ case NameColumn:
+ return "The name of the mod.";
+ case VersionColumn:
+ return "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;
+ QLOG_INFO() << "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);
+ QLOG_INFO() << "installing: " << filename;
+ // if there is no ordering, re-sort the list
+ if (m_list_file.isEmpty())
+ {
+ beginResetModel();
+ std::sort(mods.begin(), mods.end(), [](const Mod & left, const Mod & right)
+ { return left.name().localeAwareCompare(right.name()) <= 0; });
+ 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();
+ QLOG_INFO() << "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/logic/ModList.h b/logic/ModList.h
new file mode 100644
index 00000000..0d6507fb
--- /dev/null
+++ b/logic/ModList.h
@@ -0,0 +1,152 @@
+/* Copyright 2013 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 <QList>
+#include <QString>
+#include <QDir>
+#include <QAbstractListModel>
+
+#include "logic/Mod.h"
+
+class LegacyInstance;
+class BaseInstance;
+class QFileSystemWatcher;
+
+/**
+ * A legacy mod list.
+ * Backed by a folder.
+ */
+class 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 QFileInfo &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;
+ }
+
+private:
+ struct OrderItem
+ {
+ QString id;
+ bool enabled = false;
+ };
+ typedef QList<OrderItem> 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<Mod> mods;
+};
diff --git a/logic/NagUtils.cpp b/logic/NagUtils.cpp
new file mode 100644
index 00000000..c963a98a
--- /dev/null
+++ b/logic/NagUtils.cpp
@@ -0,0 +1,38 @@
+/* Copyright 2013 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 "logic/NagUtils.h"
+#include "gui/dialogs/CustomMessageBox.h"
+
+namespace NagUtils
+{
+void checkJVMArgs(QString jvmargs, QWidget *parent)
+{
+ if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]")))
+ {
+ CustomMessageBox::selectable(
+ parent, QObject::tr("JVM arguments warning"),
+ QObject::tr("You tried to manually set a JVM memory option (using "
+ " \"-XX:PermSize\", \"-Xmx\" or \"-Xms\") - there"
+ " are dedicated boxes for these in the settings (Java"
+ " tab, in the Memory group at the top).\n"
+ "Your manual settings will be overridden by the"
+ " dedicated options.\n"
+ "This message will be displayed until you remove them"
+ " from the JVM arguments."),
+ QMessageBox::Warning)->exec();
+ }
+}
+}
diff --git a/logic/NagUtils.h b/logic/NagUtils.h
new file mode 100644
index 00000000..9564a2b1
--- /dev/null
+++ b/logic/NagUtils.h
@@ -0,0 +1,23 @@
+/* Copyright 2013 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 <QWidget>
+
+namespace NagUtils
+{
+void checkJVMArgs(QString args, QWidget *parent);
+}
diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp
new file mode 100644
index 00000000..2e23ee71
--- /dev/null
+++ b/logic/NostalgiaInstance.cpp
@@ -0,0 +1,32 @@
+/* Copyright 2013 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 "NostalgiaInstance.h"
+
+NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent)
+ : OneSixInstance(rootDir, settings, parent)
+{
+}
+
+QString NostalgiaInstance::getStatusbarDescription()
+{
+ return "Nostalgia : " + intendedVersionId();
+}
+
+bool NostalgiaInstance::menuActionEnabled(QString action_name) const
+{
+ return false;
+}
diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h
new file mode 100644
index 00000000..a26f7f0a
--- /dev/null
+++ b/logic/NostalgiaInstance.h
@@ -0,0 +1,28 @@
+/* Copyright 2013 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 "OneSixInstance.h"
+
+class NostalgiaInstance : public OneSixInstance
+{
+ Q_OBJECT
+public:
+ explicit NostalgiaInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+ virtual QString getStatusbarDescription();
+ virtual bool menuActionEnabled(QString action_name) const;
+};
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
new file mode 100644
index 00000000..f8e695b9
--- /dev/null
+++ b/logic/OneSixFTBInstance.cpp
@@ -0,0 +1,125 @@
+#include "OneSixFTBInstance.h"
+
+#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "tasks/SequentialTask.h"
+#include "ForgeInstaller.h"
+#include "lists/ForgeVersionList.h"
+#include "MultiMC.h"
+
+class OneSixFTBInstanceForge : public Task
+{
+ Q_OBJECT
+public:
+ explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) :
+ Task(parent), instance(inst), version("Forge " + version)
+ {
+ }
+
+ void executeTask()
+ {
+ for (int i = 0; i < MMC->forgelist()->count(); ++i)
+ {
+ if (MMC->forgelist()->at(i)->name() == version)
+ {
+ forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i));
+ break;
+ }
+ }
+ if (!forgeVersion)
+ {
+ emitFailed(QString("Couldn't find forge version ") + version );
+ return;
+ }
+ entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
+ if (entry->stale)
+ {
+ setStatus(tr("Downloading Forge..."));
+ fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
+ connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);});
+ connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge);
+ connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); });
+ fjob->start();
+ }
+ else
+ {
+ installForge();
+ }
+ }
+
+private
+slots:
+ void installForge()
+ {
+ setStatus(tr("Installing Forge..."));
+ QString forgePath = entry->getFullPath();
+ ForgeInstaller forge(forgePath, forgeVersion->universal_url);
+ if (!instance->reloadFullVersion())
+ {
+ emitFailed(tr("Couldn't load the version config"));
+ return;
+ }
+ instance->revertCustomVersion();
+ instance->customizeVersion();
+ auto version = instance->getFullVersion();
+ if (!forge.apply(version))
+ {
+ emitFailed(tr("Couldn't install Forge"));
+ return;
+ }
+ emitSucceeded();
+ }
+
+private:
+ OneSixFTBInstance *instance;
+ QString version;
+ ForgeVersionPtr forgeVersion;
+ MetaEntryPtr entry;
+ NetJob *fjob;
+};
+
+OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
+ OneSixInstance(rootDir, settings, parent)
+{
+ QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json"));
+ if (f.open(QFile::ReadOnly))
+ {
+ QString data = QString::fromUtf8(f.readAll());
+ QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data);
+ m_forge.reset(new OneSixLibrary(match.captured()));
+ m_forge->finalize();
+ }
+}
+
+QString OneSixFTBInstance::id() const
+{
+ return "FTB/" + BaseInstance::id();
+}
+
+QString OneSixFTBInstance::getStatusbarDescription()
+{
+ return "OneSix FTB: " + intendedVersionId();
+}
+bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
+{
+ return false;
+}
+
+std::shared_ptr<Task> OneSixFTBInstance::doUpdate()
+{
+ std::shared_ptr<SequentialTask> task;
+ task.reset(new SequentialTask(this));
+ if (!MMC->forgelist()->isLoaded())
+ {
+ task->addTask(std::shared_ptr<Task>(MMC->forgelist()->getLoadTask()));
+ }
+ task->addTask(OneSixInstance::doUpdate());
+ task->addTask(std::shared_ptr<Task>(new OneSixFTBInstanceForge(m_forge->version(), this, this)));
+ //FIXME: yes. this may appear dumb. but the previous step can change the list, so we do it all again.
+ //TODO: Add a graph task. Construct graphs of tasks so we may capture the logic properly.
+ task->addTask(OneSixInstance::doUpdate());
+ return task;
+}
+
+#include "OneSixFTBInstance.moc"
diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h
new file mode 100644
index 00000000..bc543aeb
--- /dev/null
+++ b/logic/OneSixFTBInstance.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "OneSixInstance.h"
+
+class OneSixLibrary;
+
+class OneSixFTBInstance : public OneSixInstance
+{
+ Q_OBJECT
+public:
+ explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+ virtual QString getStatusbarDescription();
+ virtual bool menuActionEnabled(QString action_name) const;
+
+ virtual std::shared_ptr<Task> doUpdate() override;
+
+ virtual QString id() const;
+
+private:
+ std::shared_ptr<OneSixLibrary> m_forge;
+};
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
new file mode 100644
index 00000000..67649f77
--- /dev/null
+++ b/logic/OneSixInstance.cpp
@@ -0,0 +1,406 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "OneSixInstance.h"
+#include "OneSixInstance_p.h"
+#include "OneSixUpdate.h"
+#include "MinecraftProcess.h"
+#include "OneSixVersion.h"
+#include "JavaChecker.h"
+#include "logic/icons/IconList.h"
+
+#include <setting.h>
+#include <pathutils.h>
+#include <cmdutils.h>
+#include <JlCompress.h>
+#include "gui/dialogs/OneSixModEditDialog.h"
+#include "logger/QsLog.h"
+#include "logic/assets/AssetsUtils.h"
+#include <QIcon>
+
+OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj,
+ QObject *parent)
+ : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent)
+{
+ I_D(OneSixInstance);
+ d->m_settings->registerSetting("IntendedVersion", "");
+ d->m_settings->registerSetting("ShouldUpdate", false);
+ reloadFullVersion();
+}
+
+std::shared_ptr<Task> OneSixInstance::doUpdate()
+{
+ return std::shared_ptr<Task>(new OneSixUpdate(this));
+}
+
+QString replaceTokensIn(QString text, QMap<QString, QString> with)
+{
+ QString result;
+ QRegExp token_regexp("\\$\\{(.+)\\}");
+ token_regexp.setMinimal(true);
+ QStringList list;
+ int tail = 0;
+ int head = 0;
+ while ((head = token_regexp.indexIn(text, head)) != -1)
+ {
+ result.append(text.mid(tail, head - tail));
+ QString key = token_regexp.cap(1);
+ auto iter = with.find(key);
+ if (iter != with.end())
+ {
+ result.append(*iter);
+ }
+ head += token_regexp.matchedLength();
+ tail = head;
+ }
+ result.append(text.mid(tail));
+ return result;
+}
+
+QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
+{
+ QDir assetsDir = QDir("assets/");
+ QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
+ QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects"));
+ QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual"));
+
+ QString indexPath = PathCombine(indexDir.path(), version->assets + ".json");
+ QFile indexFile(indexPath);
+ QDir virtualRoot(PathCombine(virtualDir.path(), version->assets));
+
+ if (!indexFile.exists())
+ {
+ QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets";
+ return virtualRoot;
+ }
+
+ QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path()
+ << objectDir.path() << virtualDir.path() << virtualRoot.path();
+
+ AssetsIndex index;
+ bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index);
+
+ if (loadAssetsIndex && index.isVirtual)
+ {
+ QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path();
+
+ for (QString map : index.objects.keys())
+ {
+ AssetObject asset_object = index.objects.value(map);
+ QString target_path = PathCombine(virtualRoot.path(), map);
+ QFile target(target_path);
+
+ QString tlk = asset_object.hash.left(2);
+
+ QString original_path =
+ PathCombine(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();
+ // QLOG_DEBUG() << target_dir;
+ if (!target_dir.exists())
+ QDir("").mkpath(target_dir.path());
+
+ bool couldCopy = original.copy(target_path);
+ QLOG_DEBUG() << " Copying" << original_path << "to" << target_path
+ << QString::number(couldCopy); // << original.errorString();
+ }
+ }
+
+ // TODO: Write last used time to virtualRoot/.lastused
+ }
+
+ return virtualRoot;
+}
+
+QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
+{
+ I_D(OneSixInstance);
+ auto version = d->version;
+ QString args_pattern = version->minecraftArguments;
+
+ QMap<QString, QString> token_mapping;
+ // yggdrasil!
+ token_mapping["auth_username"] = session->username;
+ token_mapping["auth_session"] = session->session;
+ token_mapping["auth_access_token"] = session->access_token;
+ token_mapping["auth_player_name"] = session->player_name;
+ token_mapping["auth_uuid"] = session->uuid;
+
+ // these do nothing and are stupid.
+ token_mapping["profile_name"] = name();
+ token_mapping["version_name"] = version->id;
+
+ QString absRootDir = QDir(minecraftRoot()).absolutePath();
+ token_mapping["game_directory"] = absRootDir;
+ QString absAssetsDir = QDir("assets/").absolutePath();
+ token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath();
+
+ token_mapping["user_properties"] = session->serializeUserProperties();
+ token_mapping["user_type"] = session->user_type;
+ // 1.7.3+ assets tokens
+ token_mapping["assets_root"] = absAssetsDir;
+ token_mapping["assets_index_name"] = version->assets;
+
+ QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
+ for (int i = 0; i < parts.length(); i++)
+ {
+ parts[i] = replaceTokensIn(parts[i], token_mapping);
+ }
+ return parts;
+}
+
+MinecraftProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session)
+{
+ I_D(OneSixInstance);
+
+ QIcon icon = MMC->icons()->getIcon(iconKey());
+ auto pixmap = icon.pixmap(128, 128);
+ pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG");
+
+ auto version = d->version;
+ if (!version)
+ return nullptr;
+ QString launchScript;
+ {
+ auto libs = version->getActiveNormalLibs();
+ for (auto lib : libs)
+ {
+ QFileInfo fi(QString("libraries/") + lib->storagePath());
+ launchScript += "cp " + fi.absoluteFilePath() + "\n";
+ }
+ QString targetstr = "versions/" + version->id + "/" + version->id + ".jar";
+ QFileInfo fi(targetstr);
+ launchScript += "cp " + fi.absoluteFilePath() + "\n";
+ }
+ launchScript += "mainClass " + version->mainClass + "\n";
+
+ for (auto param : processMinecraftArgs(session))
+ {
+ launchScript += "param " + param + "\n";
+ }
+
+ // Set the width and height for 1.6 instances
+ bool maximize = settings().get("LaunchMaximized").toBool();
+ if (maximize)
+ {
+ // this is probably a BAD idea
+ // launchScript += "param --fullscreen\n";
+ }
+ else
+ {
+ launchScript +=
+ "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n";
+ launchScript +=
+ "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n";
+ }
+ QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
+ launchScript += "windowTitle " + windowTitle() + "\n";
+ for(auto native: version->getActiveNativeLibs())
+ {
+ QFileInfo finfo(PathCombine("libraries", native->storagePath()));
+ launchScript += "ext " + finfo.absoluteFilePath() + "\n";
+ }
+ launchScript += "natives " + natives_dir.absolutePath() + "\n";
+ launchScript += "launch onesix\n";
+
+ // create the process and set its parameters
+ MinecraftProcess *proc = new MinecraftProcess(this);
+ proc->setWorkdir(minecraftRoot());
+ proc->setLaunchScript(launchScript);
+ // proc->setNativeFolder(natives_dir.absolutePath());
+ return proc;
+}
+
+void OneSixInstance::cleanupAfterRun()
+{
+ QString target_dir = PathCombine(instanceRoot(), "natives/");
+ QDir dir(target_dir);
+ dir.removeRecursively();
+}
+
+std::shared_ptr<ModList> OneSixInstance::loaderModList()
+{
+ I_D(OneSixInstance);
+ if (!d->loader_mod_list)
+ {
+ d->loader_mod_list.reset(new ModList(loaderModsDir()));
+ }
+ d->loader_mod_list->update();
+ return d->loader_mod_list;
+}
+
+std::shared_ptr<ModList> OneSixInstance::resourcePackList()
+{
+ I_D(OneSixInstance);
+ if (!d->resource_pack_list)
+ {
+ d->resource_pack_list.reset(new ModList(resourcePacksDir()));
+ }
+ d->resource_pack_list->update();
+ return d->resource_pack_list;
+}
+
+QDialog *OneSixInstance::createModEditDialog(QWidget *parent)
+{
+ return new OneSixModEditDialog(this, parent);
+}
+
+bool OneSixInstance::setIntendedVersionId(QString version)
+{
+ settings().set("IntendedVersion", version);
+ setShouldUpdate(true);
+ auto pathCustom = PathCombine(instanceRoot(), "custom.json");
+ auto pathOrig = PathCombine(instanceRoot(), "version.json");
+ QFile::remove(pathCustom);
+ QFile::remove(pathOrig);
+ reloadFullVersion();
+ return true;
+}
+
+QString OneSixInstance::intendedVersionId() const
+{
+ return settings().get("IntendedVersion").toString();
+}
+
+void OneSixInstance::setShouldUpdate(bool val)
+{
+ settings().set("ShouldUpdate", val);
+}
+
+bool OneSixInstance::shouldUpdate() const
+{
+ QVariant var = settings().get("ShouldUpdate");
+ if (!var.isValid() || var.toBool() == false)
+ {
+ return intendedVersionId() != currentVersionId();
+ }
+ return true;
+}
+
+bool OneSixInstance::versionIsCustom()
+{
+ QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
+ QFile versionfile(verpath_custom);
+ return versionfile.exists();
+}
+
+QString OneSixInstance::currentVersionId() const
+{
+ return intendedVersionId();
+}
+
+bool OneSixInstance::customizeVersion()
+{
+ if (!versionIsCustom())
+ {
+ auto pathCustom = PathCombine(instanceRoot(), "custom.json");
+ auto pathOrig = PathCombine(instanceRoot(), "version.json");
+ QFile::copy(pathOrig, pathCustom);
+ return reloadFullVersion();
+ }
+ else
+ return true;
+}
+
+bool OneSixInstance::revertCustomVersion()
+{
+ if (versionIsCustom())
+ {
+ auto path = PathCombine(instanceRoot(), "custom.json");
+ QFile::remove(path);
+ return reloadFullVersion();
+ }
+ else
+ return true;
+}
+
+bool OneSixInstance::reloadFullVersion()
+{
+ I_D(OneSixInstance);
+
+ QString verpath = PathCombine(instanceRoot(), "version.json");
+ {
+ QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
+ QFile versionfile(verpath_custom);
+ if (versionfile.exists())
+ verpath = verpath_custom;
+ }
+
+ auto version = OneSixVersion::fromFile(verpath);
+ if (version)
+ {
+ d->version = version;
+ return true;
+ }
+ else
+ {
+ d->version.reset();
+ return false;
+ }
+}
+
+std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion()
+{
+ I_D(OneSixInstance);
+ return d->version;
+}
+
+QString OneSixInstance::defaultBaseJar() const
+{
+ return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
+}
+
+QString OneSixInstance::defaultCustomBaseJar() const
+{
+ return PathCombine(instanceRoot(), "custom.jar");
+}
+
+bool OneSixInstance::menuActionEnabled(QString action_name) const
+{
+ if (action_name == "actionChangeInstLWJGLVersion")
+ return false;
+ return true;
+}
+
+QString OneSixInstance::getStatusbarDescription()
+{
+ QString descr = "One Six : " + intendedVersionId();
+ if (versionIsCustom())
+ {
+ descr + " (custom)";
+ }
+ return descr;
+}
+
+QString OneSixInstance::loaderModsDir() const
+{
+ return PathCombine(minecraftRoot(), "mods");
+}
+
+QString OneSixInstance::resourcePacksDir() const
+{
+ return PathCombine(minecraftRoot(), "resourcepacks");
+}
+
+QString OneSixInstance::instanceConfigFolder() const
+{
+ return PathCombine(minecraftRoot(), "config");
+}
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
new file mode 100644
index 00000000..c159723b
--- /dev/null
+++ b/logic/OneSixInstance.h
@@ -0,0 +1,78 @@
+/* Copyright 2013 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 <QStringList>
+#include <QDir>
+
+#include "BaseInstance.h"
+
+class OneSixVersion;
+class Task;
+class ModList;
+
+class OneSixInstance : public BaseInstance
+{
+ Q_OBJECT
+public:
+ explicit OneSixInstance(const QString &rootDir, SettingsObject *settings,
+ QObject *parent = 0);
+
+ ////// Mod Lists //////
+ std::shared_ptr<ModList> loaderModList();
+ std::shared_ptr<ModList> resourcePackList();
+
+ ////// Directories //////
+ QString resourcePacksDir() const;
+ QString loaderModsDir() const;
+ virtual QString instanceConfigFolder() const override;
+
+ virtual std::shared_ptr<Task> doUpdate() override;
+ virtual MinecraftProcess *prepareForLaunch(AuthSessionPtr session) override;
+
+ virtual void cleanupAfterRun() override;
+
+ virtual QString intendedVersionId() const override;
+ virtual bool setIntendedVersionId(QString version) override;
+
+ virtual QString currentVersionId() const override;
+
+ virtual bool shouldUpdate() const override;
+ virtual void setShouldUpdate(bool val) override;
+
+ virtual QDialog *createModEditDialog(QWidget *parent) override;
+
+ /// reload the full version json file. return true on success!
+ bool reloadFullVersion();
+ /// get the current full version info
+ std::shared_ptr<OneSixVersion> getFullVersion();
+ /// revert the current custom version back to base
+ bool revertCustomVersion();
+ /// customize the current base version
+ bool customizeVersion();
+ /// is the current version original, or custom?
+ virtual bool versionIsCustom() override;
+
+ virtual QString defaultBaseJar() const override;
+ virtual QString defaultCustomBaseJar() const override;
+
+ virtual bool menuActionEnabled(QString action_name) const override;
+ virtual QString getStatusbarDescription() override;
+
+private:
+ QStringList processMinecraftArgs(AuthSessionPtr account);
+ QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
+};
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
new file mode 100644
index 00000000..6b7ea431
--- /dev/null
+++ b/logic/OneSixInstance_p.h
@@ -0,0 +1,30 @@
+/* Copyright 2013 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 <memory>
+
+#include "logic/BaseInstance_p.h"
+#include "logic/OneSixVersion.h"
+#include "logic/OneSixLibrary.h"
+#include "logic/ModList.h"
+
+struct OneSixInstancePrivate : public BaseInstancePrivate
+{
+ std::shared_ptr<OneSixVersion> version;
+ std::shared_ptr<ModList> loader_mod_list;
+ std::shared_ptr<ModList> resource_pack_list;
+}; \ No newline at end of file
diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp
new file mode 100644
index 00000000..7b80d5e7
--- /dev/null
+++ b/logic/OneSixLibrary.cpp
@@ -0,0 +1,268 @@
+/* Copyright 2013 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 <QJsonArray>
+
+#include "OneSixLibrary.h"
+#include "OneSixRule.h"
+#include "OpSys.h"
+#include "logic/net/URLConstants.h"
+#include <pathutils.h>
+#include <JlCompress.h>
+#include "logger/QsLog.h"
+
+void OneSixLibrary::finalize()
+{
+ QStringList parts = m_name.split(':');
+ QString relative = parts[0];
+ relative.replace('.', '/');
+ relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2];
+
+ if (!m_is_native)
+ relative += ".jar";
+ else
+ {
+ if (m_native_suffixes.contains(currentSystem))
+ {
+ relative += "-" + m_native_suffixes[currentSystem] + ".jar";
+ }
+ else
+ {
+ // really, bad.
+ relative += ".jar";
+ }
+ }
+
+ m_decentname = parts[1];
+ m_decentversion = parts[2];
+ m_storage_path = relative;
+ m_download_url = m_base_url + relative;
+
+ if (m_rules.empty())
+ {
+ m_is_active = true;
+ }
+ else
+ {
+ RuleAction result = Disallow;
+ for (auto rule : m_rules)
+ {
+ RuleAction temp = rule->apply(this);
+ if (temp != Defer)
+ result = temp;
+ }
+ m_is_active = (result == Allow);
+ }
+ if (m_is_native)
+ {
+ m_is_active = m_is_active && m_native_suffixes.contains(currentSystem);
+ m_decenttype = "Native";
+ }
+ else
+ {
+ m_decenttype = "Java";
+ }
+}
+
+void OneSixLibrary::setName(QString name)
+{
+ m_name = name;
+}
+void OneSixLibrary::setBaseUrl(QString base_url)
+{
+ m_base_url = base_url;
+}
+void OneSixLibrary::setIsNative()
+{
+ m_is_native = true;
+}
+void OneSixLibrary::addNative(OpSys os, QString suffix)
+{
+ m_is_native = true;
+ m_native_suffixes[os] = suffix;
+}
+void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
+{
+ m_rules = rules;
+}
+bool OneSixLibrary::isActive()
+{
+ return m_is_active;
+}
+bool OneSixLibrary::isNative()
+{
+ return m_is_native;
+}
+QString OneSixLibrary::downloadUrl()
+{
+ if (m_absolute_url.size())
+ return m_absolute_url;
+ return m_download_url;
+}
+QString OneSixLibrary::storagePath()
+{
+ return m_storage_path;
+}
+
+void OneSixLibrary::setAbsoluteUrl(QString absolute_url)
+{
+ m_absolute_url = absolute_url;
+}
+
+QString OneSixLibrary::absoluteUrl()
+{
+ return m_absolute_url;
+}
+
+void OneSixLibrary::setHint(QString hint)
+{
+ m_hint = hint;
+}
+
+QString OneSixLibrary::hint()
+{
+ return m_hint;
+}
+
+bool OneSixLibrary::filesExist()
+{
+ QString storage = storagePath();
+ if (storage.contains("${arch}"))
+ {
+ QString cooked_storage = storage;
+ cooked_storage.replace("${arch}", "32");
+ QFileInfo info32(PathCombine("libraries", cooked_storage));
+ if (!info32.exists())
+ {
+ return false;
+ }
+ cooked_storage = storage;
+ cooked_storage.replace("${arch}", "64");
+ QFileInfo info64(PathCombine("libraries", cooked_storage));
+ if (!info64.exists())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ QFileInfo info(PathCombine("libraries", storage));
+ if (!info.exists())
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool OneSixLibrary::extractTo(QString target_dir)
+{
+ QString storage = storagePath();
+ if (storage.contains("${arch}"))
+ {
+ QString cooked_storage = storage;
+ cooked_storage.replace("${arch}", "32");
+ QString origin = PathCombine("libraries", cooked_storage);
+ QString target_dir_cooked = PathCombine(target_dir, "32");
+ if(!ensureFolderPathExists(target_dir_cooked))
+ {
+ QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
+ return false;
+ }
+ if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes)
+ .isEmpty())
+ {
+ QLOG_ERROR() << "Couldn't extract " + origin;
+ return false;
+ }
+ cooked_storage = storage;
+ cooked_storage.replace("${arch}", "64");
+ origin = PathCombine("libraries", cooked_storage);
+ target_dir_cooked = PathCombine(target_dir, "64");
+ if(!ensureFolderPathExists(target_dir_cooked))
+ {
+ QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
+ return false;
+ }
+ if (JlCompress::extractWithExceptions(origin, target_dir_cooked, extract_excludes)
+ .isEmpty())
+ {
+ QLOG_ERROR() << "Couldn't extract " + origin;
+ return false;
+ }
+ }
+ else
+ {
+ if(!ensureFolderPathExists(target_dir))
+ {
+ QLOG_ERROR() << "Couldn't create folder " + target_dir;
+ return false;
+ }
+ QString path = PathCombine("libraries", storage);
+ if (JlCompress::extractWithExceptions(path, target_dir, extract_excludes).isEmpty())
+ {
+ QLOG_ERROR() << "Couldn't extract " + path;
+ return false;
+ }
+ }
+ return true;
+}
+
+QJsonObject OneSixLibrary::toJson()
+{
+ QJsonObject libRoot;
+ libRoot.insert("name", m_name);
+ if (m_absolute_url.size())
+ libRoot.insert("MMC-absoluteUrl", m_absolute_url);
+ if (m_hint.size())
+ libRoot.insert("MMC-hint", m_hint);
+ if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
+ m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
+ m_base_url != "https://" + URLConstants::LIBRARY_BASE)
+ libRoot.insert("url", m_base_url);
+ if (isNative() && m_native_suffixes.size())
+ {
+ QJsonObject nativeList;
+ auto iter = m_native_suffixes.begin();
+ while (iter != m_native_suffixes.end())
+ {
+ nativeList.insert(OpSys_toString(iter.key()), iter.value());
+ iter++;
+ }
+ libRoot.insert("natives", nativeList);
+ }
+ if (isNative() && extract_excludes.size())
+ {
+ QJsonArray excludes;
+ QJsonObject extract;
+ for (auto exclude : extract_excludes)
+ {
+ excludes.append(exclude);
+ }
+ extract.insert("exclude", excludes);
+ libRoot.insert("extract", extract);
+ }
+ if (m_rules.size())
+ {
+ QJsonArray allRules;
+ for (auto &rule : m_rules)
+ {
+ QJsonObject ruleObj = rule->toJson();
+ allRules.append(ruleObj);
+ }
+ libRoot.insert("rules", allRules);
+ }
+ return libRoot;
+}
diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h
new file mode 100644
index 00000000..227cdbef
--- /dev/null
+++ b/logic/OneSixLibrary.h
@@ -0,0 +1,132 @@
+/* Copyright 2013 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 <QString>
+#include <QStringList>
+#include <QMap>
+#include <QJsonObject>
+#include <memory>
+
+#include "logic/net/URLConstants.h"
+#include "OpSys.h"
+
+class Rule;
+
+class OneSixLibrary
+{
+private:
+ // basic values used internally (so far)
+ QString m_name;
+ QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
+ QList<std::shared_ptr<Rule>> m_rules;
+
+ // custom values
+ /// absolute URL. takes precedence over m_download_path, if defined
+ QString m_absolute_url;
+ /// download hint - how to actually get the library
+ QString m_hint;
+
+ // derived values used for real things
+ /// a decent name fit for display
+ QString m_decentname;
+ /// a decent version fit for display
+ QString m_decentversion;
+ /// a decent type fit for display
+ QString m_decenttype;
+ /// where to store the lib locally
+ QString m_storage_path;
+ /// where to download the lib from
+ QString m_download_url;
+ /// is this lib actually active on the current OS?
+ bool m_is_active = false;
+ /// is the library a native?
+ bool m_is_native = false;
+ /// native suffixes per OS
+ QMap<OpSys, QString> m_native_suffixes;
+
+public:
+ QStringList extract_excludes;
+
+public:
+ /// Constructor
+ OneSixLibrary(QString name)
+ {
+ m_name = name;
+ }
+
+ /// Returns the raw name field
+ QString rawName() const
+ {
+ return m_name;
+ }
+
+ QJsonObject toJson();
+
+ /**
+ * finalize the library, processing the input values into derived values and state
+ *
+ * This SHALL be called after all the values are parsed or after any further change.
+ */
+ void finalize();
+
+ /// Set the library composite name
+ void setName(QString name);
+ /// get a decent-looking name
+ QString name()
+ {
+ return m_decentname;
+ }
+ /// get a decent-looking version
+ QString version()
+ {
+ return m_decentversion;
+ }
+ /// what kind of library is it? (for display)
+ QString type()
+ {
+ return m_decenttype;
+ }
+ /// Set the url base for downloads
+ void setBaseUrl(QString base_url);
+
+ /// Call this to mark the library as 'native' (it's a zip archive with DLLs)
+ void setIsNative();
+ /// Attach a name suffix to the specified OS native
+ void addNative(OpSys os, QString suffix);
+ /// Set the load rules
+ void setRules(QList<std::shared_ptr<Rule>> rules);
+
+ /// Returns true if the library should be loaded (or extracted, in case of natives)
+ bool isActive();
+ /// Returns true if the library is native
+ bool isNative();
+ /// Get the URL to download the library from
+ QString downloadUrl();
+ /// Get the relative path where the library should be saved
+ QString storagePath();
+
+ /// set an absolute URL for the library. This is an MMC extension.
+ void setAbsoluteUrl(QString absolute_url);
+ QString absoluteUrl();
+
+ /// set a hint about how to treat the library. This is an MMC extension.
+ void setHint(QString hint);
+ QString hint();
+
+ bool extractTo(QString target_dir);
+ bool filesExist();
+};
diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp
new file mode 100644
index 00000000..392b1dd1
--- /dev/null
+++ b/logic/OneSixRule.cpp
@@ -0,0 +1,89 @@
+/* Copyright 2013 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 <QJsonObject>
+#include <QJsonArray>
+
+#include "OneSixRule.h"
+
+QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules)
+{
+ QList<std::shared_ptr<Rule>> rules;
+ auto rulesVal = objectWithRules.value("rules");
+ if (!rulesVal.isArray())
+ return rules;
+
+ QJsonArray ruleList = rulesVal.toArray();
+ for (auto ruleVal : ruleList)
+ {
+ std::shared_ptr<Rule> 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));
+ osObj.insert("version", m_version_regexp);
+ }
+ ruleObj.insert("os", osObj);
+ return ruleObj;
+}
+
+RuleAction RuleAction_fromString(QString name)
+{
+ if (name == "allow")
+ return Allow;
+ if (name == "disallow")
+ return Disallow;
+ return Defer;
+} \ No newline at end of file
diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h
new file mode 100644
index 00000000..5a13cbd9
--- /dev/null
+++ b/logic/OneSixRule.h
@@ -0,0 +1,98 @@
+/* Copyright 2013 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 <QString>
+
+#include "logic/OneSixLibrary.h"
+
+enum RuleAction
+{
+ Allow,
+ Disallow,
+ Defer
+};
+
+RuleAction RuleAction_fromString(QString);
+QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules);
+
+class Rule
+{
+protected:
+ RuleAction m_result;
+ virtual bool applies(OneSixLibrary *parent) = 0;
+
+public:
+ Rule(RuleAction result) : m_result(result)
+ {
+ }
+ virtual ~Rule() {};
+ virtual QJsonObject toJson() = 0;
+ RuleAction apply(OneSixLibrary *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(OneSixLibrary *)
+ {
+ 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<OsRule> create(RuleAction result, OpSys system,
+ QString version_regexp)
+ {
+ return std::shared_ptr<OsRule>(new OsRule(result, system, version_regexp));
+ }
+};
+
+class ImplicitRule : public Rule
+{
+protected:
+ virtual bool applies(OneSixLibrary *)
+ {
+ return true;
+ }
+ ImplicitRule(RuleAction result) : Rule(result)
+ {
+ }
+
+public:
+ virtual QJsonObject toJson();
+ static std::shared_ptr<ImplicitRule> create(RuleAction result)
+ {
+ return std::shared_ptr<ImplicitRule>(new ImplicitRule(result));
+ }
+};
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
new file mode 100644
index 00000000..7685952c
--- /dev/null
+++ b/logic/OneSixUpdate.cpp
@@ -0,0 +1,326 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "OneSixUpdate.h"
+
+#include <QtNetwork>
+
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+#include <QDataStream>
+
+#include "BaseInstance.h"
+#include "lists/MinecraftVersionList.h"
+#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "OneSixInstance.h"
+#include "net/ForgeMirrors.h"
+#include "net/URLConstants.h"
+#include "assets/AssetsUtils.h"
+
+#include "pathutils.h"
+#include <JlCompress.h>
+
+OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent)
+ : Task(parent), m_inst(inst)
+{
+}
+
+void OneSixUpdate::executeTask()
+{
+ QString intendedVersion = m_inst->intendedVersionId();
+
+ // Make directories
+ QDir mcDir(m_inst->minecraftRoot());
+ if (!mcDir.exists() && !mcDir.mkpath("."))
+ {
+ emitFailed("Failed to create bin folder.");
+ return;
+ }
+
+ if (m_inst->shouldUpdate())
+ {
+ // Get a pointer to the version object that corresponds to the instance's version.
+ targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
+ MMC->minecraftlist()->findVersion(intendedVersion));
+ if (targetVersion == nullptr)
+ {
+ // don't do anything if it was invalid
+ emitFailed("The specified Minecraft version is invalid. Choose a different one.");
+ return;
+ }
+ versionFileStart();
+ }
+ else
+ {
+ jarlibStart();
+ }
+}
+
+void OneSixUpdate::versionFileStart()
+{
+ QLOG_INFO() << m_inst->name() << ": getting version file.";
+ setStatus(tr("Getting the version files from Mojang..."));
+
+ QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS +
+ targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
+ auto job = new NetJob("Version index");
+ job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
+ specificVersionDownloadJob.reset(job);
+ connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished()));
+ connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed()));
+ connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ specificVersionDownloadJob->start();
+}
+
+void OneSixUpdate::versionFileFinished()
+{
+ NetActionPtr DlJob = specificVersionDownloadJob->first();
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+
+ QString version_id = targetVersion->descriptor();
+ QString inst_dir = m_inst->instanceRoot();
+ // save the version file in $instanceId/version.json
+ {
+ QString version1 = PathCombine(inst_dir, "/version.json");
+ ensureFilePathExists(version1);
+ // FIXME: detect errors here, download to a temp file, swap
+ QSaveFile vfile1(version1);
+ if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
+ {
+ emitFailed("Can't open " + version1 + " for writing.");
+ return;
+ }
+ auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
+ qint64 actual = 0;
+ if ((actual = vfile1.write(data)) != data.size())
+ {
+ emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " +
+ data.size() + '.');
+ return;
+ }
+ if (!vfile1.commit())
+ {
+ emitFailed("Can't commit changes to " + version1);
+ return;
+ }
+ }
+
+ // the version is downloaded safely. update is 'done' at this point
+ m_inst->setShouldUpdate(false);
+
+ // delete any custom version inside the instance (it's no longer relevant, we did an update)
+ QString custom = PathCombine(inst_dir, "/custom.json");
+ QFile finfo(custom);
+ if (finfo.exists())
+ {
+ finfo.remove();
+ }
+ inst->reloadFullVersion();
+
+ jarlibStart();
+}
+
+void OneSixUpdate::versionFileFailed()
+{
+ emitFailed("Failed to download the version description. Try again.");
+}
+
+void OneSixUpdate::assetIndexStart()
+{
+ setStatus(tr("Updating assets index..."));
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ QString assetName = version->assets;
+ QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
+ QString localPath = assetName + ".json";
+ auto job = new NetJob("Asset index for " + inst->name());
+
+ auto metacache = MMC->metacache();
+ auto entry = metacache->resolveEntry("asset_indexes", localPath);
+ job->addNetAction(CacheDownload::make(indexUrl, entry));
+ jarlibDownloadJob.reset(job);
+
+ connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetIndexFinished()));
+ connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetIndexFailed()));
+ connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+
+ jarlibDownloadJob->start();
+}
+
+void OneSixUpdate::assetIndexFinished()
+{
+ AssetsIndex index;
+
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ QString assetName = version->assets;
+
+ QString asset_fname = "assets/indexes/" + assetName + ".json";
+ if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index))
+ {
+ emitFailed("Failed to read the assets index!");
+ }
+
+ QList<Md5EtagDownloadPtr> dls;
+ for (auto object : index.objects.values())
+ {
+ QString objectName = object.hash.left(2) + "/" + object.hash;
+ QFileInfo objectFile("assets/objects/" + objectName);
+ if ((!objectFile.isFile()) || (objectFile.size() != object.size))
+ {
+ auto objectDL = MD5EtagDownload::make(
+ QUrl("http://" + URLConstants::RESOURCE_BASE + objectName),
+ objectFile.filePath());
+ objectDL->m_total_progress = object.size;
+ dls.append(objectDL);
+ }
+ }
+ if (dls.size())
+ {
+ setStatus(tr("Getting the assets files from Mojang..."));
+ auto job = new NetJob("Assets for " + inst->name());
+ for (auto dl : dls)
+ job->addNetAction(dl);
+ jarlibDownloadJob.reset(job);
+ connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished()));
+ connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetsFailed()));
+ connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ jarlibDownloadJob->start();
+ return;
+ }
+ assetsFinished();
+}
+
+void OneSixUpdate::assetIndexFailed()
+{
+ emitFailed("Failed to download the assets index!");
+}
+
+void OneSixUpdate::assetsFinished()
+{
+ emitSucceeded();
+}
+
+void OneSixUpdate::assetsFailed()
+{
+ emitFailed("Failed to download assets!");
+}
+
+void OneSixUpdate::jarlibStart()
+{
+ setStatus(tr("Getting the library files from Mojang..."));
+ QLOG_INFO() << m_inst->name() << ": downloading libraries";
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ bool successful = inst->reloadFullVersion();
+ if (!successful)
+ {
+ emitFailed("Failed to load the version description file. It might be "
+ "corrupted, missing or simply too new.");
+ return;
+ }
+
+ // Build a list of URLs that will need to be downloaded.
+ std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ // minecraft.jar for this version
+ {
+ QString version_id = version->id;
+ QString localPath = version_id + "/" + version_id + ".jar";
+ QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath;
+
+ auto job = new NetJob("Libraries for instance " + inst->name());
+
+ auto metacache = MMC->metacache();
+ auto entry = metacache->resolveEntry("versions", localPath);
+ job->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
+
+ jarlibDownloadJob.reset(job);
+ }
+
+ auto libs = version->getActiveNativeLibs();
+ libs.append(version->getActiveNormalLibs());
+
+ auto metacache = MMC->metacache();
+ QList<ForgeXzDownloadPtr> ForgeLibs;
+ for (auto lib : libs)
+ {
+ if (lib->hint() == "local")
+ continue;
+
+ QString raw_storage = lib->storagePath();
+ QString raw_dl = lib->downloadUrl();
+
+ auto f = [&](QString storage, QString dl)
+ {
+ auto entry = metacache->resolveEntry("libraries", storage);
+ if (entry->stale)
+ {
+ if (lib->hint() == "forge-pack-xz")
+ {
+ ForgeLibs.append(ForgeXzDownload::make(storage, entry));
+ }
+ else
+ {
+ jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry));
+ }
+ }
+ };
+ if (raw_storage.contains("${arch}"))
+ {
+ QString cooked_storage = raw_storage;
+ QString cooked_dl = raw_dl;
+ f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"));
+ cooked_storage = raw_storage;
+ cooked_dl = raw_dl;
+ f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"));
+ }
+ else
+ {
+ f(raw_storage, raw_dl);
+ }
+ }
+ // TODO: think about how to propagate this from the original json file... or IF AT ALL
+ QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list";
+ if (!ForgeLibs.empty())
+ {
+ jarlibDownloadJob->addNetAction(
+ ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList));
+ }
+
+ connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished()));
+ connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed()));
+ connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+
+ jarlibDownloadJob->start();
+}
+
+void OneSixUpdate::jarlibFinished()
+{
+ assetIndexStart();
+}
+
+void OneSixUpdate::jarlibFailed()
+{
+ QStringList failed = jarlibDownloadJob->getFailedFiles();
+ QString failed_all = failed.join("\n");
+ emitFailed("Failed to download the following files:\n" + failed_all +
+ "\n\nPlease try again.");
+}
diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h
new file mode 100644
index 00000000..3c18211e
--- /dev/null
+++ b/logic/OneSixUpdate.h
@@ -0,0 +1,59 @@
+/* Copyright 2013 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 <QObject>
+#include <QList>
+#include <QUrl>
+
+#include "logic/net/NetJob.h"
+#include "logic/tasks/Task.h"
+
+class MinecraftVersion;
+class BaseInstance;
+
+class OneSixUpdate : public Task
+{
+ Q_OBJECT
+public:
+ explicit OneSixUpdate(BaseInstance *inst, QObject *parent = 0);
+ virtual void executeTask();
+
+private
+slots:
+ void versionFileStart();
+ void versionFileFinished();
+ void versionFileFailed();
+
+ void jarlibStart();
+ void jarlibFinished();
+ void jarlibFailed();
+
+ void assetIndexStart();
+ void assetIndexFinished();
+ void assetIndexFailed();
+
+ void assetsFinished();
+ void assetsFailed();
+
+private:
+ NetJobPtr specificVersionDownloadJob;
+ NetJobPtr jarlibDownloadJob;
+
+ // target version, determined during this task
+ std::shared_ptr<MinecraftVersion> targetVersion;
+ BaseInstance *m_inst = nullptr;
+};
diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp
new file mode 100644
index 00000000..8ae685f0
--- /dev/null
+++ b/logic/OneSixVersion.cpp
@@ -0,0 +1,340 @@
+/* Copyright 2013 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 "logic/OneSixVersion.h"
+#include "logic/OneSixLibrary.h"
+#include "logic/OneSixRule.h"
+
+#include "logger/QsLog.h"
+
+std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root,
+ std::shared_ptr<OneSixVersion> fullVersion)
+{
+ fullVersion->id = root.value("id").toString();
+
+ fullVersion->mainClass = root.value("mainClass").toString();
+ auto procArgsValue = root.value("processArguments");
+ if (procArgsValue.isString())
+ {
+ fullVersion->processArguments = procArgsValue.toString();
+ QString toCompare = fullVersion->processArguments.toLower();
+ if (toCompare == "legacy")
+ {
+ fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}";
+ }
+ else if (toCompare == "username_session")
+ {
+ fullVersion->minecraftArguments =
+ "--username ${auth_player_name} --session ${auth_session}";
+ }
+ else if (toCompare == "username_session_version")
+ {
+ fullVersion->minecraftArguments = "--username ${auth_player_name} "
+ "--session ${auth_session} "
+ "--version ${profile_name}";
+ }
+ }
+
+ auto minecraftArgsValue = root.value("minecraftArguments");
+ if (minecraftArgsValue.isString())
+ {
+ fullVersion->minecraftArguments = minecraftArgsValue.toString();
+ }
+
+ auto minecraftTypeValue = root.value("type");
+ if (minecraftTypeValue.isString())
+ {
+ fullVersion->type = minecraftTypeValue.toString();
+ }
+
+ fullVersion->releaseTime = root.value("releaseTime").toString();
+ fullVersion->time = root.value("time").toString();
+
+ auto assetsID = root.value("assets");
+ if (assetsID.isString())
+ {
+ fullVersion->assets = assetsID.toString();
+ }
+ else
+ {
+ fullVersion->assets = "legacy";
+ }
+
+ QLOG_DEBUG() << "Assets version:" << fullVersion->assets;
+
+ // Iterate through the list, if it's a list.
+ auto librariesValue = root.value("libraries");
+ if (!librariesValue.isArray())
+ return fullVersion;
+
+ QJsonArray libList = root.value("libraries").toArray();
+ for (auto libVal : libList)
+ {
+ if (!libVal.isObject())
+ {
+ continue;
+ }
+
+ QJsonObject libObj = libVal.toObject();
+
+ // Library name
+ auto nameVal = libObj.value("name");
+ if (!nameVal.isString())
+ continue;
+ std::shared_ptr<OneSixLibrary> library(new OneSixLibrary(nameVal.toString()));
+
+ auto urlVal = libObj.value("url");
+ if (urlVal.isString())
+ {
+ library->setBaseUrl(urlVal.toString());
+ }
+ auto hintVal = libObj.value("MMC-hint");
+ if (hintVal.isString())
+ {
+ library->setHint(hintVal.toString());
+ }
+ auto urlAbsVal = libObj.value("MMC-absoluteUrl");
+ auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility
+ if (urlAbsVal.isString())
+ {
+ library->setAbsoluteUrl(urlAbsVal.toString());
+ }
+ else if (urlAbsuVal.isString())
+ {
+ library->setAbsoluteUrl(urlAbsuVal.toString());
+ }
+ // Extract excludes (if any)
+ auto extractVal = libObj.value("extract");
+ if (extractVal.isObject())
+ {
+ QStringList excludes;
+ auto extractObj = extractVal.toObject();
+ auto excludesVal = extractObj.value("exclude");
+ if (excludesVal.isArray())
+ {
+ auto excludesList = excludesVal.toArray();
+ for (auto excludeVal : excludesList)
+ {
+ if (excludeVal.isString())
+ excludes.append(excludeVal.toString());
+ }
+ library->extract_excludes = excludes;
+ }
+ }
+
+ auto nativesVal = libObj.value("natives");
+ if (nativesVal.isObject())
+ {
+ library->setIsNative();
+ auto nativesObj = nativesVal.toObject();
+ auto iter = nativesObj.begin();
+ while (iter != nativesObj.end())
+ {
+ auto osType = OpSys_fromString(iter.key());
+ if (osType == Os_Other)
+ continue;
+ if (!iter.value().isString())
+ continue;
+ library->addNative(osType, iter.value().toString());
+ iter++;
+ }
+ }
+ library->setRules(rulesFromJsonV4(libObj));
+ library->finalize();
+ fullVersion->libraries.append(library);
+ }
+ return fullVersion;
+}
+
+std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root)
+{
+ std::shared_ptr<OneSixVersion> readVersion(new OneSixVersion());
+ int launcher_ver = readVersion->minimumLauncherVersion =
+ root.value("minimumLauncherVersion").toDouble();
+
+ // ADD MORE HERE :D
+ if (launcher_ver > 0 && launcher_ver <= 13)
+ return fromJsonV4(root, readVersion);
+ else
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+}
+
+std::shared_ptr<OneSixVersion> OneSixVersion::fromFile(QString filepath)
+{
+ QFile file(filepath);
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+
+ auto data = file.readAll();
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+
+ if (!jsonDoc.isObject())
+ {
+ return std::shared_ptr<OneSixVersion>();
+ }
+ QJsonObject root = jsonDoc.object();
+ auto version = fromJson(root);
+ if (version)
+ version->original_file = filepath;
+ return version;
+}
+
+bool OneSixVersion::toOriginalFile()
+{
+ if (original_file.isEmpty())
+ return false;
+ QSaveFile file(original_file);
+ if (!file.open(QIODevice::WriteOnly))
+ {
+ return false;
+ }
+ // serialize base attributes (those we care about anyway)
+ QJsonObject root;
+ root.insert("minecraftArguments", minecraftArguments);
+ root.insert("mainClass", mainClass);
+ root.insert("minimumLauncherVersion", minimumLauncherVersion);
+ root.insert("time", time);
+ root.insert("id", id);
+ root.insert("type", type);
+ // screw processArguments
+ root.insert("releaseTime", releaseTime);
+ QJsonArray libarray;
+ for (const auto &lib : libraries)
+ {
+ libarray.append(lib->toJson());
+ }
+ if (libarray.count())
+ root.insert("libraries", libarray);
+ QJsonDocument doc(root);
+ file.write(doc.toJson());
+ return file.commit();
+}
+
+QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNormalLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary>> output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && !lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary>> output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+void OneSixVersion::externalUpdateStart()
+{
+ beginResetModel();
+}
+
+void OneSixVersion::externalUpdateFinish()
+{
+ endResetModel();
+}
+
+QVariant OneSixVersion::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= libraries.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ {
+ switch (column)
+ {
+ case 0:
+ return libraries[row]->name();
+ case 1:
+ return libraries[row]->type();
+ case 2:
+ return libraries[row]->version();
+ default:
+ return QVariant();
+ }
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ int row = index.row();
+ if (libraries[row]->isActive())
+ {
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
+ }
+ else
+ {
+ return Qt::ItemNeverHasChildren;
+ }
+ // return QAbstractListModel::flags(index);
+}
+
+QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
+ return QVariant();
+ switch (section)
+ {
+ case 0:
+ return QString("Name");
+ case 1:
+ return QString("Type");
+ case 2:
+ return QString("Version");
+ default:
+ return QString();
+ }
+}
+
+int OneSixVersion::rowCount(const QModelIndex &parent) const
+{
+ return libraries.size();
+}
+
+int OneSixVersion::columnCount(const QModelIndex &parent) const
+{
+ return 3;
+}
diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h
new file mode 100644
index 00000000..036f3d53
--- /dev/null
+++ b/logic/OneSixVersion.h
@@ -0,0 +1,106 @@
+/* Copyright 2013 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 <QtCore>
+#include <memory>
+
+class OneSixLibrary;
+
+class OneSixVersion : public QAbstractListModel
+{
+ // Things required to implement the Qt list model
+public:
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ // serialization/deserialization
+public:
+ bool toOriginalFile();
+ static std::shared_ptr<OneSixVersion> fromJson(QJsonObject root);
+ static std::shared_ptr<OneSixVersion> fromFile(QString filepath);
+
+public:
+ QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
+ QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
+ // called when something starts/stops messing with the object
+ // FIXME: these are ugly in every possible way.
+ void externalUpdateStart();
+ void externalUpdateFinish();
+
+ // data members
+public:
+ /// file this was read from. blank, if none
+ QString original_file;
+ /// the ID - determines which jar to use! ACTUALLY IMPORTANT!
+ QString id;
+ /// Last updated time - as a string
+ QString time;
+ /// Release time - as a string
+ QString releaseTime;
+ /// Release type - "release" or "snapshot"
+ QString type;
+ /// Assets type - "legacy" or a version ID
+ QString assets;
+ /**
+ * DEPRECATED: Old versions of the new vanilla launcher used this
+ * ex: "username_session_version"
+ */
+ QString processArguments;
+ /**
+ * 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 minecraftArguments;
+ /**
+ * the minimum launcher version required by this version ... current is 4 (at point of
+ * writing)
+ */
+ int minimumLauncherVersion = 0xDEADBEEF;
+ /**
+ * The main class to load first
+ */
+ QString mainClass;
+
+ /// the list of libs - both active and inactive, native and java
+ QList<std::shared_ptr<OneSixLibrary>> libraries;
+
+ /*
+ 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<Rule> rules;
+};
diff --git a/logic/OpSys.cpp b/logic/OpSys.cpp
new file mode 100644
index 00000000..e001b7f3
--- /dev/null
+++ b/logic/OpSys.cpp
@@ -0,0 +1,42 @@
+/* Copyright 2013 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/logic/OpSys.h b/logic/OpSys.h
new file mode 100644
index 00000000..363c87d7
--- /dev/null
+++ b/logic/OpSys.h
@@ -0,0 +1,37 @@
+/* Copyright 2013 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 <QString>
+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/logic/SkinUtils.cpp b/logic/SkinUtils.cpp
new file mode 100644
index 00000000..c6c80006
--- /dev/null
+++ b/logic/SkinUtils.cpp
@@ -0,0 +1,47 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "logic/SkinUtils.h"
+#include "net/HttpMetaCache.h"
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+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(MMC->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/logic/SkinUtils.h b/logic/SkinUtils.h
new file mode 100644
index 00000000..64353b72
--- /dev/null
+++ b/logic/SkinUtils.h
@@ -0,0 +1,23 @@
+/* Copyright 2013 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 <QPixmap>
+
+namespace SkinUtils
+{
+QPixmap getFaceFromCache(QString username, int height = 64, int width = 64);
+}
diff --git a/logic/assets/AssetsMigrateTask.cpp b/logic/assets/AssetsMigrateTask.cpp
new file mode 100644
index 00000000..7c1f5204
--- /dev/null
+++ b/logic/assets/AssetsMigrateTask.cpp
@@ -0,0 +1,143 @@
+#include "AssetsMigrateTask.h"
+#include "MultiMC.h"
+#include "logger/QsLog.h"
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QDirIterator>
+#include <QCryptographicHash>
+#include "gui/dialogs/CustomMessageBox.h"
+#include <QDesktopServices>
+
+AssetsMigrateTask::AssetsMigrateTask(int expected, QObject *parent)
+ : Task(parent)
+{
+ this->m_expected = expected;
+}
+
+void AssetsMigrateTask::executeTask()
+{
+ this->setStatus(tr("Migrating legacy assets..."));
+ this->setProgress(0);
+
+ QDir assets_dir("assets");
+ if (!assets_dir.exists())
+ {
+ emitFailed("Assets directory didn't exist");
+ return;
+ }
+ assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
+ int base_length = assets_dir.path().length();
+
+ QList<QString> blacklist = {"indexes", "objects", "virtual"};
+
+ if (!assets_dir.exists("objects"))
+ assets_dir.mkdir("objects");
+ QDir objects_dir("assets/objects");
+
+ QDirIterator iterator(assets_dir, QDirIterator::Subdirectories);
+ int successes = 0;
+ int failures = 0;
+ while (iterator.hasNext())
+ {
+ QString currentDir = iterator.next();
+ currentDir = currentDir.remove(0, base_length + 1);
+
+ bool ignore = false;
+ for (QString blacklisted : blacklist)
+ {
+ if (currentDir.startsWith(blacklisted))
+ ignore = true;
+ }
+
+ if (!iterator.fileInfo().isDir() && !ignore)
+ {
+ QString filename = iterator.filePath();
+
+ QFile input(filename);
+ input.open(QIODevice::ReadOnly | QIODevice::WriteOnly);
+ QString sha1sum =
+ QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1)
+ .toHex()
+ .constData();
+
+ QString object_name = filename.remove(0, base_length + 1);
+ QLOG_DEBUG() << "Processing" << object_name << ":" << sha1sum << input.size();
+
+ QString object_tlk = sha1sum.left(2);
+ QString object_tlk_dir = objects_dir.path() + "/" + object_tlk;
+
+ QDir tlk_dir(object_tlk_dir);
+ if (!tlk_dir.exists())
+ objects_dir.mkdir(object_tlk);
+
+ QString new_filename = tlk_dir.path() + "/" + sha1sum;
+ QFile new_object(new_filename);
+ if (!new_object.exists())
+ {
+ bool rename_success = input.rename(new_filename);
+ QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":"
+ << QString::number(rename_success);
+ if (rename_success)
+ successes++;
+ else
+ failures++;
+ }
+ else
+ {
+ input.remove();
+ QLOG_DEBUG() << " Already exists, deleting original and not copying.";
+ }
+
+ this->setProgress(100 * ((successes + failures) / (float) this->m_expected));
+ }
+ }
+
+ if (successes + failures == 0)
+ {
+ this->setProgress(100);
+ QLOG_DEBUG() << "No legacy assets needed importing.";
+ }
+ else
+ {
+ QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and"
+ << failures << "failures.";
+
+ this->setStatus("Cleaning up legacy assets...");
+ this->setProgress(100);
+
+ QDirIterator cleanup_iterator(assets_dir);
+
+ while (cleanup_iterator.hasNext())
+ {
+ QString currentDir = cleanup_iterator.next();
+ currentDir = currentDir.remove(0, base_length + 1);
+
+ bool ignore = false;
+ for (QString blacklisted : blacklist)
+ {
+ if (currentDir.startsWith(blacklisted))
+ ignore = true;
+ }
+
+ if (cleanup_iterator.fileInfo().isDir() && !ignore)
+ {
+ QString path = cleanup_iterator.filePath();
+ QDir folder(path);
+
+ QLOG_DEBUG() << "Cleaning up legacy assets folder:" << path;
+
+ folder.removeRecursively();
+ }
+ }
+ }
+
+ if(failures > 0)
+ {
+ emitFailed(QString("Failed to migrate %1 legacy assets").arg(failures));
+ }
+ else
+ {
+ emitSucceeded();
+ }
+}
+
diff --git a/logic/assets/AssetsMigrateTask.h b/logic/assets/AssetsMigrateTask.h
new file mode 100644
index 00000000..d8d58c97
--- /dev/null
+++ b/logic/assets/AssetsMigrateTask.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "logic/tasks/Task.h"
+#include <QMessageBox>
+#include <QNetworkReply>
+#include <memory>
+
+class AssetsMigrateTask : public Task
+{
+ Q_OBJECT
+public:
+ explicit AssetsMigrateTask(int expected, QObject* parent=0);
+
+protected:
+ virtual void executeTask();
+
+private:
+ int m_expected;
+};
diff --git a/logic/assets/AssetsUtils.cpp b/logic/assets/AssetsUtils.cpp
new file mode 100644
index 00000000..bca7773d
--- /dev/null
+++ b/logic/assets/AssetsUtils.cpp
@@ -0,0 +1,154 @@
+/* Copyright 2013 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 <QDir>
+#include <QDirIterator>
+#include <QCryptographicHash>
+#include <QJsonParseError>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+#include "AssetsUtils.h"
+#include "MultiMC.h"
+
+namespace AssetsUtils
+{
+int findLegacyAssets()
+{
+ QDir assets_dir("assets");
+ if (!assets_dir.exists())
+ return 0;
+ assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
+ int base_length = assets_dir.path().length();
+
+ QList<QString> blacklist = {"indexes", "objects", "virtual"};
+
+ QDirIterator iterator(assets_dir, QDirIterator::Subdirectories);
+ int found = 0;
+ while (iterator.hasNext())
+ {
+ QString currentDir = iterator.next();
+ currentDir = currentDir.remove(0, base_length + 1);
+
+ bool ignore = false;
+ for (QString blacklisted : blacklist)
+ {
+ if (currentDir.startsWith(blacklisted))
+ ignore = true;
+ }
+
+ if (!iterator.fileInfo().isDir() && !ignore)
+ {
+ found++;
+ }
+ }
+
+ return found;
+}
+
+/*
+ * Returns true on success, with index populated
+ * index is undefined otherwise
+ */
+bool loadAssetsIndexJson(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))
+ {
+ QLOG_ERROR() << "Failed to read assets index file" << path;
+ 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)
+ {
+ QLOG_ERROR() << "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())
+ {
+ QLOG_ERROR() << "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)
+ {
+ // QLOG_DEBUG() << 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)
+ {
+ // QLOG_DEBUG() << 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;
+}
+}
diff --git a/logic/assets/AssetsUtils.h b/logic/assets/AssetsUtils.h
new file mode 100644
index 00000000..aaacc2db
--- /dev/null
+++ b/logic/assets/AssetsUtils.h
@@ -0,0 +1,39 @@
+/* Copyright 2013 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 <QString>
+#include <QMap>
+
+class AssetObject;
+
+struct AssetObject
+{
+ QString hash;
+ qint64 size;
+};
+
+struct AssetsIndex
+{
+ QMap<QString, AssetObject> objects;
+ bool isVirtual = false;
+};
+
+namespace AssetsUtils
+{
+bool loadAssetsIndexJson(QString file, AssetsIndex* index);
+int findLegacyAssets();
+}
diff --git a/logic/auth/AuthSession.cpp b/logic/auth/AuthSession.cpp
new file mode 100644
index 00000000..8758bfbd
--- /dev/null
+++ b/logic/auth/AuthSession.cpp
@@ -0,0 +1,30 @@
+#include "AuthSession.h"
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QStringList>
+
+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/logic/auth/AuthSession.h b/logic/auth/AuthSession.h
new file mode 100644
index 00000000..2ac170fa
--- /dev/null
+++ b/logic/auth/AuthSession.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <QString>
+#include <QMultiMap>
+#include <memory>
+
+struct User
+{
+ QString id;
+ QMultiMap<QString, QString> properties;
+};
+
+struct 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<AuthSession> AuthSessionPtr;
diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp
new file mode 100644
index 00000000..6c937ef1
--- /dev/null
+++ b/logic/auth/MojangAccount.cpp
@@ -0,0 +1,278 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Authors: Orochimarufan <orochimarufan.x3@gmail.com>
+ *
+ * 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 <QUuid>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QRegExp>
+#include <QStringList>
+#include <QJsonDocument>
+
+#include <logger/QsLog.h>
+
+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())
+ {
+ QLOG_ERROR() << "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)
+ {
+ QLOG_ERROR() << "Can't load Mojang account with username \"" << username
+ << "\". No profiles found.";
+ return nullptr;
+ }
+
+ QList<AccountProfile> 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())
+ {
+ QLOG_WARN() << "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<YggdrasilTask> 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 (reason == "Yggdrasil task cancelled.")
+ {
+ 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/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h
new file mode 100644
index 00000000..a0565e2c
--- /dev/null
+++ b/logic/auth/MojangAccount.h
@@ -0,0 +1,171 @@
+/* Copyright 2013 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 <QObject>
+#include <QString>
+#include <QList>
+#include <QJsonObject>
+#include <QPair>
+#include <QMap>
+
+#include <memory>
+#include "AuthSession.h"
+
+class Task;
+class YggdrasilTask;
+class MojangAccount;
+
+typedef std::shared_ptr<MojangAccount> 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 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<YggdrasilTask> 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<AccountProfile> &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<AccountProfile> m_profiles;
+
+ // the user structure, whatever it is.
+ User m_user;
+
+ // current task we are executing here
+ std::shared_ptr<YggdrasilTask> 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/logic/auth/MojangAccountList.cpp b/logic/auth/MojangAccountList.cpp
new file mode 100644
index 00000000..70bc0cf2
--- /dev/null
+++ b/logic/auth/MojangAccountList.cpp
@@ -0,0 +1,426 @@
+/* Copyright 2013 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 "logic/auth/MojangAccountList.h"
+
+#include <QIODevice>
+#include <QFile>
+#include <QTextStream>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonParseError>
+#include <QDir>
+
+#include "logger/QsLog.h"
+
+#include "logic/auth/MojangAccount.h"
+#include <pathutils.h>
+
+#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 "Active?";
+
+ case NameColumn:
+ return "Name";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case NameColumn:
+ return "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<MojangAccountPtr> versions)
+{
+ beginResetModel();
+ m_accounts = versions;
+ endResetModel();
+}
+
+bool MojangAccountList::loadList(const QString &filePath)
+{
+ QString path = filePath;
+ if (path.isEmpty())
+ path = m_listFilePath;
+ if (path.isEmpty())
+ {
+ QLOG_ERROR() << "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))
+ {
+ QLOG_ERROR() << 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)
+ {
+ QLOG_ERROR() << 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())
+ {
+ QLOG_ERROR() << "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";
+ QLOG_WARN() << "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
+ {
+ QLOG_WARN() << "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())
+ {
+ QLOG_ERROR() << "Can't save Mojang account list. No file path given and no default set.";
+ return false;
+ }
+
+ // make sure the parent folder exists
+ if(!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();
+ }
+
+ QLOG_INFO() << "Writing account list to" << path;
+
+ QLOG_DEBUG() << "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.
+ QLOG_DEBUG() << "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.
+ QLOG_DEBUG() << "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))
+ {
+ QLOG_ERROR() << 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.close();
+
+ QLOG_INFO() << "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/logic/auth/MojangAccountList.h b/logic/auth/MojangAccountList.h
new file mode 100644
index 00000000..6f4fbb17
--- /dev/null
+++ b/logic/auth/MojangAccountList.h
@@ -0,0 +1,199 @@
+/* Copyright 2013 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 <QObject>
+#include <QVariant>
+#include <QAbstractListModel>
+#include <QSharedPointer>
+
+#include "logic/auth/MojangAccount.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 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<MojangAccountPtr> 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<MojangAccountPtr> versions);
+};
diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp
new file mode 100644
index 00000000..277d7bfd
--- /dev/null
+++ b/logic/auth/YggdrasilTask.cpp
@@ -0,0 +1,211 @@
+/* Copyright 2013 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 <logic/auth/YggdrasilTask.h>
+
+#include <QObject>
+#include <QString>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QNetworkReply>
+#include <QByteArray>
+
+#include <MultiMC.h>
+#include <logic/auth/MojangAccount.h>
+#include <logic/net/URLConstants.h>
+
+YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
+ : Task(parent), m_account(account)
+{
+}
+
+void YggdrasilTask::executeTask()
+{
+ setStatus(getStateMessage(STATE_SENDING_REQUEST));
+
+ // Get the content of the request we're going to send to the server.
+ QJsonDocument doc(getRequestContent());
+
+ auto worker = MMC->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::abort);
+ 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);
+}
+
+void YggdrasilTask::abort()
+{
+ progress(timeout_max, timeout_max);
+ m_netReply->abort();
+}
+
+void YggdrasilTask::sslErrors(QList<QSslError> errors)
+{
+ int i = 1;
+ for (auto error : errors)
+ {
+ QLOG_ERROR() << "LOGIN SSL Error #" << i << " : " << error.errorString();
+ auto cert = error.certificate();
+ QLOG_ERROR() << "Certificate in question:\n" << cert.toText();
+ i++;
+ }
+}
+
+void YggdrasilTask::processReply()
+{
+ setStatus(getStateMessage(STATE_PROCESSING_RESPONSE));
+
+ if (m_netReply->error() == QNetworkReply::SslHandshakeFailedError)
+ {
+ emitFailed(
+ tr("<b>SSL Handshake failed.</b><br/>There might be a few causes for it:<br/>"
+ "<ul>"
+ "<li>You use Windows XP and need to <a "
+ "href=\"http://www.microsoft.com/en-us/download/details.aspx?id=38918\">update "
+ "your root certificates</a></li>"
+ "<li>Some device on your network is interfering with SSL traffic. In that case, "
+ "you have bigger worries than Minecraft not starting.</li>"
+ "<li>Possibly something else. Check the MultiMC log file for details</li>"
+ "</ul>"));
+ return;
+ }
+
+ // any network errors lead to offline mode right now
+ if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError &&
+ m_netReply->error() <= QNetworkReply::UnknownNetworkError)
+ {
+ // WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout
+ emitFailed("Yggdrasil task cancelled.");
+ QLOG_ERROR() << "Yggdrasil task cancelled because of: " << m_netReply->error() << " : "
+ << m_netReply->errorString();
+ 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)
+ {
+ if (processResponse(replyData.size() > 0 ? doc.object() : QJsonObject()))
+ {
+ emitSucceeded();
+ return;
+ }
+
+ // errors happened anyway?
+ emitFailed(m_error ? m_error->m_errorMessageVerbose
+ : tr("An unknown error occurred when processing the response "
+ "from the authentication server."));
+ }
+ else
+ {
+ emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.")
+ .arg(jsonError.errorString())
+ .arg(jsonError.offset));
+ }
+ 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.
+ QLOG_DEBUG() << "The request failed, but the server gave us an error message. "
+ "Processing error.";
+ emitFailed(processError(doc.object()));
+ }
+ else
+ {
+ // The server didn't say anything regarding the error. Give the user an unknown
+ // error.
+ QLOG_DEBUG() << "The request failed and the server gave no error message. "
+ "Unknown error.";
+ emitFailed(tr("An unknown error occurred when trying to communicate with the "
+ "authentication server: %1").arg(m_netReply->errorString()));
+ }
+}
+
+QString 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<Error>(new Error{
+ errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")});
+ return m_error->m_errorMessageVerbose;
+ }
+ else
+ {
+ // Error is not in standard format. Don't set m_error and return unknown error.
+ return tr("An unknown Yggdrasil error occurred.");
+ }
+}
+
+QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const
+{
+ switch (state)
+ {
+ case STATE_SENDING_REQUEST:
+ return tr("Sending request to auth servers...");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Processing response from servers...");
+ default:
+ return tr("Processing. Please wait...");
+ }
+}
diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h
new file mode 100644
index 00000000..4a87067a
--- /dev/null
+++ b/logic/auth/YggdrasilTask.h
@@ -0,0 +1,137 @@
+/* Copyright 2013 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 <logic/tasks/Task.h>
+
+#include <QString>
+#include <QJsonObject>
+#include <QTimer>
+#include <qsslerror.h>
+
+#include "logic/auth/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;
+ };
+
+protected:
+ /**
+ * 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_SENDING_REQUEST,
+ STATE_PROCESSING_RESPONSE,
+ STATE_OTHER,
+ };
+
+ virtual void executeTask();
+
+ /**
+ * 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 bool 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 QString 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 State state) const;
+
+protected
+slots:
+ void processReply();
+ void refreshTimers(qint64, qint64);
+ void heartbeat();
+ void sslErrors(QList<QSslError>);
+
+public
+slots:
+ virtual void abort() override;
+
+protected:
+ // FIXME: segfault disaster waiting to happen
+ MojangAccount *m_account = nullptr;
+ QNetworkReply *m_netReply = nullptr;
+ std::shared_ptr<Error> m_error;
+ QTimer timeout_keeper;
+ QTimer counter;
+ int count = 0; // num msec since time reset
+
+ const int timeout_max = 10000;
+ const int time_step = 50;
+
+ AuthSessionPtr m_session;
+};
diff --git a/logic/auth/flows/AuthenticateTask.cpp b/logic/auth/flows/AuthenticateTask.cpp
new file mode 100644
index 00000000..6548c4e9
--- /dev/null
+++ b/logic/auth/flows/AuthenticateTask.cpp
@@ -0,0 +1,203 @@
+
+/* Copyright 2013 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 <logic/auth/flows/AuthenticateTask.h>
+
+#include <logic/auth/MojangAccount.h>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QVariant>
+#include <QDebug>
+
+#include "logger/QsLog.h"
+
+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())
+ req.insert("clientToken", m_account->m_clientToken);
+
+ return req;
+}
+
+bool AuthenticateTask::processResponse(QJsonObject responseData)
+{
+ // Read the response data. We need to get the client token, access token, and the selected
+ // profile.
+ QLOG_DEBUG() << "Processing authentication response.";
+ // QLOG_DEBUG() << responseData;
+ // If we already have a client token, make sure the one the server gave us matches our
+ // existing one.
+ QLOG_DEBUG() << "Getting client token.";
+ QString clientToken = responseData.value("clientToken").toString("");
+ if (clientToken.isEmpty())
+ {
+ // Fail if the server gave us an empty client token
+ // TODO: Set an error properly to display to the user.
+ QLOG_ERROR() << "Server didn't send a client token.";
+ return false;
+ }
+ if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
+ {
+ // The server changed our client token! Obey its wishes, but complain. That's what I do
+ // for my parents, so...
+ QLOG_WARN() << "Server changed our client token to '" << clientToken
+ << "'. This shouldn't happen, but it isn't really a big deal.";
+ }
+ // Set the client token.
+ m_account->m_clientToken = clientToken;
+
+ // Now, we set the access token.
+ QLOG_DEBUG() << "Getting access token.";
+ QString accessToken = responseData.value("accessToken").toString("");
+ if (accessToken.isEmpty())
+ {
+ // Fail if the server didn't give us an access token.
+ // TODO: Set an error properly to display to the user.
+ QLOG_ERROR() << "Server didn't send an access token.";
+ }
+ // 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.
+ QLOG_DEBUG() << "Loading profile list.";
+ QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
+ QList<AccountProfile> 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.
+ QLOG_WARN() << "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).
+ QLOG_DEBUG() << "Setting current profile.";
+ QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
+ QString currentProfileId = currentProfile.value("id").toString("");
+ if (currentProfileId.isEmpty())
+ {
+ // TODO: Set an error to display to the user.
+ QLOG_ERROR() << "Server didn't specify a currently selected profile.";
+ return false;
+ }
+ if (!m_account->setCurrentProfile(currentProfileId))
+ {
+ // TODO: Set an error to display to the user.
+ QLOG_ERROR() << "Server specified a selected profile that wasn't in the available "
+ "profiles list.";
+ return false;
+ }
+
+ // 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.
+ QLOG_DEBUG() << "Finished reading authentication response.";
+ return true;
+}
+
+QString AuthenticateTask::getEndpoint() const
+{
+ return "authenticate";
+}
+
+QString AuthenticateTask::getStateMessage(const YggdrasilTask::State state) const
+{
+ switch (state)
+ {
+ case STATE_SENDING_REQUEST:
+ return tr("Authenticating: Sending request...");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Authenticating: Processing response...");
+ default:
+ return YggdrasilTask::getStateMessage(state);
+ }
+}
diff --git a/logic/auth/flows/AuthenticateTask.h b/logic/auth/flows/AuthenticateTask.h
new file mode 100644
index 00000000..b6564657
--- /dev/null
+++ b/logic/auth/flows/AuthenticateTask.h
@@ -0,0 +1,46 @@
+/* Copyright 2013 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 <logic/auth/YggdrasilTask.h>
+
+#include <QObject>
+#include <QString>
+#include <QJsonObject>
+
+/**
+ * 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;
+
+ virtual QString getEndpoint() const;
+
+ virtual bool processResponse(QJsonObject responseData);
+
+ QString getStateMessage(const YggdrasilTask::State state) const;
+
+private:
+ QString m_password;
+};
diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp
new file mode 100644
index 00000000..5a55ed91
--- /dev/null
+++ b/logic/auth/flows/RefreshTask.cpp
@@ -0,0 +1,152 @@
+/* Copyright 2013 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 <logic/auth/flows/RefreshTask.h>
+
+#include <logic/auth/MojangAccount.h>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QVariant>
+#include <QDebug>
+
+#include "logger/QsLog.h"
+
+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;
+}
+
+bool RefreshTask::processResponse(QJsonObject responseData)
+{
+ // Read the response data. We need to get the client token, access token, and the selected
+ // profile.
+ QLOG_DEBUG() << "Processing authentication response.";
+
+ // QLOG_DEBUG() << 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
+ // TODO: Set an error properly to display to the user.
+ QLOG_ERROR() << "Server didn't send a client token.";
+ return false;
+ }
+ if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
+ {
+ // The server changed our client token! Obey its wishes, but complain. That's what I do
+ // for my parents, so...
+ QLOG_ERROR() << "Server changed our client token to '" << clientToken
+ << "'. This shouldn't happen, but it isn't really a big deal.";
+ return false;
+ }
+
+ // Now, we set the access token.
+ QLOG_DEBUG() << "Getting new access token.";
+ QString accessToken = responseData.value("accessToken").toString("");
+ if (accessToken.isEmpty())
+ {
+ // Fail if the server didn't give us an access token.
+ // TODO: Set an error properly to display to the user.
+ QLOG_ERROR() << "Server didn't send an access token.";
+ return false;
+ }
+
+ // 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)
+ {
+ // TODO: Set an error to display to the user.
+ QLOG_ERROR() << "Server didn't specify the same selected profile as ours.";
+ return false;
+ }
+
+ // 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.
+ QLOG_DEBUG() << "Finished reading refresh response.";
+ // Reset the access token.
+ m_account->m_accessToken = accessToken;
+ return true;
+}
+
+QString RefreshTask::getEndpoint() const
+{
+ return "refresh";
+}
+
+QString RefreshTask::getStateMessage(const YggdrasilTask::State state) const
+{
+ switch (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(state);
+ }
+}
diff --git a/logic/auth/flows/RefreshTask.h b/logic/auth/flows/RefreshTask.h
new file mode 100644
index 00000000..0dadc025
--- /dev/null
+++ b/logic/auth/flows/RefreshTask.h
@@ -0,0 +1,44 @@
+/* Copyright 2013 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 <logic/auth/YggdrasilTask.h>
+
+#include <QObject>
+#include <QString>
+#include <QJsonObject>
+
+/**
+ * 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;
+
+ virtual QString getEndpoint() const;
+
+ virtual bool processResponse(QJsonObject responseData);
+
+ QString getStateMessage(const YggdrasilTask::State state) const;
+};
+
diff --git a/logic/auth/flows/ValidateTask.cpp b/logic/auth/flows/ValidateTask.cpp
new file mode 100644
index 00000000..4f7323fd
--- /dev/null
+++ b/logic/auth/flows/ValidateTask.cpp
@@ -0,0 +1,64 @@
+
+/* Copyright 2013 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 <logic/auth/flows/ValidateTask.h>
+
+#include <logic/auth/MojangAccount.h>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QVariant>
+#include <QDebug>
+
+#include "logger/QsLog.h"
+
+ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
+ : YggdrasilTask(account, parent)
+{
+}
+
+QJsonObject ValidateTask::getRequestContent() const
+{
+ QJsonObject req;
+ req.insert("accessToken", m_account->m_accessToken);
+ return req;
+}
+
+bool ValidateTask::processResponse(QJsonObject responseData)
+{
+ // Assume that if processError wasn't called, then the request was successful.
+ emitSucceeded();
+ return true;
+}
+
+QString ValidateTask::getEndpoint() const
+{
+ return "validate";
+}
+
+QString ValidateTask::getStateMessage(const YggdrasilTask::State state) const
+{
+ switch (state)
+ {
+ case STATE_SENDING_REQUEST:
+ return tr("Validating access token: Sending request...");
+ case STATE_PROCESSING_RESPONSE:
+ return tr("Validating access token: Processing response...");
+ default:
+ return YggdrasilTask::getStateMessage(state);
+ }
+}
diff --git a/logic/auth/flows/ValidateTask.h b/logic/auth/flows/ValidateTask.h
new file mode 100644
index 00000000..0e34f0c3
--- /dev/null
+++ b/logic/auth/flows/ValidateTask.h
@@ -0,0 +1,47 @@
+/* Copyright 2013 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 <logic/auth/YggdrasilTask.h>
+
+#include <QObject>
+#include <QString>
+#include <QJsonObject>
+
+/**
+ * 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;
+
+ virtual QString getEndpoint() const;
+
+ virtual bool processResponse(QJsonObject responseData);
+
+ QString getStateMessage(const YggdrasilTask::State state) const;
+
+private:
+};
diff --git a/logic/icons/IconList.cpp b/logic/icons/IconList.cpp
new file mode 100644
index 00000000..d76e6fbb
--- /dev/null
+++ b/logic/icons/IconList.cpp
@@ -0,0 +1,368 @@
+/* Copyright 2013 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 <pathutils.h>
+#include <settingsobject.h>
+#include <QMap>
+#include <QEventLoop>
+#include <QMimeData>
+#include <QUrl>
+#include <QFileSystemWatcher>
+#include <MultiMC.h>
+#include <setting.h>
+
+#define MAX_SIZE 1024
+
+IconList::IconList(QObject *parent) : QAbstractListModel(parent)
+{
+ // add builtin icons
+ QDir instance_icons(":/icons/instances/");
+ 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)));
+
+ auto setting = MMC->settings()->getSetting("IconsDir");
+ QString path = setting->get().toString();
+ connect(setting.get(), SIGNAL(settingChanged(const Setting &, QVariant)),
+ SLOT(settingChanged(const Setting &, QVariant)));
+ 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(!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<QString> current_list;
+ for (auto &it : icons)
+ {
+ if (!it.has(MMCIcon::FileBased))
+ continue;
+ current_list.push_back(it.m_images[MMCIcon::FileBased].filename);
+ }
+ QSet<QString> current_set = current_list.toSet();
+
+ QSet<QString> to_remove = current_set;
+ to_remove -= new_set;
+
+ QSet<QString> to_add = new_set;
+ to_add -= current_set;
+
+ for (auto remove : to_remove)
+ {
+ QLOG_INFO() << "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)
+ {
+ QLOG_INFO() << "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)
+{
+ QLOG_INFO() << "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();
+ ensureFolderPathExists(abs_path);
+ is_watching = m_watcher->addPath(abs_path);
+ if (is_watching)
+ {
+ QLOG_INFO() << "Started watching " << abs_path;
+ }
+ else
+ {
+ QLOG_INFO() << "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 = PathCombine("icons", fileinfo.fileName());
+
+ QString suffix = fileinfo.suffix();
+ if (suffix != "jpeg" && suffix != "png" && suffix != "jpg" && suffix != "ico")
+ continue;
+
+ if (!QFile::copy(file, target))
+ continue;
+ }
+}
+
+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/logic/icons/IconList.h b/logic/icons/IconList.h
new file mode 100644
index 00000000..4ee3f782
--- /dev/null
+++ b/logic/icons/IconList.h
@@ -0,0 +1,78 @@
+/* Copyright 2013 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 <QMutex>
+#include <QAbstractListModel>
+#include <QFile>
+#include <QDir>
+#include <QtGui/QIcon>
+#include <memory>
+#include "MMCIcon.h"
+#include "setting.h"
+
+class QFileSystemWatcher;
+
+class IconList : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit IconList(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);
+
+ 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);
+
+ 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();
+
+protected
+slots:
+ void directoryChanged(const QString &path);
+ void fileChanged(const QString &path);
+ void settingChanged(const Setting & setting, QVariant value);
+private:
+ std::shared_ptr<QFileSystemWatcher> m_watcher;
+ bool is_watching;
+ QMap<QString, int> name_index;
+ QVector<MMCIcon> icons;
+ QDir m_dir;
+};
diff --git a/logic/icons/MMCIcon.cpp b/logic/icons/MMCIcon.cpp
new file mode 100644
index 00000000..d721513d
--- /dev/null
+++ b/logic/icons/MMCIcon.cpp
@@ -0,0 +1,89 @@
+/* Copyright 2013 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 <QFileInfo>
+
+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/logic/icons/MMCIcon.h b/logic/icons/MMCIcon.h
new file mode 100644
index 00000000..5e4b3bb6
--- /dev/null
+++ b/logic/icons/MMCIcon.h
@@ -0,0 +1,52 @@
+/* Copyright 2013 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 <QString>
+#include <QDateTime>
+#include <QIcon>
+struct MMCImage
+{
+ QIcon icon;
+ QString filename;
+ QDateTime changed;
+ bool present() const
+ {
+ return !icon.isNull();
+ }
+};
+
+struct 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/logic/lists/BaseVersionList.cpp b/logic/lists/BaseVersionList.cpp
new file mode 100644
index 00000000..6e2c5282
--- /dev/null
+++ b/logic/lists/BaseVersionList.cpp
@@ -0,0 +1,121 @@
+/* Copyright 2013 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 "logic/lists/BaseVersionList.h"
+#include "logic/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);
+}
+
+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 Qt::DisplayRole:
+ switch (index.column())
+ {
+ case NameColumn:
+ return version->name();
+
+ case TypeColumn:
+ return version->typeString();
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return version->descriptor();
+
+ case VersionPointerRole:
+ return qVariantFromValue(version);
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant BaseVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case NameColumn:
+ return "Name";
+
+ case TypeColumn:
+ return "Type";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case NameColumn:
+ return "The name of the version.";
+
+ case TypeColumn:
+ return "The version's type.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+int BaseVersionList::rowCount(const QModelIndex &parent) const
+{
+ // Return count
+ return count();
+}
+
+int BaseVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 2;
+}
diff --git a/logic/lists/BaseVersionList.h b/logic/lists/BaseVersionList.h
new file mode 100644
index 00000000..21b44e8d
--- /dev/null
+++ b/logic/lists/BaseVersionList.h
@@ -0,0 +1,120 @@
+/* Copyright 2013 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 <QObject>
+#include <QVariant>
+#include <QAbstractListModel>
+
+#include "logic/BaseVersion.h"
+
+class Task;
+
+/*!
+ * \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 BaseVersionList : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum ModelRoles
+ {
+ VersionPointerRole = 0x34B1CB48
+ };
+
+ enum VListColumns
+ {
+ // First column - Name
+ NameColumn = 0,
+
+ // Second column - Type
+ TypeColumn,
+
+ // Third column - Timestamp
+ TimeColumn
+ };
+
+ 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 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int rowCount(const QModelIndex &parent) const;
+ virtual int columnCount(const QModelIndex &parent) 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 of this instance type.
+ * This is the version that will be selected by default.
+ * By default, this is simply the first version in the list.
+ */
+ virtual BaseVersionPtr getLatestStable() const;
+
+ /*!
+ * Sorts the version list.
+ */
+ virtual void sort() = 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<BaseVersionPtr> versions) = 0;
+};
diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp
new file mode 100644
index 00000000..4902dc64
--- /dev/null
+++ b/logic/lists/ForgeVersionList.cpp
@@ -0,0 +1,439 @@
+/* Copyright 2013 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 <logic/net/NetJob.h>
+#include <logic/net/URLConstants.h>
+#include "MultiMC.h"
+
+#include <QtNetwork>
+#include <QtXml>
+#include <QRegExp>
+
+#include "logger/QsLog.h"
+
+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 3;
+}
+
+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<ForgeVersion>(m_vlist[index.row()]);
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case 0:
+ return version->name();
+
+ case 1:
+ return version->mcver;
+
+ case 2:
+ return version->typeString();
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return version->descriptor();
+
+ case VersionPointerRole:
+ return qVariantFromValue(m_vlist[index.row()]);
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant ForgeVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case 0:
+ return "Version";
+
+ case 1:
+ return "Minecraft";
+
+ case 2:
+ return "Type";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case 0:
+ return "The name of the version.";
+
+ case 1:
+ return "Minecraft version";
+
+ case 2:
+ return "The version's type.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+BaseVersionPtr ForgeVersionList::getLatestStable() const
+{
+ return BaseVersionPtr();
+}
+
+void ForgeVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ m_vlist = versions;
+ m_loaded = true;
+ endResetModel();
+ // NOW SORT!!
+ // sort();
+}
+
+void ForgeVersionList::sort()
+{
+ // 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 = MMC->metacache()->resolveEntry("minecraftforge", "list.json");
+ auto gradleForgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "json");
+
+ // verify by poking the server.
+ forgeListEntry->stale = true;
+ gradleForgeListEntry->stale = 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::parseForgeList(QList<BaseVersionPtr> &out)
+{
+ QByteArray data;
+ {
+ auto dlJob = listDownload;
+ auto filename = std::dynamic_pointer_cast<CacheDownload>(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, 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('/');
+ 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<ForgeVersion> fVersion(new ForgeVersion());
+ fVersion->universal_url = url;
+ fVersion->changelog_url = changelog_url;
+ fVersion->installer_url = installer_url;
+ fVersion->jobbuildver = jobbuildver;
+ fVersion->mcver = mcver;
+ if (installer_filename.isEmpty())
+ {
+ fVersion->filename = filename;
+ }
+ else
+ {
+ fVersion->filename = installer_filename;
+ }
+ fVersion->m_buildnr = build_nr;
+ out.append(fVersion);
+ }
+ }
+
+ return true;
+}
+
+bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
+{
+ QByteArray data;
+ {
+ auto dlJob = gradleListDownload;
+ auto filename = std::dynamic_pointer_cast<CacheDownload>(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<ForgeVersion> fVersion(new ForgeVersion());
+ fVersion->m_buildnr = number.value("build").toDouble();
+ fVersion->jobbuildver = number.value("version").toString();
+ fVersion->mcver = number.value("mcversion").toString();
+ fVersion->filename = "";
+ QString 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;
+ }
+ if (file.at(1).toString() == "installer")
+ {
+ fVersion->installer_url = QString("%1/%2-%3/%4-%2-%3-installer.%5").arg(
+ webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
+ file.at(0).toString());
+ installer_filename = QString("%1-%2-%3-installer.%4").arg(
+ artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString());
+ }
+ else if (file.at(1).toString() == "universal")
+ {
+ fVersion->universal_url = QString("%1/%2-%3/%4-%2-%3-universal.%5").arg(
+ webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
+ file.at(0).toString());
+ filename = QString("%1-%2-%3-universal.%4").arg(
+ artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString());
+ }
+ else if (file.at(1).toString() == "changelog")
+ {
+ fVersion->changelog_url = QString("%1/%2-%3/%4-%2-%3-changelog.%5").arg(
+ webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
+ file.at(0).toString());
+ }
+ }
+ if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty())
+ {
+ continue;
+ }
+ fVersion->filename = fVersion->installer_url.isEmpty() ? filename : installer_filename;
+ out.append(fVersion);
+ }
+
+ return true;
+}
+
+void ForgeListLoadTask::listDownloaded()
+{
+ QList<BaseVersionPtr> list;
+ bool ret = true;
+ if (!parseForgeList(list))
+ {
+ ret = false;
+ }
+ if (!parseForgeGradleList(list))
+ {
+ ret = false;
+ }
+
+ if (!ret)
+ {
+ return;
+ }
+ std::sort(list.begin(), list.end(), [](const BaseVersionPtr & l, const BaseVersionPtr & r)
+ { return (*l > *r); });
+
+ m_list->updateListData(list);
+
+ emitSucceeded();
+ return;
+}
+
+void ForgeListLoadTask::listFailed()
+{
+ auto reply = listDownload->m_reply;
+ if (reply)
+ {
+ QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString();
+ }
+ else
+ {
+ QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
+ }
+}
+void ForgeListLoadTask::gradleListFailed()
+{
+ auto reply = gradleListDownload->m_reply;
+ if (reply)
+ {
+ QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString();
+ }
+ else
+ {
+ QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
+ }
+}
diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h
new file mode 100644
index 00000000..b19d3f56
--- /dev/null
+++ b/logic/lists/ForgeVersionList.h
@@ -0,0 +1,128 @@
+/* Copyright 2013 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 <QObject>
+#include <QAbstractListModel>
+#include <QUrl>
+
+#include <QNetworkReply>
+#include "BaseVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/net/NetJob.h"
+
+class ForgeVersion;
+typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
+
+struct ForgeVersion : public BaseVersion
+{
+ virtual QString descriptor() override
+ {
+ return filename;
+ }
+ ;
+ virtual QString name() override
+ {
+ return "Forge " + jobbuildver;
+ }
+ ;
+ virtual QString typeString() const override
+ {
+ if (installer_url.isEmpty())
+ return "Universal";
+ else
+ return "Installer";
+ }
+
+ virtual bool operator<(BaseVersion &a) override
+ {
+ ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
+ if(!pa)
+ return true;
+ return m_buildnr < pa->m_buildnr;
+ }
+ virtual bool operator>(BaseVersion &a) override
+ {
+ ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
+ if(!pa)
+ return false;
+ return m_buildnr > pa->m_buildnr;
+ }
+ int m_buildnr = 0;
+ QString universal_url;
+ QString changelog_url;
+ QString installer_url;
+ QString jobbuildver;
+ QString mcver;
+ QString filename;
+};
+
+class ForgeVersionList : public BaseVersionList
+{
+ Q_OBJECT
+public:
+ friend class ForgeListLoadTask;
+
+ explicit ForgeVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+ virtual bool isLoaded();
+ virtual const BaseVersionPtr at(int i) const;
+ virtual int count() const;
+ virtual void sort();
+
+ virtual BaseVersionPtr getLatestStable() const;
+
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+
+protected:
+ QList<BaseVersionPtr> m_vlist;
+
+ bool m_loaded = false;
+
+protected
+slots:
+ virtual void updateListData(QList<BaseVersionPtr> versions);
+};
+
+class ForgeListLoadTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit ForgeListLoadTask(ForgeVersionList *vlist);
+
+ virtual void executeTask();
+
+protected
+slots:
+ void listDownloaded();
+ void listFailed();
+ void gradleListFailed();
+
+protected:
+ NetJobPtr listJob;
+ ForgeVersionList *m_list;
+
+ CacheDownloadPtr listDownload;
+ CacheDownloadPtr gradleListDownload;
+
+private:
+ bool parseForgeList(QList<BaseVersionPtr> &out);
+ bool parseForgeGradleList(QList<BaseVersionPtr> &out);
+};
diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp
new file mode 100644
index 00000000..0d4eab95
--- /dev/null
+++ b/logic/lists/InstanceList.cpp
@@ -0,0 +1,608 @@
+/* Copyright 2013 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 <QDir>
+#include <QSet>
+#include <QFile>
+#include <QDirIterator>
+#include <QThread>
+#include <QTextStream>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QXmlStreamReader>
+#include <QRegularExpression>
+#include <pathutils.h>
+
+#include "MultiMC.h"
+#include "logic/lists/InstanceList.h"
+#include "logic/icons/IconList.h"
+#include "logic/lists/MinecraftVersionList.h"
+#include "logic/BaseInstance.h"
+#include "logic/InstanceFactory.h"
+#include "logger/QsLog.h"
+
+const static int GROUP_FILE_FORMAT_VERSION = 1;
+
+InstanceList::InstanceList(const QString &instDir, QObject *parent)
+ : QAbstractListModel(parent), m_instDir(instDir)
+{
+ connect(MMC, &MultiMC::aboutToQuit, this, &InstanceList::saveGroupList);
+
+ if (!QDir::current().exists(m_instDir))
+ {
+ QDir::current().mkpath(m_instDir);
+ }
+
+ connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this,
+ &InstanceList::loadList);
+}
+
+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<BaseInstance *>(index.internalPointer());
+ switch (role)
+ {
+ case InstancePointerRole:
+ {
+ QVariant v = qVariantFromValue((void *)pdata);
+ return v;
+ }
+ case Qt::DisplayRole:
+ {
+ return pdata->name();
+ }
+ case Qt::ToolTipRole:
+ {
+ return pdata->instanceRoot();
+ }
+ case Qt::DecorationRole:
+ {
+ QString key = pdata->iconKey();
+ return MMC->icons()->getIcon(key);
+ }
+ // for now.
+ case KCategorizedSortFilterProxyModel::CategorySortRole:
+ case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
+ {
+ 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::saveGroupList()
+{
+ QString groupFileName = m_instDir + "/instgroups.json";
+ QFile groupFile(groupFileName);
+
+ // if you can't open the file, fail
+ if (!groupFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ {
+ // An error occurred. Ignore it.
+ QLOG_ERROR() << "Failed to save instance group file.";
+ return;
+ }
+ QTextStream out(&groupFile);
+ QMap<QString, QSet<QString>> 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<QString> set;
+ set.insert(id);
+ groupMap[group] = set;
+ }
+ else
+ {
+ QSet<QString> &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);
+ groupFile.write(doc.toJson());
+ groupFile.close();
+}
+
+void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
+{
+ QString groupFileName = m_instDir + "/instgroups.json";
+
+ // if there's no group file, fail
+ if (!QFileInfo(groupFileName).exists())
+ return;
+
+ QFile groupFile(groupFileName);
+
+ // if you can't open the file, fail
+ if (!groupFile.open(QIODevice::ReadOnly))
+ {
+ // An error occurred. Ignore it.
+ QLOG_ERROR() << "Failed to read instance group file.";
+ return;
+ }
+
+ QTextStream in(&groupFile);
+ QString jsonStr = in.readAll();
+ groupFile.close();
+
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
+
+ // if the json was bad, fail
+ if (error.error != QJsonParseError::NoError)
+ {
+ QLOG_ERROR() << 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())
+ {
+ QLOG_WARN() << "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())
+ {
+ QLOG_WARN() << "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())
+ {
+ QLOG_WARN() << 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())
+ {
+ QLOG_WARN() << 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;
+ }
+ }
+}
+
+struct FTBRecord
+{
+ QString dir;
+ QString name;
+ QString logo;
+ QString mcVersion;
+ QString description;
+};
+
+void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
+{
+ QList<FTBRecord> records;
+ QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString());
+ QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString());
+ if (!dir.exists())
+ {
+ QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your "
+ "settings.";
+ return;
+ }
+ else if (!dataDir.exists())
+ {
+ QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
+ return;
+ }
+ dir.cd("ModPacks");
+ auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name);
+ for(auto filename: allFiles)
+ {
+ if(!filename.endsWith(".xml"))
+ continue;
+ auto fpath = dir.absoluteFilePath(filename);
+ QFile f(fpath);
+ QLOG_INFO() << "Discovering FTB instances -- " << fpath;
+ if (!f.open(QFile::ReadOnly))
+ continue;
+
+ // read the FTB packs XML.
+ QXmlStreamReader reader(&f);
+ while (!reader.atEnd())
+ {
+ switch (reader.readNext())
+ {
+ case QXmlStreamReader::StartElement:
+ {
+ if (reader.name() == "modpack")
+ {
+ QXmlStreamAttributes attrs = reader.attributes();
+ FTBRecord record;
+ record.dir = attrs.value("dir").toString();
+ QDir test(dataDir.absoluteFilePath(record.dir));
+ if(!test.exists())
+ continue;
+ record.name = attrs.value("name").toString();
+ record.logo = attrs.value("logo").toString();
+ record.mcVersion = attrs.value("mcVersion").toString();
+ record.description = attrs.value("description").toString();
+ records.append(record);
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement:
+ break;
+ case QXmlStreamReader::Characters:
+ break;
+ default:
+ break;
+ }
+ }
+ f.close();
+ }
+
+ if(!records.size())
+ {
+ QLOG_INFO() << "No FTB instances to load.";
+ return;
+ }
+ QLOG_INFO() << "Loading FTB instances! -- got " << records.size();
+ // process the records we acquired.
+ for (auto record : records)
+ {
+ auto instanceDir = dataDir.absoluteFilePath(record.dir);
+ QLOG_INFO() << "Loading FTB instance from " << instanceDir;
+ auto templateDir = dir.absoluteFilePath(record.dir);
+ if (!QFileInfo(instanceDir).exists())
+ {
+ continue;
+ }
+
+ QString iconKey = record.logo;
+ iconKey.remove(QRegularExpression("\\..*"));
+ MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo),
+ MMCIcon::Transient);
+
+ if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists())
+ {
+ QLOG_INFO() << "Converting " << record.name << " as new.";
+ BaseInstance *instPtr = NULL;
+ auto &factory = InstanceFactory::get();
+ auto version = MMC->minecraftlist()->findVersion(record.mcVersion);
+ if (!version)
+ {
+ QLOG_ERROR() << "Can't load instance " << instanceDir
+ << " because minecraft version " << record.mcVersion
+ << " can't be resolved.";
+ continue;
+ }
+ auto error = factory.createInstance(instPtr, version, instanceDir,
+ InstanceFactory::FTBInstance);
+
+ if (!instPtr || error != InstanceFactory::NoCreateError)
+ continue;
+
+ instPtr->setGroupInitial("FTB");
+ instPtr->setName(record.name);
+ instPtr->setIconKey(iconKey);
+ instPtr->setIntendedVersionId(record.mcVersion);
+ instPtr->setNotes(record.description);
+ continueProcessInstance(instPtr, error, instanceDir, groupMap);
+ }
+ else
+ {
+ QLOG_INFO() << "Loading existing " << record.name;
+ BaseInstance *instPtr = NULL;
+ auto error = InstanceFactory::get().loadInstance(instPtr, instanceDir);
+ if (!instPtr || error != InstanceFactory::NoCreateError)
+ continue;
+ instPtr->setGroupInitial("FTB");
+ instPtr->setName(record.name);
+ instPtr->setIconKey(iconKey);
+ if (instPtr->intendedVersionId() != record.mcVersion)
+ instPtr->setIntendedVersionId(record.mcVersion);
+ instPtr->setNotes(record.description);
+ continueProcessInstance(instPtr, error, instanceDir, groupMap);
+ }
+ }
+}
+
+InstanceList::InstListError InstanceList::loadList()
+{
+ // load the instance groups
+ QMap<QString, QString> groupMap;
+ loadGroupList(groupMap);
+
+ beginResetModel();
+
+ m_instances.clear();
+
+ {
+ QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
+ QDirIterator::FollowSymlinks);
+ while (iter.hasNext())
+ {
+ QString subDir = iter.next();
+ if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists())
+ continue;
+ QLOG_INFO() << "Loading MultiMC instance from " << subDir;
+ BaseInstance *instPtr = NULL;
+ auto error = InstanceFactory::get().loadInstance(instPtr, subDir);
+ continueProcessInstance(instPtr, error, subDir, groupMap);
+ }
+ }
+
+ if (MMC->settings()->get("TrackFTBInstances").toBool())
+ {
+ loadForgeInstances(groupMap);
+ }
+
+ 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 (m_instances.isEmpty())
+ {
+ return InstancePtr();
+ }
+
+ QListIterator<InstancePtr> iter(m_instances);
+ InstancePtr inst;
+ while (iter.hasNext())
+ {
+ inst = iter.next();
+ if (inst->id() == instId)
+ break;
+ }
+ if (inst->id() != instId)
+ return InstancePtr();
+ else
+ return iter.peekPrevious();
+}
+
+QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
+{
+ return index(getInstIndex(getInstanceById(id).get()));
+}
+
+int InstanceList::getInstIndex(BaseInstance *inst) const
+{
+ for (int i = 0; i < m_instances.count(); i++)
+ {
+ if (inst == m_instances[i].get())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error,
+ const QDir &dir, QMap<QString, QString> &groupMap)
+{
+ if (error != InstanceFactory::NoLoadError && error != InstanceFactory::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;
+ }
+ QLOG_ERROR() << errorMsg.toUtf8();
+ }
+ else if (!instPtr)
+ {
+ QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
+ .arg(QFileInfo(dir.absolutePath()).baseName())
+ .toUtf8();
+ }
+ else
+ {
+ auto iter = groupMap.find(instPtr->id());
+ if (iter != groupMap.end())
+ {
+ instPtr->setGroupInitial((*iter));
+ }
+ QLOG_INFO() << "Loaded instance " << instPtr->name() << " from " << dir.absolutePath();
+ instPtr->setParent(this);
+ m_instances.append(std::shared_ptr<BaseInstance>(instPtr));
+ connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
+ SLOT(propertiesChanged(BaseInstance *)));
+ connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
+ connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
+ SLOT(instanceNuked(BaseInstance *)));
+ }
+}
+
+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));
+ }
+}
+
+InstanceProxyModel::InstanceProxyModel(QObject *parent)
+ : KCategorizedSortFilterProxyModel(parent)
+{
+ // disable since by default we are globally sorting by date:
+ setCategorizedModel(true);
+}
+
+bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
+ const QModelIndex &right) const
+{
+ BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer());
+ BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer());
+ QString sortMode = MMC->settings()->get("InstSortMode").toString();
+ if (sortMode == "LastLaunch")
+ {
+ return pdataLeft->lastLaunch() > pdataRight->lastLaunch();
+ }
+ else
+ {
+ return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0;
+ }
+}
diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h
new file mode 100644
index 00000000..0ce808e5
--- /dev/null
+++ b/logic/lists/InstanceList.h
@@ -0,0 +1,139 @@
+/* Copyright 2013 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 <QObject>
+#include <QAbstractListModel>
+#include <QSet>
+#include "categorizedsortfilterproxymodel.h"
+#include <QIcon>
+
+#include "logic/BaseInstance.h"
+
+class BaseInstance;
+
+class QDir;
+
+class InstanceList : public QAbstractListModel
+{
+ Q_OBJECT
+private:
+ void loadGroupList(QMap<QString, QString> &groupList);
+
+private
+slots:
+ void saveGroupList();
+
+public:
+ explicit InstanceList(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
+ {
+ InstancePointerRole = 0x34B1CB48 ///< Return pointer to real 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
+ };
+
+ 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();
+signals:
+ void dataIsInvalid();
+
+public
+slots:
+ void on_InstFolderChanged(const Setting &setting, QVariant value);
+
+ /*!
+ * \brief Loads the instance list. Triggers notifications.
+ */
+ InstListError loadList();
+ void loadForgeInstances(QMap<QString, QString> groupMap);
+
+private
+slots:
+ void propertiesChanged(BaseInstance *inst);
+ void instanceNuked(BaseInstance *inst);
+ void groupChanged();
+
+private:
+ int getInstIndex(BaseInstance *inst) const;
+
+ void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir,
+ QMap<QString, QString> &groupMap);
+
+protected:
+ QString m_instDir;
+ QList<InstancePtr> m_instances;
+ QSet<QString> m_groups;
+};
+
+class InstanceProxyModel : public KCategorizedSortFilterProxyModel
+{
+public:
+ explicit InstanceProxyModel(QObject *parent = 0);
+
+protected:
+ virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const;
+};
diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp
new file mode 100644
index 00000000..eb1c5650
--- /dev/null
+++ b/logic/lists/JavaVersionList.cpp
@@ -0,0 +1,242 @@
+/* Copyright 2013 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 "JavaVersionList.h"
+#include "MultiMC.h"
+
+#include <QtNetwork>
+#include <QtXml>
+#include <QRegExp>
+
+#include "logger/QsLog.h"
+#include "logic/JavaCheckerJob.h"
+#include "logic/JavaUtils.h"
+
+JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent)
+{
+}
+
+Task *JavaVersionList::getLoadTask()
+{
+ return new JavaListLoadTask(this);
+}
+
+const BaseVersionPtr JavaVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+bool JavaVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+int JavaVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+int JavaVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 3;
+}
+
+QVariant JavaVersionList::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ auto version = std::dynamic_pointer_cast<JavaVersion>(m_vlist[index.row()]);
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case 0:
+ return version->id;
+
+ case 1:
+ return version->arch;
+
+ case 2:
+ return version->path;
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ return version->descriptor();
+
+ case VersionPointerRole:
+ return qVariantFromValue(m_vlist[index.row()]);
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case 0:
+ return "Version";
+
+ case 1:
+ return "Arch";
+
+ case 2:
+ return "Path";
+
+ default:
+ return QVariant();
+ }
+
+ case Qt::ToolTipRole:
+ switch (section)
+ {
+ case 0:
+ return "The name of the version.";
+
+ case 1:
+ return "The architecture this version is for.";
+
+ case 2:
+ return "Path to this Java version.";
+
+ default:
+ return QVariant();
+ }
+
+ default:
+ return QVariant();
+ }
+}
+
+BaseVersionPtr JavaVersionList::getTopRecommended() const
+{
+ auto first = m_vlist.first();
+ if(first != nullptr)
+ {
+ return first;
+ }
+ else
+ {
+ return BaseVersionPtr();
+ }
+}
+
+void JavaVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ m_vlist = versions;
+ m_loaded = true;
+ endResetModel();
+ // NOW SORT!!
+ // sort();
+}
+
+void JavaVersionList::sort()
+{
+ // NO-OP for now
+}
+
+JavaListLoadTask::JavaListLoadTask(JavaVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentRecommended = NULL;
+}
+
+JavaListLoadTask::~JavaListLoadTask()
+{
+}
+
+void JavaListLoadTask::executeTask()
+{
+ setStatus(tr("Detecting Java installations..."));
+
+ JavaUtils ju;
+ QList<QString> candidate_paths = ju.FindJavaPaths();
+
+ m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
+ connect(m_job.get(), SIGNAL(finished(QList<JavaCheckResult>)), this, SLOT(javaCheckerFinished(QList<JavaCheckResult>)));
+ connect(m_job.get(), SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int)));
+
+ QLOG_DEBUG() << "Probing the following Java paths: ";
+ int id = 0;
+ for(QString candidate : candidate_paths)
+ {
+ QLOG_DEBUG() << " " << candidate;
+
+ auto candidate_checker = new JavaChecker();
+ candidate_checker->path = candidate;
+ candidate_checker->id = id;
+ m_job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker));
+
+ id++;
+ }
+
+ m_job->start();
+}
+
+void JavaListLoadTask::checkerProgress(int current, int total)
+{
+ float progress = (current * 100.0) / total;
+ this->setProgress((int) progress);
+}
+
+void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results)
+{
+ QList<JavaVersionPtr> candidates;
+ m_job.reset();
+
+ QLOG_DEBUG() << "Found the following valid Java installations:";
+ for(JavaCheckResult result : results)
+ {
+ if(result.valid)
+ {
+ JavaVersionPtr javaVersion(new JavaVersion());
+
+ javaVersion->id = result.javaVersion;
+ javaVersion->arch = result.mojangPlatform;
+ javaVersion->path = result.path;
+ candidates.append(javaVersion);
+
+ QLOG_DEBUG() << " " << javaVersion->id << javaVersion->arch << javaVersion->path;
+ }
+ }
+
+ QList<BaseVersionPtr> javas_bvp;
+ for (auto &java : candidates)
+ {
+ //QLOG_INFO() << java->id << java->arch << " at " << java->path;
+ BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);
+
+ if (bp_java)
+ {
+ javas_bvp.append(bp_java);
+ }
+ }
+
+ m_list->updateListData(javas_bvp);
+ emitSucceeded();
+}
diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h
new file mode 100644
index 00000000..e6cc8e5f
--- /dev/null
+++ b/logic/lists/JavaVersionList.h
@@ -0,0 +1,96 @@
+/* Copyright 2013 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 <QObject>
+#include <QAbstractListModel>
+
+#include "BaseVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/JavaCheckerJob.h"
+
+class JavaListLoadTask;
+
+struct JavaVersion : public BaseVersion
+{
+ virtual QString descriptor()
+ {
+ return id;
+ }
+
+ virtual QString name()
+ {
+ return id;
+ }
+
+ virtual QString typeString() const
+ {
+ return arch;
+ }
+
+ QString id;
+ QString arch;
+ QString path;
+};
+
+typedef std::shared_ptr<JavaVersion> JavaVersionPtr;
+
+class JavaVersionList : public BaseVersionList
+{
+ Q_OBJECT
+public:
+ explicit JavaVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+ virtual bool isLoaded();
+ virtual const BaseVersionPtr at(int i) const;
+ virtual int count() const;
+ virtual void sort();
+
+ virtual BaseVersionPtr getTopRecommended() const;
+
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ virtual int columnCount(const QModelIndex &parent) const;
+
+public
+slots:
+ virtual void updateListData(QList<BaseVersionPtr> versions);
+
+protected:
+ QList<BaseVersionPtr> m_vlist;
+
+ bool m_loaded = false;
+};
+
+class JavaListLoadTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit JavaListLoadTask(JavaVersionList *vlist);
+ ~JavaListLoadTask();
+
+ virtual void executeTask();
+public slots:
+ void javaCheckerFinished(QList<JavaCheckResult> results);
+ void checkerProgress(int current, int total);
+
+protected:
+ std::shared_ptr<JavaCheckerJob> m_job;
+ JavaVersionList *m_list;
+ JavaVersion *m_currentRecommended;
+};
diff --git a/logic/lists/LwjglVersionList.cpp b/logic/lists/LwjglVersionList.cpp
new file mode 100644
index 00000000..df46d7be
--- /dev/null
+++ b/logic/lists/LwjglVersionList.cpp
@@ -0,0 +1,199 @@
+/* Copyright 2013 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 "LwjglVersionList.h"
+#include "MultiMC.h"
+
+#include <QtNetwork>
+#include <QtXml>
+#include <QRegExp>
+
+#include "logger/QsLog.h"
+
+#define RSS_URL "http://sourceforge.net/api/file/index/project-id/58488/mtime/desc/rss"
+
+LWJGLVersionList::LWJGLVersionList(QObject *parent) : QAbstractListModel(parent)
+{
+ setLoading(false);
+}
+
+QVariant LWJGLVersionList::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.row() > count())
+ return QVariant();
+
+ const PtrLWJGLVersion version = at(index.row());
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return version->name();
+
+ case Qt::ToolTipRole:
+ return version->url();
+
+ default:
+ return QVariant();
+ }
+}
+
+QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return "Version";
+
+ case Qt::ToolTipRole:
+ return "LWJGL version name.";
+
+ default:
+ return QVariant();
+ }
+}
+
+int LWJGLVersionList::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+bool LWJGLVersionList::isLoading() const
+{
+ return m_loading;
+}
+
+void LWJGLVersionList::loadList()
+{
+ Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)");
+
+ setLoading(true);
+ auto worker = MMC->qnam();
+ QNetworkRequest req(QUrl(RSS_URL));
+ req.setRawHeader("Accept", "text/xml");
+ req.setRawHeader("User-Agent", "MultiMC/5.0 (Uncached)");
+ reply = worker->get(req);
+ connect(reply, SIGNAL(finished()), SLOT(netRequestComplete()));
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+void LWJGLVersionList::netRequestComplete()
+{
+ if (reply->error() == QNetworkReply::NoError)
+ {
+ QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip");
+ Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", "LWJGL regex is invalid");
+
+ QDomDocument doc;
+
+ QString xmlErrorMsg;
+ int errorLine;
+ if (!doc.setContent(reply->readAll(), false, &xmlErrorMsg, &errorLine))
+ {
+ failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " +
+ QString::number(errorLine));
+ setLoading(false);
+ return;
+ }
+
+ QDomNodeList items = doc.elementsByTagName("item");
+
+ QList<PtrLWJGLVersion> tempList;
+
+ for (int i = 0; i < items.length(); i++)
+ {
+ Q_ASSERT_X(items.at(i).isElement(), "load LWJGL list",
+ "XML element isn't an element... wat?");
+
+ QDomElement linkElement = getDomElementByTagName(items.at(i).toElement(), "link");
+ if (linkElement.isNull())
+ {
+ QLOG_INFO() << "Link element" << i << "in RSS feed doesn't exist! Skipping.";
+ continue;
+ }
+
+ QString link = linkElement.text();
+
+ // Make sure it's a download link.
+ if (link.endsWith("/download") && link.contains(lwjglRegex))
+ {
+ QString name = link.mid(lwjglRegex.indexIn(link) + 6);
+ // Subtract 4 here to remove the .zip file extension.
+ name = name.left(lwjglRegex.matchedLength() - 10);
+
+ QUrl url(link);
+ if (!url.isValid())
+ {
+ QLOG_INFO() << "LWJGL version URL isn't valid:" << link << "Skipping.";
+ continue;
+ }
+
+ tempList.append(LWJGLVersion::Create(name, link));
+ }
+ }
+
+ beginResetModel();
+ m_vlist.swap(tempList);
+ endResetModel();
+
+ QLOG_INFO() << "Loaded LWJGL list.";
+ finished();
+ }
+ else
+ {
+ failed("Failed to load LWJGL list. Network error: " + reply->errorString());
+ }
+
+ setLoading(false);
+ reply->deleteLater();
+}
+
+const PtrLWJGLVersion LWJGLVersionList::getVersion(const QString &versionName)
+{
+ for (int i = 0; i < count(); i++)
+ {
+ QString name = at(i)->name();
+ if (name == versionName)
+ return at(i);
+ }
+ return PtrLWJGLVersion();
+}
+
+void LWJGLVersionList::failed(QString msg)
+{
+ QLOG_INFO() << msg;
+ emit loadListFailed(msg);
+}
+
+void LWJGLVersionList::finished()
+{
+ emit loadListFinished();
+}
+
+void LWJGLVersionList::setLoading(bool loading)
+{
+ m_loading = loading;
+ emit loadingStateUpdated(m_loading);
+}
diff --git a/logic/lists/LwjglVersionList.h b/logic/lists/LwjglVersionList.h
new file mode 100644
index 00000000..fa57e8eb
--- /dev/null
+++ b/logic/lists/LwjglVersionList.h
@@ -0,0 +1,148 @@
+/* Copyright 2013 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 <QObject>
+#include <QAbstractListModel>
+#include <QUrl>
+#include <QNetworkReply>
+
+#include <memory>
+
+class LWJGLVersion;
+typedef std::shared_ptr<LWJGLVersion> PtrLWJGLVersion;
+
+class LWJGLVersion : public QObject
+{
+ Q_OBJECT
+
+ LWJGLVersion(const QString &name, const QString &url, QObject *parent = 0)
+ : QObject(parent), m_name(name), m_url(url)
+ {
+ }
+
+public:
+
+ static PtrLWJGLVersion Create(const QString &name, const QString &url, QObject *parent = 0)
+ {
+ return PtrLWJGLVersion(new LWJGLVersion(name, url, parent));
+ }
+ ;
+
+ QString name() const
+ {
+ return m_name;
+ }
+
+ QString url() const
+ {
+ return m_url;
+ }
+
+protected:
+ QString m_name;
+ QString m_url;
+};
+
+class LWJGLVersionList : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit LWJGLVersionList(QObject *parent = 0);
+
+ bool isLoaded()
+ {
+ return m_vlist.length() > 0;
+ }
+
+ const PtrLWJGLVersion getVersion(const QString &versionName);
+ PtrLWJGLVersion at(int index)
+ {
+ return m_vlist[index];
+ }
+ const PtrLWJGLVersion at(int index) const
+ {
+ return m_vlist[index];
+ }
+
+ int count() const
+ {
+ return m_vlist.length();
+ }
+
+ 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
+ {
+ return count();
+ }
+ virtual int columnCount(const QModelIndex &parent) const;
+
+ virtual bool isLoading() const;
+ virtual bool errored() const
+ {
+ return m_errored;
+ }
+
+ virtual QString lastErrorMsg() const
+ {
+ return m_lastErrorMsg;
+ }
+
+public
+slots:
+ /*!
+ * Loads the version list.
+ * This is done asynchronously. On success, the loadListFinished() signal will
+ * be emitted. The list model will be reset as well, resulting in the modelReset()
+ * signal being emitted. Note that the model will be reset before loadListFinished() is
+ * emitted.
+ * If loading the list failed, the loadListFailed(QString msg),
+ * signal will be emitted.
+ */
+ virtual void loadList();
+
+signals:
+ /*!
+ * Emitted when the list either starts or finishes loading.
+ * \param loading Whether or not the list is loading.
+ */
+ void loadingStateUpdated(bool loading);
+
+ void loadListFinished();
+
+ void loadListFailed(QString msg);
+
+private:
+ QList<PtrLWJGLVersion> m_vlist;
+
+ QNetworkReply *m_netReply;
+ QNetworkReply *reply;
+
+ bool m_loading;
+ bool m_errored;
+ QString m_lastErrorMsg;
+
+ void failed(QString msg);
+
+ void finished();
+
+ void setLoading(bool loading);
+
+private
+slots:
+ virtual void netRequestComplete();
+};
diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp
new file mode 100644
index 00000000..91f86df0
--- /dev/null
+++ b/logic/lists/MinecraftVersionList.cpp
@@ -0,0 +1,286 @@
+/* Copyright 2013 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 "MinecraftVersionList.h"
+#include "MultiMC.h"
+#include "logic/net/URLConstants.h"
+
+#include <QtXml>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+#include <QJsonParseError>
+
+#include <QtAlgorithms>
+
+#include <QtNetwork>
+
+MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
+{
+}
+
+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();
+}
+
+bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
+{
+ auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
+ auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
+ return left->timestamp > right->timestamp;
+}
+
+void MinecraftVersionList::sort()
+{
+ beginResetModel();
+ qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
+ endResetModel();
+}
+
+BaseVersionPtr MinecraftVersionList::getLatestStable() const
+{
+ for (int i = 0; i < m_vlist.length(); i++)
+ {
+ auto ver = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist.at(i));
+ if (ver->is_latest && !ver->is_snapshot)
+ {
+ return m_vlist.at(i);
+ }
+ }
+ return BaseVersionPtr();
+}
+
+void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ m_vlist = versions;
+ m_loaded = true;
+ endResetModel();
+ // NOW SORT!!
+ sort();
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+inline QDateTime timeFromS3Time(QString str)
+{
+ return QDateTime::fromString(str, Qt::ISODate);
+}
+
+MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentStable = NULL;
+ vlistReply = nullptr;
+ legacyWhitelist.insert("1.5.2");
+ legacyWhitelist.insert("1.5.1");
+ legacyWhitelist.insert("1.5");
+ legacyWhitelist.insert("1.4.7");
+ legacyWhitelist.insert("1.4.6");
+ legacyWhitelist.insert("1.4.5");
+ legacyWhitelist.insert("1.4.4");
+ legacyWhitelist.insert("1.4.3");
+ legacyWhitelist.insert("1.4.2");
+ legacyWhitelist.insert("1.4.1");
+ legacyWhitelist.insert("1.4");
+ legacyWhitelist.insert("1.3.2");
+ legacyWhitelist.insert("1.3.1");
+ legacyWhitelist.insert("1.3");
+ legacyWhitelist.insert("1.2.5");
+ legacyWhitelist.insert("1.2.4");
+ legacyWhitelist.insert("1.2.3");
+ legacyWhitelist.insert("1.2.2");
+ legacyWhitelist.insert("1.2.1");
+ legacyWhitelist.insert("1.1");
+ legacyWhitelist.insert("1.0.1");
+ legacyWhitelist.insert("1.0");
+}
+
+MCVListLoadTask::~MCVListLoadTask()
+{
+}
+
+void MCVListLoadTask::executeTask()
+{
+ setStatus(tr("Loading instance version list..."));
+ auto worker = MMC->qnam();
+ vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.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;
+ }
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
+ vlistReply->deleteLater();
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed("Error parsing version list JSON:" + jsonError.errorString());
+ return;
+ }
+
+ if (!jsonDoc.isObject())
+ {
+ emitFailed("Error parsing version list JSON: jsonDoc is not an object");
+ return;
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ // Get the ID of the latest release and the latest snapshot.
+ if (!root.value("latest").isObject())
+ {
+ emitFailed("Error parsing version list JSON: version list is missing 'latest' object");
+ return;
+ }
+
+ QJsonObject latest = root.value("latest").toObject();
+
+ QString latestReleaseID = latest.value("release").toString("");
+ QString latestSnapshotID = latest.value("snapshot").toString("");
+ if (latestReleaseID.isEmpty())
+ {
+ emitFailed("Error parsing version list JSON: latest release field is missing");
+ return;
+ }
+ if (latestSnapshotID.isEmpty())
+ {
+ emitFailed("Error parsing version list JSON: latest snapshot field is missing");
+ return;
+ }
+
+ // Now, get the array of versions.
+ if (!root.value("versions").isArray())
+ {
+ emitFailed(
+ "Error parsing version list JSON: version list object is missing 'versions' array");
+ return;
+ }
+ QJsonArray versions = root.value("versions").toArray();
+
+ QList<BaseVersionPtr> tempList;
+ for (int i = 0; i < versions.count(); i++)
+ {
+ bool is_snapshot = false;
+ bool is_latest = false;
+
+ // Load the version info.
+ if (!versions[i].isObject())
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ QJsonObject version = versions[i].toObject();
+ QString versionID = version.value("id").toString("");
+ QString versionTimeStr = version.value("releaseTime").toString("");
+ QString versionTypeStr = version.value("type").toString("");
+ if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+
+ // Parse the timestamp.
+ QDateTime versionTime = timeFromS3Time(versionTimeStr);
+ if (!versionTime.isValid())
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ // Parse the type.
+ MinecraftVersion::VersionType versionType;
+ // OneSix or Legacy. use filter to determine type
+ if (versionTypeStr == "release")
+ {
+ versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
+ : MinecraftVersion::OneSix;
+ is_latest = (versionID == latestReleaseID);
+ is_snapshot = false;
+ }
+ else if (versionTypeStr == "snapshot") // It's a snapshot... yay
+ {
+ versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
+ : MinecraftVersion::OneSix;
+ is_latest = (versionID == latestSnapshotID);
+ is_snapshot = true;
+ }
+ else if (versionTypeStr == "old_alpha")
+ {
+ versionType = MinecraftVersion::Nostalgia;
+ is_latest = false;
+ is_snapshot = false;
+ }
+ else if (versionTypeStr == "old_beta")
+ {
+ versionType = MinecraftVersion::Legacy;
+ is_latest = false;
+ is_snapshot = false;
+ }
+ else
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ // Get the download URL.
+ QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
+
+ // Now, we construct the version object and add it to the list.
+ std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
+ mcVersion->m_name = mcVersion->m_descriptor = versionID;
+ mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
+ mcVersion->download_url = dlUrl;
+ mcVersion->is_latest = is_latest;
+ mcVersion->is_snapshot = is_snapshot;
+ mcVersion->type = versionType;
+ tempList.append(mcVersion);
+ }
+ m_list->updateListData(tempList);
+
+ emitSucceeded();
+ return;
+}
diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h
new file mode 100644
index 00000000..82af1009
--- /dev/null
+++ b/logic/lists/MinecraftVersionList.h
@@ -0,0 +1,74 @@
+/* Copyright 2013 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 <QObject>
+#include <QList>
+#include <QSet>
+
+#include "BaseVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/MinecraftVersion.h"
+
+class MCVListLoadTask;
+class QNetworkReply;
+
+class MinecraftVersionList : public BaseVersionList
+{
+ Q_OBJECT
+public:
+ friend class MCVListLoadTask;
+
+ explicit MinecraftVersionList(QObject *parent = 0);
+
+ virtual Task *getLoadTask();
+ virtual bool isLoaded();
+ virtual const BaseVersionPtr at(int i) const;
+ virtual int count() const;
+ virtual void sort();
+
+ virtual BaseVersionPtr getLatestStable() const;
+
+protected:
+ QList<BaseVersionPtr> m_vlist;
+
+ bool m_loaded = false;
+
+protected
+slots:
+ virtual void updateListData(QList<BaseVersionPtr> versions);
+};
+
+class MCVListLoadTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit MCVListLoadTask(MinecraftVersionList *vlist);
+ ~MCVListLoadTask();
+
+ virtual void executeTask();
+
+protected
+slots:
+ void list_downloaded();
+
+protected:
+ QNetworkReply *vlistReply;
+ MinecraftVersionList *m_list;
+ MinecraftVersion *m_currentStable;
+ QSet<QString> legacyWhitelist;
+};
diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp
new file mode 100644
index 00000000..27d2a250
--- /dev/null
+++ b/logic/net/ByteArrayDownload.cpp
@@ -0,0 +1,82 @@
+/* Copyright 2013 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 "ByteArrayDownload.h"
+#include "MultiMC.h"
+#include "logger/QsLog.h"
+
+ByteArrayDownload::ByteArrayDownload(QUrl url) : NetAction()
+{
+ m_url = url;
+ m_status = Job_NotStarted;
+}
+
+void ByteArrayDownload::start()
+{
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void ByteArrayDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
+}
+
+void ByteArrayDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit()
+ << "Network error: " << error;
+ m_status = Job_Failed;
+}
+
+void ByteArrayDownload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ m_data = m_reply->readAll();
+ m_reply.reset();
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ // else the download failed
+ else
+ {
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+}
+
+void ByteArrayDownload::downloadReadyRead()
+{
+ // ~_~
+}
diff --git a/logic/net/ByteArrayDownload.h b/logic/net/ByteArrayDownload.h
new file mode 100644
index 00000000..0d90abc2
--- /dev/null
+++ b/logic/net/ByteArrayDownload.h
@@ -0,0 +1,44 @@
+/* Copyright 2013 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 "NetAction.h"
+
+typedef std::shared_ptr<class ByteArrayDownload> ByteArrayDownloadPtr;
+class ByteArrayDownload : public NetAction
+{
+ Q_OBJECT
+public:
+ ByteArrayDownload(QUrl url);
+ static ByteArrayDownloadPtr make(QUrl url)
+ {
+ return ByteArrayDownloadPtr(new ByteArrayDownload(url));
+ }
+
+public:
+ /// if not saving to file, downloaded data is placed here
+ QByteArray m_data;
+
+public
+slots:
+ virtual void start();
+
+protected
+slots:
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ void downloadError(QNetworkReply::NetworkError error);
+ void downloadFinished();
+ void downloadReadyRead();
+};
diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp
new file mode 100644
index 00000000..d2a9bdee
--- /dev/null
+++ b/logic/net/CacheDownload.cpp
@@ -0,0 +1,169 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "CacheDownload.h"
+#include <pathutils.h>
+
+#include <QCryptographicHash>
+#include <QFileInfo>
+#include <QDateTime>
+#include "logger/QsLog.h"
+
+CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry)
+ : NetAction(), md5sum(QCryptographicHash::Md5)
+{
+ m_url = url;
+ m_entry = entry;
+ m_target_path = entry->getFullPath();
+ m_status = Job_NotStarted;
+}
+
+void CacheDownload::start()
+{
+ m_status = Job_InProgress;
+ if (!m_entry->stale)
+ {
+ m_status = Job_Finished;
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ // create a new save file
+ m_output_file.reset(new QSaveFile(m_target_path));
+
+ // if there already is a file and md5 checking is in effect and it can be opened
+ if (!ensureFilePathExists(m_target_path))
+ {
+ QLOG_ERROR() << "Could not create folder for " + m_target_path;
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
+ return;
+ }
+ if (!m_output_file->open(QIODevice::WriteOnly))
+ {
+ QLOG_ERROR() << "Could not open " + m_target_path + " for writing";
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
+ return;
+ }
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+
+ // check file consistency first.
+ QFile current(m_target_path);
+ if(current.exists() && current.size() != 0)
+ {
+ if (m_entry->remote_changed_timestamp.size())
+ request.setRawHeader(QString("If-Modified-Since").toLatin1(),
+ m_entry->remote_changed_timestamp.toLatin1());
+ if (m_entry->etag.size())
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1());
+ }
+
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
+}
+
+void CacheDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ QLOG_ERROR() << "Failed " << m_url.toString() << " with reason " << error;
+ m_status = Job_Failed;
+}
+void CacheDownload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status == Job_Failed)
+ {
+ m_output_file->cancelWriting();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+
+ // if we wrote any data to the save file, we try to commit the data to the real file.
+ if (wroteAnyData)
+ {
+ // nothing went wrong...
+ if (m_output_file->commit())
+ {
+ m_status = Job_Finished;
+ m_entry->md5sum = md5sum.result().toHex().constData();
+ }
+ else
+ {
+ QLOG_ERROR() << "Failed to commit changes to " << m_target_path;
+ m_output_file->cancelWriting();
+ m_reply.reset();
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
+ return;
+ }
+ }
+ else
+ {
+ m_status = Job_Finished;
+ }
+
+ // then get rid of the save file
+ m_output_file.reset();
+
+ QFileInfo output_file_info(m_target_path);
+
+ m_entry->etag = m_reply->rawHeader("ETag").constData();
+ if (m_reply->hasRawHeader("Last-Modified"))
+ {
+ m_entry->remote_changed_timestamp = m_reply->rawHeader("Last-Modified").constData();
+ }
+ m_entry->local_changed_timestamp =
+ output_file_info.lastModified().toUTC().toMSecsSinceEpoch();
+ m_entry->stale = false;
+ MMC->metacache()->updateEntry(m_entry);
+
+ m_reply.reset();
+ emit succeeded(m_index_within_job);
+ return;
+}
+
+void CacheDownload::downloadReadyRead()
+{
+ QByteArray ba = m_reply->readAll();
+ md5sum.addData(ba);
+ if (m_output_file->write(ba) != ba.size())
+ {
+ QLOG_ERROR() << "Failed writing into " + m_target_path;
+ m_status = Job_Failed;
+ m_reply->abort();
+ emit failed(m_index_within_job);
+ }
+ wroteAnyData = true;
+}
diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h
new file mode 100644
index 00000000..154f5988
--- /dev/null
+++ b/logic/net/CacheDownload.h
@@ -0,0 +1,58 @@
+/* Copyright 2013 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 "NetAction.h"
+#include "HttpMetaCache.h"
+#include <QCryptographicHash>
+#include <QSaveFile>
+
+typedef std::shared_ptr<class CacheDownload> CacheDownloadPtr;
+class CacheDownload : public NetAction
+{
+ Q_OBJECT
+private:
+ MetaEntryPtr m_entry;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ std::shared_ptr<QSaveFile> m_output_file;
+ /// the hash-as-you-download
+ QCryptographicHash md5sum;
+
+ bool wroteAnyData = false;
+
+public:
+ explicit CacheDownload(QUrl url, MetaEntryPtr entry);
+ static CacheDownloadPtr make(QUrl url, MetaEntryPtr entry)
+ {
+ return CacheDownloadPtr(new CacheDownload(url, entry));
+ }
+ QString getTargetFilepath()
+ {
+ return m_target_path;
+ }
+protected
+slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+public
+slots:
+ virtual void start();
+};
diff --git a/logic/net/ForgeMirror.h b/logic/net/ForgeMirror.h
new file mode 100644
index 00000000..2518dffe
--- /dev/null
+++ b/logic/net/ForgeMirror.h
@@ -0,0 +1,10 @@
+#pragma once
+#include <QString>
+
+struct ForgeMirror
+{
+ QString name;
+ QString logo_url;
+ QString website_url;
+ QString mirror_url;
+}; \ No newline at end of file
diff --git a/logic/net/ForgeMirrors.cpp b/logic/net/ForgeMirrors.cpp
new file mode 100644
index 00000000..b224306f
--- /dev/null
+++ b/logic/net/ForgeMirrors.cpp
@@ -0,0 +1,118 @@
+#include "MultiMC.h"
+#include "ForgeMirrors.h"
+#include "logger/QsLog.h"
+#include <algorithm>
+#include <random>
+
+ForgeMirrors::ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job,
+ QString mirrorlist)
+{
+ m_libs = libs;
+ m_parent_job = parent_job;
+ m_url = QUrl(mirrorlist);
+ m_status = Job_NotStarted;
+}
+
+void ForgeMirrors::start()
+{
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void ForgeMirrors::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit()
+ << "Network error: " << error;
+ m_status = Job_Failed;
+}
+
+void ForgeMirrors::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+ // nothing went wrong... ?
+ parseMirrorList();
+ return;
+ }
+ // else the download failed, we use a fixed list
+ else
+ {
+ m_status = Job_Finished;
+ m_reply.reset();
+ deferToFixedList();
+ return;
+ }
+}
+
+void ForgeMirrors::deferToFixedList()
+{
+ m_mirrors.clear();
+ m_mirrors.append(
+ {"Minecraft Forge", "http://files.minecraftforge.net/forge_logo.png",
+ "http://files.minecraftforge.net/", "http://files.minecraftforge.net/maven/"});
+ m_mirrors.append({"Creeper Host",
+ "http://files.minecraftforge.net/forge_logo.png",
+ "https://www.creeperhost.net/link.php?id=1",
+ "http://new.creeperrepo.net/forge/maven/"});
+ injectDownloads();
+ emit succeeded(m_index_within_job);
+}
+
+void ForgeMirrors::parseMirrorList()
+{
+ m_status = Job_Finished;
+ auto data = m_reply->readAll();
+ m_reply.reset();
+ auto dataLines = data.split('\n');
+ for(auto line: dataLines)
+ {
+ auto elements = line.split('!');
+ if (elements.size() == 4)
+ {
+ m_mirrors.append({elements[0],elements[1],elements[2],elements[3]});
+ }
+ }
+ if(!m_mirrors.size())
+ deferToFixedList();
+ injectDownloads();
+ emit succeeded(m_index_within_job);
+}
+
+void ForgeMirrors::injectDownloads()
+{
+ // shuffle the mirrors randomly
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ std::shuffle(m_mirrors.begin(), m_mirrors.end(), rng);
+
+ // tell parent to download the libs
+ for(auto lib: m_libs)
+ {
+ lib->setMirrors(m_mirrors);
+ m_parent_job->addNetAction(lib);
+ }
+}
+
+void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
+}
+
+void ForgeMirrors::downloadReadyRead()
+{
+}
diff --git a/logic/net/ForgeMirrors.h b/logic/net/ForgeMirrors.h
new file mode 100644
index 00000000..990e49d6
--- /dev/null
+++ b/logic/net/ForgeMirrors.h
@@ -0,0 +1,58 @@
+/* Copyright 2013 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 "NetAction.h"
+#include "HttpMetaCache.h"
+#include "ForgeXzDownload.h"
+#include "NetJob.h"
+#include <QFile>
+#include <QTemporaryFile>
+typedef std::shared_ptr<class ForgeMirrors> ForgeMirrorsPtr;
+
+class ForgeMirrors : public NetAction
+{
+ Q_OBJECT
+public:
+ QList<ForgeXzDownloadPtr> m_libs;
+ NetJobPtr m_parent_job;
+ QList<ForgeMirror> m_mirrors;
+
+public:
+ explicit ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job,
+ QString mirrorlist);
+ static ForgeMirrorsPtr make(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job,
+ QString mirrorlist)
+ {
+ return ForgeMirrorsPtr(new ForgeMirrors(libs, parent_job, mirrorlist));
+ }
+
+protected
+slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+private:
+ void parseMirrorList();
+ void deferToFixedList();
+ void injectDownloads();
+
+public
+slots:
+ virtual void start();
+};
diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp
new file mode 100644
index 00000000..359ad858
--- /dev/null
+++ b/logic/net/ForgeXzDownload.cpp
@@ -0,0 +1,389 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "ForgeXzDownload.h"
+#include <pathutils.h>
+
+#include <QCryptographicHash>
+#include <QFileInfo>
+#include <QDateTime>
+#include <QDir>
+#include "logger/QsLog.h"
+
+ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction()
+{
+ m_entry = entry;
+ m_target_path = entry->getFullPath();
+ m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX");
+ m_status = Job_NotStarted;
+ m_url_path = relative_path;
+}
+
+void ForgeXzDownload::setMirrors(QList<ForgeMirror> &mirrors)
+{
+ m_mirror_index = 0;
+ m_mirrors = mirrors;
+ updateUrl();
+}
+
+void ForgeXzDownload::start()
+{
+ m_status = Job_InProgress;
+ if (!m_entry->stale)
+ {
+ m_status = Job_Finished;
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ // can we actually create the real, final file?
+ if (!ensureFilePathExists(m_target_path))
+ {
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
+ return;
+ }
+ if (m_mirrors.empty())
+ {
+ m_status = Job_Failed;
+ emit failed(m_index_within_job);
+ return;
+ }
+
+ QLOG_INFO() << "Downloading " << m_url.toString();
+ QNetworkRequest request(m_url);
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1());
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
+
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
+}
+
+void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ // TODO: log the reason why
+ m_status = Job_Failed;
+}
+
+void ForgeXzDownload::failAndTryNextMirror()
+{
+ m_status = Job_Failed;
+ int next = m_mirror_index + 1;
+ if(m_mirrors.size() == next)
+ m_mirror_index = 0;
+ else
+ m_mirror_index = next;
+
+ updateUrl();
+ emit failed(m_index_within_job);
+}
+
+void ForgeXzDownload::updateUrl()
+{
+ QLOG_INFO() << "Updating URL for " << m_url_path;
+ for (auto possible : m_mirrors)
+ {
+ QLOG_INFO() << "Possible: " << possible.name << " : " << possible.mirror_url;
+ }
+ QString aggregate = m_mirrors[m_mirror_index].mirror_url + m_url_path + ".pack.xz";
+ m_url = QUrl(aggregate);
+}
+
+void ForgeXzDownload::downloadFinished()
+{
+ //TEST: defer to other possible mirrors (autofail the first one)
+ /*
+ QLOG_INFO() <<"dl " << index_within_job << " mirror " << m_mirror_index;
+ if( m_mirror_index == 0)
+ {
+ QLOG_INFO() <<"dl " << index_within_job << " AUTOFAIL";
+ m_status = Job_Failed;
+ m_pack200_xz_file.close();
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ failAndTryNextMirror();
+ return;
+ }
+ */
+
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ if (m_pack200_xz_file.isOpen())
+ {
+ // we actually downloaded something! process and isntall it
+ decompressAndInstall();
+ return;
+ }
+ else
+ {
+ // something bad happened -- on the local machine!
+ m_status = Job_Failed;
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+ }
+ // else the download failed
+ else
+ {
+ m_status = Job_Failed;
+ m_pack200_xz_file.close();
+ m_pack200_xz_file.remove();
+ m_reply.reset();
+ failAndTryNextMirror();
+ return;
+ }
+}
+
+void ForgeXzDownload::downloadReadyRead()
+{
+
+ if (!m_pack200_xz_file.isOpen())
+ {
+ if (!m_pack200_xz_file.open())
+ {
+ /*
+ * Can't open the file... the job failed
+ */
+ m_reply->abort();
+ emit failed(m_index_within_job);
+ return;
+ }
+ }
+ m_pack200_xz_file.write(m_reply->readAll());
+}
+
+#include "xz.h"
+#include "unpack200.h"
+#include <stdexcept>
+
+const size_t buffer_size = 8196;
+
+void ForgeXzDownload::decompressAndInstall()
+{
+ // rewind the downloaded temp file
+ m_pack200_xz_file.seek(0);
+ // de-xz'd file
+ QTemporaryFile pack200_file("./dl_temp.XXXXXX");
+ pack200_file.open();
+
+ bool xz_success = false;
+ // first, de-xz
+ {
+ uint8_t in[buffer_size];
+ uint8_t out[buffer_size];
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+ xz_crc32_init();
+ xz_crc64_init();
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == nullptr)
+ {
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+ }
+ b.in = in;
+ b.in_pos = 0;
+ b.in_size = 0;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = buffer_size;
+ while (!xz_success)
+ {
+ if (b.in_pos == b.in_size)
+ {
+ b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in));
+ b.in_pos = 0;
+ }
+
+ ret = xz_dec_run(s, &b);
+
+ if (b.out_pos == sizeof(out))
+ {
+ if (pack200_file.write((char *)out, b.out_pos) != b.out_pos)
+ {
+ // msg = "Write error\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+ }
+
+ b.out_pos = 0;
+ }
+
+ if (ret == XZ_OK)
+ continue;
+
+ if (ret == XZ_UNSUPPORTED_CHECK)
+ {
+ // unsupported check. this is OK, but we should log this
+ continue;
+ }
+
+ if (pack200_file.write((char *)out, b.out_pos) != b.out_pos)
+ {
+ // write error
+ pack200_file.close();
+ xz_dec_end(s);
+ return;
+ }
+
+ switch (ret)
+ {
+ case XZ_STREAM_END:
+ xz_dec_end(s);
+ xz_success = true;
+ break;
+
+ case XZ_MEM_ERROR:
+ QLOG_ERROR() << "Memory allocation failed\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_MEMLIMIT_ERROR:
+ QLOG_ERROR() << "Memory usage limit reached\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_FORMAT_ERROR:
+ QLOG_ERROR() << "Not a .xz file\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_OPTIONS_ERROR:
+ QLOG_ERROR() << "Unsupported options in the .xz headers\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ case XZ_DATA_ERROR:
+ case XZ_BUF_ERROR:
+ QLOG_ERROR() << "File is corrupt\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+
+ default:
+ QLOG_ERROR() << "Bug!\n";
+ xz_dec_end(s);
+ failAndTryNextMirror();
+ return;
+ }
+ }
+ }
+ m_pack200_xz_file.remove();
+
+ // revert pack200
+ pack200_file.seek(0);
+ int handle_in = pack200_file.handle();
+ // FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects.
+ if(handle_in == -1)
+ {
+ QLOG_ERROR() << "Error reopening " << pack200_file.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ FILE * file_in = fdopen(handle_in,"r");
+ if(!file_in)
+ {
+ QLOG_ERROR() << "Error reopening " << pack200_file.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ QFile qfile_out(m_target_path);
+ if(!qfile_out.open(QIODevice::WriteOnly))
+ {
+ QLOG_ERROR() << "Error opening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ int handle_out = qfile_out.handle();
+ if(handle_out == -1)
+ {
+ QLOG_ERROR() << "Error opening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ FILE * file_out = fdopen(handle_out,"w");
+ if(!file_out)
+ {
+ QLOG_ERROR() << "Error opening " << qfile_out.fileName();
+ failAndTryNextMirror();
+ return;
+ }
+ try
+ {
+ unpack_200(file_in, file_out);
+ }
+ catch (std::runtime_error &err)
+ {
+ m_status = Job_Failed;
+ QLOG_ERROR() << "Error unpacking " << pack200_file.fileName() << " : " << err.what();
+ QFile f(m_target_path);
+ if (f.exists())
+ f.remove();
+ failAndTryNextMirror();
+ return;
+ }
+ pack200_file.remove();
+
+ QFile jar_file(m_target_path);
+
+ if (!jar_file.open(QIODevice::ReadOnly))
+ {
+ jar_file.remove();
+ failAndTryNextMirror();
+ return;
+ }
+ m_entry->md5sum = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5)
+ .toHex()
+ .constData();
+ jar_file.close();
+
+ QFileInfo output_file_info(m_target_path);
+ m_entry->etag = m_reply->rawHeader("ETag").constData();
+ m_entry->local_changed_timestamp =
+ output_file_info.lastModified().toUTC().toMSecsSinceEpoch();
+ m_entry->stale = false;
+ MMC->metacache()->updateEntry(m_entry);
+
+ m_reply.reset();
+ emit succeeded(m_index_within_job);
+}
diff --git a/logic/net/ForgeXzDownload.h b/logic/net/ForgeXzDownload.h
new file mode 100644
index 00000000..990f91f0
--- /dev/null
+++ b/logic/net/ForgeXzDownload.h
@@ -0,0 +1,65 @@
+/* Copyright 2013 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 "NetAction.h"
+#include "HttpMetaCache.h"
+#include <QFile>
+#include <QTemporaryFile>
+#include "ForgeMirror.h"
+
+typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr;
+
+class ForgeXzDownload : public NetAction
+{
+ Q_OBJECT
+public:
+ MetaEntryPtr m_entry;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ QTemporaryFile m_pack200_xz_file;
+ /// mirror index (NOT OPTICS, I SWEAR)
+ int m_mirror_index = 0;
+ /// list of mirrors to use. Mirror has the url base
+ QList<ForgeMirror> m_mirrors;
+ /// path relative to the mirror base
+ QString m_url_path;
+
+public:
+ explicit ForgeXzDownload(QString relative_path, MetaEntryPtr entry);
+ static ForgeXzDownloadPtr make(QString relative_path, MetaEntryPtr entry)
+ {
+ return ForgeXzDownloadPtr(new ForgeXzDownload(relative_path, entry));
+ }
+ void setMirrors(QList<ForgeMirror> & mirrors);
+
+protected
+slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+public
+slots:
+ virtual void start();
+
+private:
+ void decompressAndInstall();
+ void failAndTryNextMirror();
+ void updateUrl();
+};
diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp
new file mode 100644
index 00000000..29007951
--- /dev/null
+++ b/logic/net/HttpMetaCache.cpp
@@ -0,0 +1,253 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "HttpMetaCache.h"
+#include <pathutils.h>
+
+#include <QFileInfo>
+#include <QFile>
+#include <QTemporaryFile>
+#include <QSaveFile>
+#include <QDateTime>
+#include <QCryptographicHash>
+
+#include "logger/QsLog.h"
+
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QJsonObject>
+
+QString MetaEntry::getFullPath()
+{
+ return PathCombine(MMC->metacache()->getBasePath(base), path);
+}
+
+HttpMetaCache::HttpMetaCache(QString path) : QObject()
+{
+ m_index_file = path;
+ saveBatchingTimer.setSingleShot(true);
+ saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer);
+ connect(&saveBatchingTimer, SIGNAL(timeout()), SLOT(SaveNow()));
+}
+
+HttpMetaCache::~HttpMetaCache()
+{
+ saveBatchingTimer.stop();
+ SaveNow();
+}
+
+MetaEntryPtr HttpMetaCache::getEntry(QString base, QString resource_path)
+{
+ // no base. no base path. can't store
+ if (!m_entries.contains(base))
+ {
+ // TODO: log problem
+ return MetaEntryPtr();
+ }
+ EntryMap &map = m_entries[base];
+ if (map.entry_list.contains(resource_path))
+ {
+ return map.entry_list[resource_path];
+ }
+ return MetaEntryPtr();
+}
+
+MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path,
+ QString expected_etag)
+{
+ auto entry = getEntry(base, resource_path);
+ // it's not present? generate a default stale entry
+ if (!entry)
+ {
+ return staleEntry(base, resource_path);
+ }
+
+ auto &selected_base = m_entries[base];
+ QString real_path = PathCombine(selected_base.base_path, resource_path);
+ QFileInfo finfo(real_path);
+
+ // is the file really there? if not -> stale
+ if (!finfo.isFile() || !finfo.isReadable())
+ {
+ // if the file doesn't exist, we disown the entry
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
+
+ if (!expected_etag.isEmpty() && expected_etag != entry->etag)
+ {
+ // if the etag doesn't match expected, we disown the entry
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
+
+ // if the file changed, check md5sum
+ qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch();
+ if (file_last_changed != entry->local_changed_timestamp)
+ {
+ QFile input(real_path);
+ input.open(QIODevice::ReadOnly);
+ QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5)
+ .toHex()
+ .constData();
+ if (entry->md5sum != md5sum)
+ {
+ selected_base.entry_list.remove(resource_path);
+ return staleEntry(base, resource_path);
+ }
+ // md5sums matched... keep entry and save the new state to file
+ entry->local_changed_timestamp = file_last_changed;
+ SaveEventually();
+ }
+
+ // entry passed all the checks we cared about.
+ return entry;
+}
+
+bool HttpMetaCache::updateEntry(MetaEntryPtr stale_entry)
+{
+ if (!m_entries.contains(stale_entry->base))
+ {
+ QLOG_ERROR() << "Cannot add entry with unknown base: "
+ << stale_entry->base.toLocal8Bit();
+ return false;
+ }
+ if (stale_entry->stale)
+ {
+ QLOG_ERROR() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit();
+ return false;
+ }
+ m_entries[stale_entry->base].entry_list[stale_entry->path] = stale_entry;
+ SaveEventually();
+ return true;
+}
+
+MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path)
+{
+ auto foo = new MetaEntry;
+ foo->base = base;
+ foo->path = resource_path;
+ foo->stale = true;
+ return MetaEntryPtr(foo);
+}
+
+void HttpMetaCache::addBase(QString base, QString base_root)
+{
+ // TODO: report error
+ if (m_entries.contains(base))
+ return;
+ // TODO: check if the base path is valid
+ EntryMap foo;
+ foo.base_path = base_root;
+ m_entries[base] = foo;
+}
+
+QString HttpMetaCache::getBasePath(QString base)
+{
+ if (m_entries.contains(base))
+ {
+ return m_entries[base].base_path;
+ }
+ return QString();
+}
+
+void HttpMetaCache::Load()
+{
+ QFile index(m_index_file);
+ if (!index.open(QIODevice::ReadOnly))
+ return;
+
+ QJsonDocument json = QJsonDocument::fromJson(index.readAll());
+ if (!json.isObject())
+ return;
+ auto root = json.object();
+ // check file version first
+ auto version_val = root.value("version");
+ if (!version_val.isString())
+ return;
+ if (version_val.toString() != "1")
+ return;
+
+ // read the entry array
+ auto entries_val = root.value("entries");
+ if (!entries_val.isArray())
+ return;
+ QJsonArray array = entries_val.toArray();
+ for (auto element : array)
+ {
+ if (!element.isObject())
+ return;
+ auto element_obj = element.toObject();
+ QString base = element_obj.value("base").toString();
+ if (!m_entries.contains(base))
+ continue;
+ auto &entrymap = m_entries[base];
+ auto foo = new MetaEntry;
+ foo->base = base;
+ QString path = foo->path = element_obj.value("path").toString();
+ foo->md5sum = element_obj.value("md5sum").toString();
+ foo->etag = element_obj.value("etag").toString();
+ foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble();
+ foo->remote_changed_timestamp =
+ element_obj.value("remote_changed_timestamp").toString();
+ // presumed innocent until closer examination
+ foo->stale = false;
+ entrymap.entry_list[path] = MetaEntryPtr(foo);
+ }
+}
+
+void HttpMetaCache::SaveEventually()
+{
+ // reset the save timer
+ saveBatchingTimer.stop();
+ saveBatchingTimer.start(30000);
+}
+
+void HttpMetaCache::SaveNow()
+{
+ QSaveFile tfile(m_index_file);
+ if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ return;
+ QJsonObject toplevel;
+ toplevel.insert("version", QJsonValue(QString("1")));
+ QJsonArray entriesArr;
+ for (auto group : m_entries)
+ {
+ for (auto entry : group.entry_list)
+ {
+ QJsonObject entryObj;
+ entryObj.insert("base", QJsonValue(entry->base));
+ entryObj.insert("path", QJsonValue(entry->path));
+ entryObj.insert("md5sum", QJsonValue(entry->md5sum));
+ entryObj.insert("etag", QJsonValue(entry->etag));
+ entryObj.insert("last_changed_timestamp",
+ QJsonValue(double(entry->local_changed_timestamp)));
+ if (!entry->remote_changed_timestamp.isEmpty())
+ entryObj.insert("remote_changed_timestamp",
+ QJsonValue(entry->remote_changed_timestamp));
+ entriesArr.append(entryObj);
+ }
+ }
+ toplevel.insert("entries", entriesArr);
+ QJsonDocument doc(toplevel);
+ QByteArray jsonData = doc.toJson();
+ qint64 result = tfile.write(jsonData);
+ if (result == -1)
+ return;
+ if (result != jsonData.size())
+ return;
+ tfile.commit();
+}
diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h
new file mode 100644
index 00000000..08b39fe2
--- /dev/null
+++ b/logic/net/HttpMetaCache.h
@@ -0,0 +1,75 @@
+/* Copyright 2013 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 <QString>
+#include <QMap>
+#include <qtimer.h>
+
+struct MetaEntry
+{
+ QString base;
+ QString path;
+ QString md5sum;
+ QString etag;
+ qint64 local_changed_timestamp = 0;
+ QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time
+ bool stale = true;
+ QString getFullPath();
+};
+
+typedef std::shared_ptr<MetaEntry> MetaEntryPtr;
+
+class HttpMetaCache : public QObject
+{
+ Q_OBJECT
+public:
+ // supply path to the cache index file
+ HttpMetaCache(QString path);
+ ~HttpMetaCache();
+
+ // get the entry solely from the cache
+ // you probably don't want this, unless you have some specific caching needs.
+ MetaEntryPtr getEntry(QString base, QString resource_path);
+
+ // get the entry from cache and verify that it isn't stale (within reason)
+ MetaEntryPtr resolveEntry(QString base, QString resource_path,
+ QString expected_etag = QString());
+
+ // add a previously resolved stale entry
+ bool updateEntry(MetaEntryPtr stale_entry);
+
+ void addBase(QString base, QString base_root);
+
+ // (re)start a timer that calls SaveNow later.
+ void SaveEventually();
+ void Load();
+ QString getBasePath(QString base);
+public
+slots:
+ void SaveNow();
+
+private:
+ // create a new stale entry, given the parameters
+ MetaEntryPtr staleEntry(QString base, QString resource_path);
+ struct EntryMap
+ {
+ QString base_path;
+ QMap<QString, MetaEntryPtr> entry_list;
+ };
+ QMap<QString, EntryMap> m_entries;
+ QString m_index_file;
+ QTimer saveBatchingTimer;
+}; \ No newline at end of file
diff --git a/logic/net/MD5EtagDownload.cpp b/logic/net/MD5EtagDownload.cpp
new file mode 100644
index 00000000..63583e8d
--- /dev/null
+++ b/logic/net/MD5EtagDownload.cpp
@@ -0,0 +1,156 @@
+/* Copyright 2013 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 "MultiMC.h"
+#include "MD5EtagDownload.h"
+#include <pathutils.h>
+#include <QCryptographicHash>
+#include "logger/QsLog.h"
+
+MD5EtagDownload::MD5EtagDownload(QUrl url, QString target_path) : NetAction()
+{
+ m_url = url;
+ m_target_path = target_path;
+ m_status = Job_NotStarted;
+}
+
+void MD5EtagDownload::start()
+{
+ QString filename = m_target_path;
+ m_output_file.setFileName(filename);
+ // if there already is a file and md5 checking is in effect and it can be opened
+ if (m_output_file.exists() && m_output_file.open(QIODevice::ReadOnly))
+ {
+ // get the md5 of the local file.
+ m_local_md5 =
+ QCryptographicHash::hash(m_output_file.readAll(), QCryptographicHash::Md5)
+ .toHex()
+ .constData();
+ m_output_file.close();
+ // if we are expecting some md5sum, compare it with the local one
+ if (!m_expected_md5.isEmpty())
+ {
+ // skip if they match
+ if(m_local_md5 == m_expected_md5)
+ {
+ QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match.";
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ }
+ else
+ {
+ // no expected md5. we use the local md5sum as an ETag
+ }
+ }
+ if (!ensureFilePathExists(filename))
+ {
+ emit failed(m_index_within_job);
+ return;
+ }
+
+ QNetworkRequest request(m_url);
+
+ QLOG_INFO() << "Downloading " << m_url.toString() << " got " << m_local_md5;
+
+ if(!m_local_md5.isEmpty())
+ {
+ QLOG_INFO() << "Got " << m_local_md5;
+ request.setRawHeader(QString("If-None-Match").toLatin1(), m_local_md5.toLatin1());
+ }
+ if(!m_expected_md5.isEmpty())
+ QLOG_INFO() << "Expecting " << m_expected_md5;
+
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+
+ // Go ahead and try to open the file.
+ // If we don't do this, empty files won't be created, which breaks the updater.
+ // Plus, this way, we don't end up starting a download for a file we can't open.
+ if (!m_output_file.open(QIODevice::WriteOnly))
+ {
+ emit failed(m_index_within_job);
+ return;
+ }
+
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->get(request);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, SIGNAL(downloadProgress(qint64, qint64)),
+ SLOT(downloadProgress(qint64, qint64)));
+ connect(rep, SIGNAL(finished()), SLOT(downloadFinished()));
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
+}
+
+void MD5EtagDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_total_progress = bytesTotal;
+ m_progress = bytesReceived;
+ emit progress(m_index_within_job, bytesReceived, bytesTotal);
+}
+
+void MD5EtagDownload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ // TODO: log the reason why
+ m_status = Job_Failed;
+}
+
+void MD5EtagDownload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_status != Job_Failed)
+ {
+ // nothing went wrong...
+ m_status = Job_Finished;
+ m_output_file.close();
+
+ // FIXME: compare with the real written data md5sum
+ // this is just an ETag
+ QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData();
+
+ m_reply.reset();
+ emit succeeded(m_index_within_job);
+ return;
+ }
+ // else the download failed
+ else
+ {
+ m_output_file.close();
+ m_output_file.remove();
+ m_reply.reset();
+ emit failed(m_index_within_job);
+ return;
+ }
+}
+
+void MD5EtagDownload::downloadReadyRead()
+{
+ if (!m_output_file.isOpen())
+ {
+ if (!m_output_file.open(QIODevice::WriteOnly))
+ {
+ /*
+ * Can't open the file... the job failed
+ */
+ m_reply->abort();
+ emit failed(m_index_within_job);
+ return;
+ }
+ }
+ m_output_file.write(m_reply->readAll());
+}
diff --git a/logic/net/MD5EtagDownload.h b/logic/net/MD5EtagDownload.h
new file mode 100644
index 00000000..d5aed0ca
--- /dev/null
+++ b/logic/net/MD5EtagDownload.h
@@ -0,0 +1,51 @@
+/* Copyright 2013 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 "NetAction.h"
+#include <QFile>
+
+typedef std::shared_ptr<class MD5EtagDownload> Md5EtagDownloadPtr;
+class MD5EtagDownload : public NetAction
+{
+ Q_OBJECT
+public:
+ /// the expected md5 checksum. Only set from outside
+ QString m_expected_md5;
+ /// the md5 checksum of a file that already exists.
+ QString m_local_md5;
+ /// if saving to file, use the one specified in this string
+ QString m_target_path;
+ /// this is the output file, if any
+ QFile m_output_file;
+
+public:
+ explicit MD5EtagDownload(QUrl url, QString target_path);
+ static Md5EtagDownloadPtr make(QUrl url, QString target_path)
+ {
+ return Md5EtagDownloadPtr(new MD5EtagDownload(url, target_path));
+ }
+protected
+slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ virtual void downloadError(QNetworkReply::NetworkError error);
+ virtual void downloadFinished();
+ virtual void downloadReadyRead();
+
+public
+slots:
+ virtual void start();
+};
diff --git a/logic/net/NetAction.h b/logic/net/NetAction.h
new file mode 100644
index 00000000..97c96e5d
--- /dev/null
+++ b/logic/net/NetAction.h
@@ -0,0 +1,89 @@
+/* Copyright 2013 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 <QObject>
+#include <QUrl>
+#include <memory>
+#include <QNetworkReply>
+
+enum JobStatus
+{
+ Job_NotStarted,
+ Job_InProgress,
+ Job_Finished,
+ Job_Failed
+};
+
+typedef std::shared_ptr<class NetAction> NetActionPtr;
+class NetAction : public QObject
+{
+ Q_OBJECT
+protected:
+ explicit NetAction() : QObject(0) {};
+
+public:
+ virtual ~NetAction() {};
+
+public:
+ virtual qint64 totalProgress() const
+ {
+ return m_total_progress;
+ }
+ virtual qint64 currentProgress() const
+ {
+ return m_progress;
+ }
+ virtual qint64 numberOfFailures() const
+ {
+ return m_failures;
+ }
+public:
+ /// the network reply
+ std::shared_ptr<QNetworkReply> m_reply;
+
+ /// source URL
+ QUrl m_url;
+
+ /// The file's status
+ JobStatus m_status = Job_NotStarted;
+
+ /// index within the parent job
+ int m_index_within_job = 0;
+
+ qint64 m_progress = 0;
+ qint64 m_total_progress = 1;
+
+ /// number of failures up to this point
+ int m_failures = 0;
+
+signals:
+ void started(int index);
+ void progress(int index, qint64 current, qint64 total);
+ void succeeded(int index);
+ void failed(int index);
+
+protected
+slots:
+ virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0;
+ virtual void downloadError(QNetworkReply::NetworkError error) = 0;
+ virtual void downloadFinished() = 0;
+ virtual void downloadReadyRead() = 0;
+
+public
+slots:
+ virtual void start() = 0;
+};
diff --git a/logic/net/NetJob.cpp b/logic/net/NetJob.cpp
new file mode 100644
index 00000000..9e800d13
--- /dev/null
+++ b/logic/net/NetJob.cpp
@@ -0,0 +1,112 @@
+/* Copyright 2013 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 "NetJob.h"
+#include "pathutils.h"
+#include "MultiMC.h"
+#include "MD5EtagDownload.h"
+#include "ByteArrayDownload.h"
+#include "CacheDownload.h"
+
+#include "logger/QsLog.h"
+
+void NetJob::partSucceeded(int index)
+{
+ // do progress. all slots are 1 in size at least
+ auto &slot = parts_progress[index];
+ partProgress(index, slot.total_progress, slot.total_progress);
+
+ num_succeeded++;
+ QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_succeeded << "/"
+ << downloads.size();
+
+ if (num_failed + num_succeeded == downloads.size())
+ {
+ if (num_failed)
+ {
+ QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
+ emit failed();
+ }
+ else
+ {
+ QLOG_INFO() << m_job_name.toLocal8Bit() << "succeeded.";
+ emit succeeded();
+ }
+ }
+}
+
+void NetJob::partFailed(int index)
+{
+ auto &slot = parts_progress[index];
+ if (slot.failures == 3)
+ {
+ QLOG_ERROR() << "Part" << index << "failed 3 times (" << downloads[index]->m_url << ")";
+ num_failed++;
+ if (num_failed + num_succeeded == downloads.size())
+ {
+ QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
+ emit failed();
+ }
+ }
+ else
+ {
+ QLOG_ERROR() << "Part" << index << "failed, restarting (" << downloads[index]->m_url
+ << ")";
+ // restart the job
+ slot.failures++;
+ downloads[index]->start();
+ }
+}
+
+void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal)
+{
+ auto &slot = parts_progress[index];
+
+ current_progress -= slot.current_progress;
+ slot.current_progress = bytesReceived;
+ current_progress += slot.current_progress;
+
+ total_progress -= slot.total_progress;
+ slot.total_progress = bytesTotal;
+ total_progress += slot.total_progress;
+ emit progress(current_progress, total_progress);
+}
+
+void NetJob::start()
+{
+ QLOG_INFO() << m_job_name.toLocal8Bit() << " started.";
+ m_running = true;
+ for (auto iter : downloads)
+ {
+ connect(iter.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(iter.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+ connect(iter.get(), SIGNAL(progress(int, qint64, qint64)),
+ SLOT(partProgress(int, qint64, qint64)));
+ iter->start();
+ }
+}
+
+QStringList NetJob::getFailedFiles()
+{
+ QStringList failed;
+ for (auto download : downloads)
+ {
+ if (download->m_status == Job_Failed)
+ {
+ failed.push_back(download->m_url.toString());
+ }
+ }
+ return failed;
+}
diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h
new file mode 100644
index 00000000..03d6a36e
--- /dev/null
+++ b/logic/net/NetJob.h
@@ -0,0 +1,124 @@
+/* Copyright 2013 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 <QtNetwork>
+#include <QLabel>
+#include "NetAction.h"
+#include "ByteArrayDownload.h"
+#include "MD5EtagDownload.h"
+#include "CacheDownload.h"
+#include "HttpMetaCache.h"
+#include "ForgeXzDownload.h"
+#include "logic/tasks/ProgressProvider.h"
+
+class NetJob;
+typedef std::shared_ptr<NetJob> NetJobPtr;
+
+class NetJob : public ProgressProvider
+{
+ Q_OBJECT
+public:
+ explicit NetJob(QString job_name) : ProgressProvider(), m_job_name(job_name) {};
+
+ template <typename T> bool addNetAction(T action)
+ {
+ NetActionPtr base = std::static_pointer_cast<NetAction>(action);
+ base->m_index_within_job = downloads.size();
+ downloads.append(action);
+ part_info pi;
+ {
+ pi.current_progress = base->currentProgress();
+ pi.total_progress = base->totalProgress();
+ pi.failures = base->numberOfFailures();
+ }
+ parts_progress.append(pi);
+ total_progress += pi.total_progress;
+ // if this is already running, the action needs to be started right away!
+ if (isRunning())
+ {
+ emit progress(current_progress, total_progress);
+ connect(base.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int)));
+ connect(base.get(), SIGNAL(failed(int)), SLOT(partFailed(int)));
+ connect(base.get(), SIGNAL(progress(int, qint64, qint64)),
+ SLOT(partProgress(int, qint64, qint64)));
+ base->start();
+ }
+ return true;
+ }
+
+ NetActionPtr operator[](int index)
+ {
+ return downloads[index];
+ }
+ ;
+ NetActionPtr first()
+ {
+ if (downloads.size())
+ return downloads[0];
+ return NetActionPtr();
+ }
+ int size() const
+ {
+ return downloads.size();
+ }
+ virtual void getProgress(qint64 &current, qint64 &total)
+ {
+ current = current_progress;
+ total = total_progress;
+ }
+ ;
+ virtual QString getStatus() const
+ {
+ return m_job_name;
+ }
+ virtual bool isRunning() const
+ {
+ return m_running;
+ }
+ ;
+ QStringList getFailedFiles();
+signals:
+ void started();
+ void progress(qint64 current, qint64 total);
+ void succeeded();
+ void failed();
+public
+slots:
+ virtual void start();
+ // FIXME: implement
+ virtual void abort() {};
+private
+slots:
+ void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal);
+ void partSucceeded(int index);
+ void partFailed(int index);
+
+private:
+ struct part_info
+ {
+ qint64 current_progress = 0;
+ qint64 total_progress = 1;
+ int failures = 0;
+ };
+ QString m_job_name;
+ QList<NetActionPtr> downloads;
+ QList<part_info> parts_progress;
+ qint64 current_progress = 0;
+ qint64 total_progress = 0;
+ int num_succeeded = 0;
+ int num_failed = 0;
+ bool m_running = false;
+};
diff --git a/logic/net/PasteUpload.cpp b/logic/net/PasteUpload.cpp
new file mode 100644
index 00000000..fa54d084
--- /dev/null
+++ b/logic/net/PasteUpload.cpp
@@ -0,0 +1,86 @@
+#include "PasteUpload.h"
+#include "MultiMC.h"
+#include "logger/QsLog.h"
+#include <QJsonObject>
+#include <QJsonDocument>
+#include "gui/dialogs/CustomMessageBox.h"
+#include <QDesktopServices>
+
+PasteUpload::PasteUpload(QWidget *window, QString text) : m_text(text), m_window(window)
+{
+}
+
+void PasteUpload::executeTask()
+{
+ QNetworkRequest request(QUrl("http://paste.ee/api"));
+ request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)");
+ QByteArray content(
+ "key=public&description=MultiMC5+Log+File&language=plain&format=json&paste=" +
+ m_text.toUtf8());
+ request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
+ request.setRawHeader("Content-Length", QByteArray::number(content.size()));
+
+ auto worker = MMC->qnam();
+ QNetworkReply *rep = worker->post(request, content);
+
+ m_reply = std::shared_ptr<QNetworkReply>(rep);
+ connect(rep, &QNetworkReply::downloadProgress, [&](qint64 value, qint64 max)
+ { setProgress(value / max * 100); });
+ connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this,
+ SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
+}
+
+void PasteUpload::downloadError(QNetworkReply::NetworkError error)
+{
+ // error happened during download.
+ QLOG_ERROR() << "Network error: " << error;
+ emitFailed(m_reply->errorString());
+}
+
+void PasteUpload::downloadFinished()
+{
+ // if the download succeeded
+ if (m_reply->error() == QNetworkReply::NetworkError::NoError)
+ {
+ QByteArray data = m_reply->readAll();
+ m_reply.reset();
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed(jsonError.errorString());
+ return;
+ }
+ QString error;
+ if (!parseResult(doc, &error))
+ {
+ emitFailed(error);
+ return;
+ }
+ }
+ // else the download failed
+ else
+ {
+ emitFailed(QString("Network error: %1").arg(m_reply->errorString()));
+ m_reply.reset();
+ return;
+ }
+ emitSucceeded();
+}
+
+bool PasteUpload::parseResult(QJsonDocument doc, QString *parseError)
+{
+ auto object = doc.object();
+ auto status = object.value("status").toString("error");
+ if (status == "error")
+ {
+ parseError = new QString(object.value("error").toString());
+ return false;
+ }
+ // FIXME: not the place for GUI things.
+ QString pasteUrl = object.value("paste").toObject().value("link").toString();
+ QDesktopServices::openUrl(pasteUrl);
+ return true;
+}
+
diff --git a/logic/net/PasteUpload.h b/logic/net/PasteUpload.h
new file mode 100644
index 00000000..917a0016
--- /dev/null
+++ b/logic/net/PasteUpload.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "logic/tasks/Task.h"
+#include <QMessageBox>
+#include <QNetworkReply>
+#include <memory>
+
+class PasteUpload : public Task
+{
+ Q_OBJECT
+public:
+ PasteUpload(QWidget *window, QString text);
+
+protected:
+ virtual void executeTask();
+
+private:
+ bool parseResult(QJsonDocument doc, QString *parseError);
+ QString m_text;
+ QString m_error;
+ QWidget *m_window;
+ std::shared_ptr<QNetworkReply> m_reply;
+public
+slots:
+ void downloadError(QNetworkReply::NetworkError);
+ void downloadFinished();
+};
diff --git a/logic/net/URLConstants.h b/logic/net/URLConstants.h
new file mode 100644
index 00000000..8cb1f3fd
--- /dev/null
+++ b/logic/net/URLConstants.h
@@ -0,0 +1,36 @@
+/* Copyright 2013 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 <QString>
+
+namespace URLConstants
+{
+const QString AWS_DOWNLOAD_BASE("s3.amazonaws.com/Minecraft.Download/");
+const QString AWS_DOWNLOAD_VERSIONS(AWS_DOWNLOAD_BASE + "versions/");
+const QString AWS_DOWNLOAD_LIBRARIES(AWS_DOWNLOAD_BASE + "libraries/");
+const QString AWS_DOWNLOAD_INDEXES(AWS_DOWNLOAD_BASE + "indexes/");
+const QString ASSETS_BASE("assets.minecraft.net/");
+//const QString MCN_BASE("sonicrules.org/mcnweb.py");
+const QString RESOURCE_BASE("resources.download.minecraft.net/");
+const QString LIBRARY_BASE("libraries.minecraft.net/");
+const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/");
+const QString AUTH_BASE("authserver.mojang.com/");
+const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json");
+const QString FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json");
+const QString MOJANG_STATUS_URL("http://status.mojang.com/check");
+const QString MOJANG_STATUS_NEWS_URL("http://status.mojang.com/news");
+}
diff --git a/logic/news/NewsChecker.cpp b/logic/news/NewsChecker.cpp
new file mode 100644
index 00000000..8fc44fa9
--- /dev/null
+++ b/logic/news/NewsChecker.cpp
@@ -0,0 +1,135 @@
+/* Copyright 2013 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 "NewsChecker.h"
+
+#include <QByteArray>
+#include <QDomDocument>
+
+#include <logger/QsLog.h>
+
+NewsChecker::NewsChecker(const QString& feedUrl)
+{
+ m_feedUrl = feedUrl;
+}
+
+void NewsChecker::reloadNews()
+{
+ // Start a netjob to download the RSS feed and call rssDownloadFinished() when it's done.
+ if (isLoadingNews())
+ {
+ QLOG_INFO() << "Ignored request to reload news. Currently reloading already.";
+ return;
+ }
+
+ QLOG_INFO() << "Reloading news.";
+
+ NetJob* job = new NetJob("News RSS Feed");
+ job->addNetAction(ByteArrayDownload::make(m_feedUrl));
+ QObject::connect(job, &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &NewsChecker::rssDownloadFailed);
+ m_newsNetJob.reset(job);
+ job->start();
+}
+
+void NewsChecker::rssDownloadFinished()
+{
+ // Parse the XML file and process the RSS feed entries.
+ QLOG_DEBUG() << "Finished loading RSS feed.";
+
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(m_newsNetJob->first());
+ data = dl->m_data;
+ m_newsNetJob.reset();
+ }
+
+ QDomDocument doc;
+ {
+ // Stuff to store error info in.
+ QString errorMsg = "Unknown error.";
+ int errorLine = -1;
+ int errorCol = -1;
+
+ // Parse the XML.
+ if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol))
+ {
+ QString fullErrorMsg = QString("Error parsing RSS feed XML. %s at %d:%d.").arg(errorMsg, errorLine, errorCol);
+ fail(fullErrorMsg);
+ return;
+ }
+ }
+
+ // If the parsing succeeded, read it.
+ QDomNodeList items = doc.elementsByTagName("item");
+ m_newsEntries.clear();
+ for (int i = 0; i < items.length(); i++)
+ {
+ QDomElement element = items.at(i).toElement();
+ NewsEntryPtr entry;
+ entry.reset(new NewsEntry());
+ QString errorMsg = "An unknown error occurred.";
+ if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg))
+ {
+ QLOG_DEBUG() << "Loaded news entry" << entry->title;
+ m_newsEntries.append(entry);
+ }
+ else
+ {
+ QLOG_WARN() << "Failed to load news entry at index" << i << ":" << errorMsg;
+ }
+ }
+
+ succeed();
+}
+
+void NewsChecker::rssDownloadFailed()
+{
+ // Set an error message and fail.
+ fail("Failed to load news RSS feed.");
+}
+
+
+QList<NewsEntryPtr> NewsChecker::getNewsEntries() const
+{
+ return m_newsEntries;
+}
+
+bool NewsChecker::isLoadingNews() const
+{
+ return m_newsNetJob.get() != nullptr;
+}
+
+QString NewsChecker::getLastLoadErrorMsg() const
+{
+ return m_lastLoadError;
+}
+
+void NewsChecker::succeed()
+{
+ m_lastLoadError = "";
+ QLOG_DEBUG() << "News loading succeeded.";
+ m_newsNetJob.reset();
+ emit newsLoaded();
+}
+
+void NewsChecker::fail(const QString& errorMsg)
+{
+ m_lastLoadError = errorMsg;
+ QLOG_DEBUG() << "Failed to load news:" << errorMsg;
+ m_newsNetJob.reset();
+ emit newsLoadingFailed(errorMsg);
+}
+
diff --git a/logic/news/NewsChecker.h b/logic/news/NewsChecker.h
new file mode 100644
index 00000000..820fe626
--- /dev/null
+++ b/logic/news/NewsChecker.h
@@ -0,0 +1,105 @@
+/* Copyright 2013 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 <QObject>
+#include <QString>
+#include <QList>
+
+#include <logic/net/NetJob.h>
+
+#include "NewsEntry.h"
+
+class NewsChecker : public QObject
+{
+ Q_OBJECT
+public:
+ /*!
+ * Constructs a news reader to read from the given RSS feed URL.
+ */
+ NewsChecker(const QString& feedUrl);
+
+ /*!
+ * Returns the error message for the last time the news was loaded.
+ * Empty string if the last load was successful.
+ */
+ QString getLastLoadErrorMsg() const;
+
+ /*!
+ * Returns true if the news has been loaded successfully.
+ */
+ bool isNewsLoaded() const;
+
+ //! True if the news is currently loading. If true, reloadNews() will do nothing.
+ bool isLoadingNews() const;
+
+ /*!
+ * Returns a list of news entries.
+ */
+ QList<NewsEntryPtr> getNewsEntries() const;
+
+ /*!
+ * Reloads the news from the website's RSS feed.
+ * If the news is already loading, this does nothing.
+ */
+ void Q_SLOT reloadNews();
+
+signals:
+ /*!
+ * Signal fired after the news has finished loading.
+ */
+ void newsLoaded();
+
+ /*!
+ * Signal fired after the news fails to load.
+ */
+ void newsLoadingFailed(QString errorMsg);
+
+protected slots:
+ void rssDownloadFinished();
+ void rssDownloadFailed();
+
+protected:
+ //! The URL for the RSS feed to fetch.
+ QString m_feedUrl;
+
+ //! List of news entries.
+ QList<NewsEntryPtr> m_newsEntries;
+
+ //! The network job to use to load the news.
+ NetJobPtr m_newsNetJob;
+
+ //! True if news has been loaded.
+ bool m_loadedNews;
+
+ /*!
+ * Gets the error message that was given last time the news was loaded.
+ * If the last news load succeeded, this will be an empty string.
+ */
+ QString m_lastLoadError;
+
+
+ /*!
+ * Emits newsLoaded() and sets m_lastLoadError to empty string.
+ */
+ void Q_SLOT succeed();
+
+ /*!
+ * Emits newsLoadingFailed() and sets m_lastLoadError to the given message.
+ */
+ void Q_SLOT fail(const QString& errorMsg);
+};
+
diff --git a/logic/news/NewsEntry.cpp b/logic/news/NewsEntry.cpp
new file mode 100644
index 00000000..4c940f2e
--- /dev/null
+++ b/logic/news/NewsEntry.cpp
@@ -0,0 +1,77 @@
+/* Copyright 2013 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 "NewsEntry.h"
+
+#include <QDomNodeList>
+#include <QVariant>
+
+NewsEntry::NewsEntry(QObject* parent) :
+ QObject(parent)
+{
+ this->title = tr("Untitled");
+ this->content = tr("No content.");
+ this->link = "";
+ this->author = tr("Unknown Author");
+ this->pubDate = QDateTime::currentDateTime();
+}
+
+NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent) :
+ QObject(parent)
+{
+ this->title = title;
+ this->content = content;
+ this->link = link;
+ this->author = author;
+ this->pubDate = pubDate;
+}
+
+/*!
+ * Gets the text content of the given child element as a QVariant.
+ */
+inline QString childValue(const QDomElement& element, const QString& childName, QString defaultVal="")
+{
+ QDomNodeList nodes = element.elementsByTagName(childName);
+ if (nodes.count() > 0)
+ {
+ QDomElement element = nodes.at(0).toElement();
+ return element.text();
+ }
+ else
+ {
+ return defaultVal;
+ }
+}
+
+bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg)
+{
+ QString title = childValue(element, "title", tr("Untitled"));
+ QString content = childValue(element, "description", tr("No content."));
+ QString link = childValue(element, "link");
+ QString author = childValue(element, "dc:creator", tr("Unknown Author"));
+ QString pubDateStr = childValue(element, "pubDate");
+
+ // FIXME: For now, we're just ignoring timezones. We assume that all time zones in the RSS feed are the same.
+ QString dateFormat("ddd, dd MMM yyyy hh:mm:ss");
+ QDateTime pubDate = QDateTime::fromString(pubDateStr, dateFormat);
+
+ entry->title = title;
+ entry->content = content;
+ entry->link = link;
+ entry->author = author;
+ entry->pubDate = pubDate;
+ return true;
+}
+
diff --git a/logic/news/NewsEntry.h b/logic/news/NewsEntry.h
new file mode 100644
index 00000000..6bfa1adc
--- /dev/null
+++ b/logic/news/NewsEntry.h
@@ -0,0 +1,65 @@
+/* Copyright 2013 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 <QObject>
+#include <QString>
+#include <QDomElement>
+#include <QDateTime>
+
+#include <memory>
+
+class NewsEntry : public QObject
+{
+ Q_OBJECT
+
+public:
+ /*!
+ * Constructs an empty news entry.
+ */
+ explicit NewsEntry(QObject* parent=0);
+
+ /*!
+ * Constructs a new news entry.
+ * Note that content may contain HTML.
+ */
+ NewsEntry(const QString& title, const QString& content, const QString& link, const QString& author, const QDateTime& pubDate, QObject* parent=0);
+
+ /*!
+ * Attempts to load information from the given XML element into the given news entry pointer.
+ * If this fails, the function will return false and store an error message in the errorMsg pointer.
+ */
+ static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg=0);
+
+
+ //! The post title.
+ QString title;
+
+ //! The post's content. May contain HTML.
+ QString content;
+
+ //! URL to the post.
+ QString link;
+
+ //! The post's author.
+ QString author;
+
+ //! The date and time that this post was published.
+ QDateTime pubDate;
+};
+
+typedef std::shared_ptr<NewsEntry> NewsEntryPtr;
+
diff --git a/logic/status/StatusChecker.cpp b/logic/status/StatusChecker.cpp
new file mode 100644
index 00000000..66f800ae
--- /dev/null
+++ b/logic/status/StatusChecker.cpp
@@ -0,0 +1,137 @@
+/* Copyright 2013 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 "StatusChecker.h"
+
+#include <logic/net/URLConstants.h>
+
+#include <QByteArray>
+#include <QDomDocument>
+
+#include <logger/QsLog.h>
+
+StatusChecker::StatusChecker()
+{
+
+}
+
+void StatusChecker::reloadStatus()
+{
+ if (isLoadingStatus())
+ {
+ // QLOG_INFO() << "Ignored request to reload status. Currently reloading already.";
+ return;
+ }
+
+ // QLOG_INFO() << "Reloading status.";
+
+ NetJob* job = new NetJob("Status JSON");
+ job->addNetAction(ByteArrayDownload::make(URLConstants::MOJANG_STATUS_URL));
+ QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
+ m_statusNetJob.reset(job);
+ job->start();
+}
+
+void StatusChecker::statusDownloadFinished()
+{
+ QLOG_DEBUG() << "Finished loading status JSON.";
+
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(m_statusNetJob->first());
+ data = dl->m_data;
+ m_statusNetJob.reset();
+ }
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ fail("Error parsing status JSON:" + jsonError.errorString());
+ return;
+ }
+
+ if (!jsonDoc.isArray())
+ {
+ fail("Error parsing status JSON: JSON root is not an array");
+ return;
+ }
+
+ QJsonArray root = jsonDoc.array();
+
+ for(auto status = root.begin(); status != root.end(); ++status)
+ {
+ QVariantMap map = (*status).toObject().toVariantMap();
+
+ for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
+ {
+ QString key = iter.key();
+ QVariant value = iter.value();
+
+ if(value.type() == QVariant::Type::String)
+ {
+ m_statusEntries.insert(key, value.toString());
+ //QLOG_DEBUG() << "Status JSON object: " << key << m_statusEntries[key];
+ }
+ else
+ {
+ fail("Malformed status JSON: expected status type to be a string.");
+ return;
+ }
+ }
+ }
+
+ succeed();
+}
+
+void StatusChecker::statusDownloadFailed()
+{
+ fail("Failed to load status JSON.");
+}
+
+
+QMap<QString, QString> StatusChecker::getStatusEntries() const
+{
+ return m_statusEntries;
+}
+
+bool StatusChecker::isLoadingStatus() const
+{
+ return m_statusNetJob.get() != nullptr;
+}
+
+QString StatusChecker::getLastLoadErrorMsg() const
+{
+ return m_lastLoadError;
+}
+
+void StatusChecker::succeed()
+{
+ m_lastLoadError = "";
+ QLOG_DEBUG() << "Status loading succeeded.";
+ m_statusNetJob.reset();
+ emit statusLoaded();
+}
+
+void StatusChecker::fail(const QString& errorMsg)
+{
+ m_lastLoadError = errorMsg;
+ QLOG_DEBUG() << "Failed to load status:" << errorMsg;
+ m_statusNetJob.reset();
+ emit statusLoadingFailed(errorMsg);
+}
+
diff --git a/logic/status/StatusChecker.h b/logic/status/StatusChecker.h
new file mode 100644
index 00000000..1cb01836
--- /dev/null
+++ b/logic/status/StatusChecker.h
@@ -0,0 +1,57 @@
+/* Copyright 2013 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 <QObject>
+#include <QString>
+#include <QList>
+
+#include <logic/net/NetJob.h>
+
+class StatusChecker : public QObject
+{
+ Q_OBJECT
+public:
+ StatusChecker();
+
+ QString getLastLoadErrorMsg() const;
+
+ bool isStatusLoaded() const;
+
+ bool isLoadingStatus() const;
+
+ QMap<QString, QString> getStatusEntries() const;
+
+ void Q_SLOT reloadStatus();
+
+signals:
+ void statusLoaded();
+ void statusLoadingFailed(QString errorMsg);
+
+protected slots:
+ void statusDownloadFinished();
+ void statusDownloadFailed();
+
+protected:
+ QMap<QString, QString> m_statusEntries;
+ NetJobPtr m_statusNetJob;
+ bool m_loadedStatus;
+ QString m_lastLoadError;
+
+ void Q_SLOT succeed();
+ void Q_SLOT fail(const QString& errorMsg);
+};
+
diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h
new file mode 100644
index 00000000..15e453a3
--- /dev/null
+++ b/logic/tasks/ProgressProvider.h
@@ -0,0 +1,42 @@
+/* Copyright 2013 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 <QObject>
+
+class ProgressProvider : public QObject
+{
+ Q_OBJECT
+protected:
+ explicit ProgressProvider(QObject *parent = 0) : QObject(parent)
+ {
+ }
+signals:
+ void started();
+ void progress(qint64 current, qint64 total);
+ void succeeded();
+ void failed(QString reason);
+ void status(QString status);
+
+public:
+ virtual QString getStatus() const = 0;
+ virtual void getProgress(qint64 &current, qint64 &total) = 0;
+ virtual bool isRunning() const = 0;
+public
+slots:
+ virtual void start() = 0;
+ virtual void abort() = 0;
+};
diff --git a/logic/tasks/SequentialTask.cpp b/logic/tasks/SequentialTask.cpp
new file mode 100644
index 00000000..63025eee
--- /dev/null
+++ b/logic/tasks/SequentialTask.cpp
@@ -0,0 +1,77 @@
+#include "SequentialTask.h"
+
+SequentialTask::SequentialTask(QObject *parent) :
+ Task(parent), m_currentIndex(-1)
+{
+
+}
+
+QString SequentialTask::getStatus() const
+{
+ if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
+ {
+ return QString();
+ }
+ return m_queue.at(m_currentIndex)->getStatus();
+}
+
+void SequentialTask::getProgress(qint64 &current, qint64 &total)
+{
+ current = 0;
+ total = 0;
+ for (int i = 0; i < m_queue.size(); ++i)
+ {
+ qint64 subCurrent, subTotal;
+ m_queue.at(i)->getProgress(subCurrent, subTotal);
+ current += subCurrent;
+ total += subTotal;
+ }
+}
+
+void SequentialTask::addTask(std::shared_ptr<Task> task)
+{
+ m_queue.append(task);
+}
+
+void SequentialTask::executeTask()
+{
+ m_currentIndex = -1;
+ startNext();
+}
+
+void SequentialTask::startNext()
+{
+ if (m_currentIndex != -1)
+ {
+ std::shared_ptr<Task> previous = m_queue[m_currentIndex];
+ disconnect(previous.get(), 0, this, 0);
+ }
+ m_currentIndex++;
+ if (m_queue.isEmpty() || m_currentIndex >= m_queue.size())
+ {
+ emitSucceeded();
+ return;
+ }
+ std::shared_ptr<Task> next = m_queue[m_currentIndex];
+ connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString)));
+ connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString)));
+ connect(next.get(), SIGNAL(progress(qint64,qint64)), this, SLOT(subTaskProgress()));
+ connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext()));
+ next->start();
+ emit status(getStatus());
+}
+
+void SequentialTask::subTaskFailed(const QString &msg)
+{
+ emitFailed(msg);
+}
+void SequentialTask::subTaskStatus(const QString &msg)
+{
+ setStatus(msg);
+}
+void SequentialTask::subTaskProgress()
+{
+ qint64 current, total;
+ getProgress(current, total);
+ setProgress(100 * current / total);
+}
diff --git a/logic/tasks/SequentialTask.h b/logic/tasks/SequentialTask.h
new file mode 100644
index 00000000..7f046928
--- /dev/null
+++ b/logic/tasks/SequentialTask.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "Task.h"
+
+#include <QQueue>
+#include <memory>
+
+class SequentialTask : public Task
+{
+ Q_OBJECT
+public:
+ explicit SequentialTask(QObject *parent = 0);
+
+ virtual QString getStatus() const;
+ virtual void getProgress(qint64 &current, qint64 &total);
+
+ void addTask(std::shared_ptr<Task> task);
+
+protected:
+ void executeTask();
+
+private
+slots:
+ void startNext();
+ void subTaskFailed(const QString &msg);
+ void subTaskStatus(const QString &msg);
+ void subTaskProgress();
+
+private:
+ QQueue<std::shared_ptr<Task> > m_queue;
+ int m_currentIndex;
+};
diff --git a/logic/tasks/Task.cpp b/logic/tasks/Task.cpp
new file mode 100644
index 00000000..cb7a5443
--- /dev/null
+++ b/logic/tasks/Task.cpp
@@ -0,0 +1,84 @@
+/* Copyright 2013 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 "Task.h"
+#include "logger/QsLog.h"
+
+Task::Task(QObject *parent) : ProgressProvider(parent)
+{
+}
+
+QString Task::getStatus() const
+{
+ return m_status;
+}
+
+void Task::setStatus(const QString &new_status)
+{
+ m_status = new_status;
+ emit status(new_status);
+}
+
+void Task::setProgress(int new_progress)
+{
+ m_progress = new_progress;
+ emit progress(new_progress, 100);
+}
+
+void Task::getProgress(qint64 &current, qint64 &total)
+{
+ current = m_progress;
+ total = 100;
+}
+
+void Task::start()
+{
+ m_running = true;
+ emit started();
+ executeTask();
+}
+
+void Task::emitFailed(QString reason)
+{
+ m_running = false;
+ m_succeeded = false;
+ m_failReason = reason;
+ QLOG_ERROR() << "Task failed: " << reason;
+ emit failed(reason);
+}
+
+void Task::emitSucceeded()
+{
+ m_running = false;
+ m_succeeded = true;
+ QLOG_INFO() << "Task succeeded";
+ emit succeeded();
+}
+
+bool Task::isRunning() const
+{
+ return m_running;
+}
+
+bool Task::successful() const
+{
+ return m_succeeded;
+}
+
+QString Task::failReason() const
+{
+ return m_failReason;
+}
+
diff --git a/logic/tasks/Task.h b/logic/tasks/Task.h
new file mode 100644
index 00000000..80d5e38b
--- /dev/null
+++ b/logic/tasks/Task.h
@@ -0,0 +1,66 @@
+/* Copyright 2013 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 <QObject>
+#include <QString>
+#include "ProgressProvider.h"
+
+class Task : public ProgressProvider
+{
+ Q_OBJECT
+public:
+ explicit Task(QObject *parent = 0);
+
+ virtual QString getStatus() const;
+ virtual void getProgress(qint64 &current, qint64 &total);
+ virtual bool isRunning() const;
+
+ /*!
+ * True if this task was successful.
+ * If the task failed or is still running, returns false.
+ */
+ virtual bool successful() const;
+
+ /*!
+ * Returns the string that was passed to emitFailed as the error message when the task failed.
+ * If the task hasn't failed, returns an empty string.
+ */
+ virtual QString failReason() const;
+
+public
+slots:
+ virtual void start();
+ virtual void abort() {};
+
+protected:
+ virtual void executeTask() = 0;
+
+ virtual void emitSucceeded();
+ virtual void emitFailed(QString reason);
+
+protected
+slots:
+ void setStatus(const QString &status);
+ void setProgress(int progress);
+
+protected:
+ QString m_status;
+ int m_progress = 0;
+ bool m_running = false;
+ bool m_succeeded = false;
+ QString m_failReason = "";
+};
diff --git a/logic/tasks/ThreadTask.cpp b/logic/tasks/ThreadTask.cpp
new file mode 100644
index 00000000..ddd1dee5
--- /dev/null
+++ b/logic/tasks/ThreadTask.cpp
@@ -0,0 +1,41 @@
+#include "ThreadTask.h"
+#include <QtConcurrentRun>
+ThreadTask::ThreadTask(Task * internal, QObject *parent) : Task(parent), m_internal(internal)
+{
+}
+
+void ThreadTask::start()
+{
+ connect(m_internal, SIGNAL(failed(QString)), SLOT(iternal_failed(QString)));
+ connect(m_internal, SIGNAL(progress(qint64,qint64)), SLOT(iternal_progress(qint64,qint64)));
+ connect(m_internal, SIGNAL(started()), SLOT(iternal_started()));
+ connect(m_internal, SIGNAL(status(QString)), SLOT(iternal_status(QString)));
+ connect(m_internal, SIGNAL(succeeded()), SLOT(iternal_succeeded()));
+ m_running = true;
+ QtConcurrent::run(m_internal, &Task::start);
+}
+
+void ThreadTask::iternal_failed(QString reason)
+{
+ emitFailed(reason);
+}
+
+void ThreadTask::iternal_progress(qint64 current, qint64 total)
+{
+ progress(current, total);
+}
+
+void ThreadTask::iternal_started()
+{
+ emit started();
+}
+
+void ThreadTask::iternal_status(QString status)
+{
+ setStatus(status);
+}
+
+void ThreadTask::iternal_succeeded()
+{
+ emitSucceeded();
+}
diff --git a/logic/tasks/ThreadTask.h b/logic/tasks/ThreadTask.h
new file mode 100644
index 00000000..718dbc91
--- /dev/null
+++ b/logic/tasks/ThreadTask.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Task.h"
+
+class ThreadTask : public Task
+{
+ Q_OBJECT
+public:
+ explicit ThreadTask(Task * internal, QObject * parent = nullptr);
+
+protected:
+ void executeTask() {};
+
+public slots:
+ virtual void start();
+
+private slots:
+ void iternal_started();
+ void iternal_progress(qint64 current, qint64 total);
+ void iternal_succeeded();
+ void iternal_failed(QString reason);
+ void iternal_status(QString status);
+private:
+ Task * m_internal;
+}; \ No newline at end of file
diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp
new file mode 100644
index 00000000..83679f19
--- /dev/null
+++ b/logic/updater/DownloadUpdateTask.cpp
@@ -0,0 +1,543 @@
+/* Copyright 2013 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 "DownloadUpdateTask.h"
+
+#include "MultiMC.h"
+#include "logic/updater/UpdateChecker.h"
+#include "logic/net/NetJob.h"
+#include "pathutils.h"
+
+#include <QFile>
+#include <QTemporaryDir>
+#include <QCryptographicHash>
+
+#include <QDomDocument>
+
+DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject *parent)
+ : Task(parent)
+{
+ m_cVersionId = MMC->version().build;
+
+ m_nRepoUrl = repoUrl;
+ m_nVersionId = versionId;
+
+ m_updateFilesDir.setAutoRemove(false);
+}
+
+void DownloadUpdateTask::executeTask()
+{
+ // GO!
+ // This will call the next step when it's done.
+ findCurrentVersionInfo();
+}
+
+void DownloadUpdateTask::processChannels()
+{
+ auto checker = MMC->updateChecker();
+
+ // Now, check the channel list again.
+ if (!checker->hasChannels())
+ {
+ // We still couldn't load the channel list. Give up. Call loadVersionInfo and return.
+ QLOG_INFO() << "Reloading the channel list didn't work. Giving up.";
+ loadVersionInfo();
+ return;
+ }
+
+ QList<UpdateChecker::ChannelListEntry> channels = checker->getChannelList();
+ QString channelId = MMC->version().channel;
+
+ m_cRepoUrl.clear();
+ // Search through the channel list for a channel with the correct ID.
+ for (auto channel : channels)
+ {
+ if (channel.id == channelId)
+ {
+ QLOG_INFO() << "Found matching channel.";
+ m_cRepoUrl = channel.url;
+ break;
+ }
+ }
+
+ // Now that we've done that, load version info.
+ loadVersionInfo();
+}
+
+void DownloadUpdateTask::findCurrentVersionInfo()
+{
+ setStatus(tr("Finding information about the current version..."));
+
+ auto checker = MMC->updateChecker();
+
+ if (!checker->hasChannels())
+ {
+ // Load the channel list and wait for it to finish loading.
+ QLOG_INFO() << "No channel list entries found. Will try reloading it.";
+
+ QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, this,
+ &DownloadUpdateTask::processChannels);
+ checker->updateChanList();
+ }
+ else
+ {
+ processChannels();
+ }
+}
+
+void DownloadUpdateTask::loadVersionInfo()
+{
+ setStatus(tr("Loading version information..."));
+
+ // Create the net job for loading version info.
+ NetJob *netJob = new NetJob("Version Info");
+
+ // Find the index URL.
+ QUrl newIndexUrl = QUrl(m_nRepoUrl).resolved(QString::number(m_nVersionId) + ".json");
+ QLOG_DEBUG() << m_nRepoUrl << " turns into " << newIndexUrl;
+
+ // Add a net action to download the version info for the version we're updating to.
+ netJob->addNetAction(ByteArrayDownload::make(newIndexUrl));
+
+ // If we have a current version URL, get that one too.
+ if (!m_cRepoUrl.isEmpty())
+ {
+ QUrl cIndexUrl = QUrl(m_cRepoUrl).resolved(QString::number(m_cVersionId) + ".json");
+ netJob->addNetAction(ByteArrayDownload::make(cIndexUrl));
+ QLOG_DEBUG() << m_cRepoUrl << " turns into " << cIndexUrl;
+ }
+
+ // Connect slots so we know when it's done.
+ QObject::connect(netJob, &NetJob::succeeded, this,
+ &DownloadUpdateTask::vinfoDownloadFinished);
+ QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::vinfoDownloadFailed);
+
+ // Store the NetJob in a class member. We don't want to lose it!
+ m_vinfoNetJob.reset(netJob);
+
+ // Finally, we start the network job and the thread's event loop to wait for it to finish.
+ netJob->start();
+}
+
+void DownloadUpdateTask::vinfoDownloadFinished()
+{
+ // Both downloads succeeded. OK. Parse stuff.
+ parseDownloadedVersionInfo();
+}
+
+void DownloadUpdateTask::vinfoDownloadFailed()
+{
+ // Something failed. We really need the second download (current version info), so parse
+ // downloads anyways as long as the first one succeeded.
+ if (m_vinfoNetJob->first()->m_status != Job_Failed)
+ {
+ parseDownloadedVersionInfo();
+ return;
+ }
+
+ // TODO: Give a more detailed error message.
+ QLOG_ERROR() << "Failed to download version info files.";
+ emitFailed(tr("Failed to download version info files."));
+}
+
+void DownloadUpdateTask::parseDownloadedVersionInfo()
+{
+ setStatus(tr("Reading file list for new version..."));
+ QLOG_DEBUG() << "Reading file list for new version...";
+ QString error;
+ if (!parseVersionInfo(
+ std::dynamic_pointer_cast<ByteArrayDownload>(m_vinfoNetJob->first())->m_data,
+ &m_nVersionFileList, &error))
+ {
+ emitFailed(error);
+ return;
+ }
+
+ // If there is a second entry in the network job's list, load it as the current version's
+ // info.
+ if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed)
+ {
+ setStatus(tr("Reading file list for current version..."));
+ QLOG_DEBUG() << "Reading file list for current version...";
+ QString error;
+ parseVersionInfo(
+ std::dynamic_pointer_cast<ByteArrayDownload>(m_vinfoNetJob->operator[](1))->m_data,
+ &m_cVersionFileList, &error);
+ }
+
+ // We don't need this any more.
+ m_vinfoNetJob.reset();
+
+ // Now that we're done loading version info, we can move on to the next step. Process file
+ // lists and download files.
+ processFileLists();
+}
+
+bool DownloadUpdateTask::parseVersionInfo(const QByteArray &data, VersionFileList *list,
+ QString *error)
+{
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ *error = QString("Failed to parse version info JSON: %1 at %2")
+ .arg(jsonError.errorString())
+ .arg(jsonError.offset);
+ QLOG_ERROR() << error;
+ return false;
+ }
+
+ QJsonObject json = jsonDoc.object();
+
+ QLOG_DEBUG() << data;
+ QLOG_DEBUG() << "Loading version info from JSON.";
+ QJsonArray filesArray = json.value("Files").toArray();
+ for (QJsonValue fileValue : filesArray)
+ {
+ QJsonObject fileObj = fileValue.toObject();
+
+ QString file_path = fileObj.value("Path").toString();
+#ifdef Q_OS_MAC
+ // On OSX, the paths for the updater need to be fixed.
+ // basically, anything that isn't in the .app folder is ignored.
+ // everything else is changed so the code that processes the files actually finds
+ // them and puts the replacements in the right spots.
+ if (!fixPathForOSX(file_path))
+ continue;
+#endif
+ VersionFileEntry file{file_path, fileObj.value("Perms").toVariant().toInt(),
+ FileSourceList(), fileObj.value("MD5").toString(), };
+ QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode;
+
+ QJsonArray sourceArray = fileObj.value("Sources").toArray();
+ for (QJsonValue val : sourceArray)
+ {
+ QJsonObject sourceObj = val.toObject();
+
+ QString type = sourceObj.value("SourceType").toString();
+ if (type == "http")
+ {
+ file.sources.append(
+ FileSource("http", sourceObj.value("Url").toString()));
+ }
+ else if (type == "httpc")
+ {
+ file.sources.append(
+ FileSource("httpc", sourceObj.value("Url").toString(),
+ sourceObj.value("CompressionType").toString()));
+ }
+ else
+ {
+ QLOG_WARN() << "Unknown source type" << type << "ignored.";
+ }
+ }
+
+ QLOG_DEBUG() << "Loaded info for" << file.path;
+
+ list->append(file);
+ }
+
+ return true;
+}
+
+void DownloadUpdateTask::processFileLists()
+{
+ // Create a network job for downloading files.
+ NetJob *netJob = new NetJob("Update Files");
+
+ if (!processFileLists(netJob, m_cVersionFileList, m_nVersionFileList, m_operationList))
+ {
+ emitFailed(tr("Failed to process update lists..."));
+ return;
+ }
+
+ // Add listeners to wait for the downloads to finish.
+ QObject::connect(netJob, &NetJob::succeeded, this,
+ &DownloadUpdateTask::fileDownloadFinished);
+ QObject::connect(netJob, &NetJob::progress, this,
+ &DownloadUpdateTask::fileDownloadProgressChanged);
+ QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed);
+
+ // Now start the download.
+ setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size())));
+ QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path();
+ m_filesNetJob.reset(netJob);
+ netJob->start();
+
+ writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml"));
+}
+
+bool
+DownloadUpdateTask::processFileLists(NetJob *job,
+ const DownloadUpdateTask::VersionFileList &currentVersion,
+ const DownloadUpdateTask::VersionFileList &newVersion,
+ DownloadUpdateTask::UpdateOperationList &ops)
+{
+ setStatus(tr("Processing file lists - figuring out how to install the update..."));
+
+ // First, if we've loaded the current version's file list, we need to iterate through it and
+ // delete anything in the current one version's list that isn't in the new version's list.
+ for (VersionFileEntry entry : currentVersion)
+ {
+ QFileInfo toDelete(PathCombine(MMC->root(), entry.path));
+ if (!toDelete.exists())
+ {
+ QLOG_ERROR() << "Expected file " << toDelete.absoluteFilePath()
+ << " doesn't exist!";
+ }
+ bool keep = false;
+
+ //
+ for (VersionFileEntry newEntry : newVersion)
+ {
+ if (newEntry.path == entry.path)
+ {
+ QLOG_DEBUG() << "Not deleting" << entry.path
+ << "because it is still present in the new version.";
+ keep = true;
+ break;
+ }
+ }
+
+ // If the loop reaches the end and we didn't find a match, delete the file.
+ if (!keep)
+ {
+ if (toDelete.exists())
+ ops.append(UpdateOperation::DeleteOp(entry.path));
+ }
+ }
+
+ // Next, check each file in MultiMC's folder and see if we need to update them.
+ for (VersionFileEntry entry : newVersion)
+ {
+ // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a
+ // way to do this in the background.
+ QString fileMD5;
+ QString realEntryPath = PathCombine(MMC->root(), entry.path);
+ QFile entryFile(realEntryPath);
+ QFileInfo entryInfo(realEntryPath);
+
+ bool needs_upgrade = false;
+ if (!entryFile.exists())
+ {
+ needs_upgrade = true;
+ }
+ else
+ {
+ bool pass = true;
+ if (!entryInfo.isReadable())
+ {
+ QLOG_ERROR() << "File " << realEntryPath << " is not readable.";
+ pass = false;
+ }
+ if (!entryInfo.isWritable())
+ {
+ QLOG_ERROR() << "File " << realEntryPath << " is not writable.";
+ pass = false;
+ }
+ if (!entryFile.open(QFile::ReadOnly))
+ {
+ QLOG_ERROR() << "File " << realEntryPath << " cannot be opened for reading.";
+ pass = false;
+ }
+ if (!pass)
+ {
+ QLOG_ERROR() << "ROOT: " << MMC->root();
+ ops.clear();
+ return false;
+ }
+ }
+
+ if(!needs_upgrade)
+ {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ auto foo = entryFile.readAll();
+
+ hash.addData(foo);
+ fileMD5 = hash.result().toHex();
+ if ((fileMD5 != entry.md5))
+ {
+ QLOG_DEBUG() << "MD5Sum does not match!";
+ QLOG_DEBUG() << "Expected:'" << entry.md5 << "'";
+ QLOG_DEBUG() << "Got: '" << fileMD5 << "'";
+ needs_upgrade = true;
+ }
+ }
+
+ // skip file. it doesn't need an upgrade.
+ if (!needs_upgrade)
+ {
+ QLOG_DEBUG() << "File" << realEntryPath << " does not need updating.";
+ continue;
+ }
+
+ // yep. this file actually needs an upgrade. PROCEED.
+ QLOG_DEBUG() << "Found file" << realEntryPath << " that needs updating.";
+
+ // if it's the updater we want to treat it separately
+ bool isUpdater = entry.path.endsWith("updater") || entry.path.endsWith("updater.exe");
+
+ // Go through the sources list and find one to use.
+ // TODO: Make a NetAction that takes a source list and tries each of them until one
+ // works. For now, we'll just use the first http one.
+ for (FileSource source : entry.sources)
+ {
+ if (source.type == "http")
+ {
+ QLOG_DEBUG() << "Will download" << entry.path << "from" << source.url;
+
+ // Download it to updatedir/<filepath>-<md5> where filepath is the file's
+ // path with slashes replaced by underscores.
+ QString dlPath =
+ PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_"));
+
+ if (isUpdater)
+ {
+#ifdef MultiMC_UPDATER_FORCE_LOCAL
+ QLOG_DEBUG() << "Skipping updater download and using local version.";
+#else
+ auto cache_entry = MMC->metacache()->resolveEntry("root", entry.path);
+ QLOG_DEBUG() << "Updater will be in " << cache_entry->getFullPath();
+ // force check.
+ cache_entry->stale = true;
+
+ auto download = CacheDownload::make(QUrl(source.url), cache_entry);
+ job->addNetAction(download);
+#endif
+ }
+ else
+ {
+ // We need to download the file to the updatefiles folder and add a task
+ // to copy it to its install path.
+ auto download = MD5EtagDownload::make(source.url, dlPath);
+ download->m_expected_md5 = entry.md5;
+ job->addNetAction(download);
+ ops.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode));
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool DownloadUpdateTask::writeInstallScript(UpdateOperationList &opsList, QString scriptFile)
+{
+ // Build the base structure of the XML document.
+ QDomDocument doc;
+
+ QDomElement root = doc.createElement("update");
+ root.setAttribute("version", "3");
+ doc.appendChild(root);
+
+ QDomElement installFiles = doc.createElement("install");
+ root.appendChild(installFiles);
+
+ QDomElement removeFiles = doc.createElement("uninstall");
+ root.appendChild(removeFiles);
+
+ // Write the operation list to the XML document.
+ for (UpdateOperation op : opsList)
+ {
+ QDomElement file = doc.createElement("file");
+
+ switch (op.type)
+ {
+ case UpdateOperation::OP_COPY:
+ {
+ // Install the file.
+ QDomElement name = doc.createElement("source");
+ QDomElement path = doc.createElement("dest");
+ QDomElement mode = doc.createElement("mode");
+ name.appendChild(doc.createTextNode(op.file));
+ path.appendChild(doc.createTextNode(op.dest));
+ // We need to add a 0 at the beginning here, because Qt doesn't convert to octal
+ // correctly.
+ mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8)));
+ file.appendChild(name);
+ file.appendChild(path);
+ file.appendChild(mode);
+ installFiles.appendChild(file);
+ QLOG_DEBUG() << "Will install file " << op.file << " to " << op.dest;
+ }
+ break;
+
+ case UpdateOperation::OP_DELETE:
+ {
+ // Delete the file.
+ file.appendChild(doc.createTextNode(op.file));
+ removeFiles.appendChild(file);
+ QLOG_DEBUG() << "Will remove file" << op.file;
+ }
+ break;
+
+ default:
+ QLOG_WARN() << "Can't write update operation of type" << op.type
+ << "to file. Not implemented.";
+ continue;
+ }
+ }
+
+ // Write the XML document to the file.
+ QFile outFile(scriptFile);
+
+ if (outFile.open(QIODevice::WriteOnly))
+ {
+ outFile.write(doc.toByteArray());
+ }
+ else
+ {
+ emitFailed(tr("Failed to write update script file."));
+ return false;
+ }
+
+ return true;
+}
+
+bool DownloadUpdateTask::fixPathForOSX(QString &path)
+{
+ if (path.startsWith("MultiMC.app/"))
+ {
+ // remove the prefix and add a new, more appropriate one.
+ path.remove(0, 12);
+ return true;
+ }
+ else
+ {
+ QLOG_ERROR() << "Update path not within .app: " << path;
+ return false;
+ }
+}
+
+void DownloadUpdateTask::fileDownloadFinished()
+{
+ emitSucceeded();
+}
+
+void DownloadUpdateTask::fileDownloadFailed()
+{
+ // TODO: Give more info about the failure.
+ QLOG_ERROR() << "Failed to download update files.";
+ emitFailed(tr("Failed to download update files."));
+}
+
+void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 total)
+{
+ setProgress((int)(((float)current / (float)total) * 100));
+}
+
+QString DownloadUpdateTask::updateFilesDir()
+{
+ return m_updateFilesDir.path();
+}
diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h
new file mode 100644
index 00000000..518bc235
--- /dev/null
+++ b/logic/updater/DownloadUpdateTask.h
@@ -0,0 +1,217 @@
+/* Copyright 2013 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 "logic/tasks/Task.h"
+#include "logic/net/NetJob.h"
+
+/*!
+ * The DownloadUpdateTask is a task that takes a given version ID and repository URL,
+ * downloads that version's files from the repository, and prepares to install them.
+ */
+class DownloadUpdateTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent=0);
+
+ /*!
+ * Gets the directory that contains the update files.
+ */
+ QString updateFilesDir();
+
+public:
+
+ // TODO: We should probably put these data structures into a separate header...
+
+ /*!
+ * Struct that describes an entry in a VersionFileEntry's `Sources` list.
+ */
+ struct FileSource
+ {
+ FileSource(QString type, QString url, QString compression="")
+ {
+ this->type = type;
+ this->url = url;
+ this->compressionType = compression;
+ }
+
+ QString type;
+ QString url;
+ QString compressionType;
+ };
+ typedef QList<FileSource> FileSourceList;
+
+ /*!
+ * Structure that describes an entry in a GoUpdate version's `Files` list.
+ */
+ struct VersionFileEntry
+ {
+ QString path;
+ int mode;
+ FileSourceList sources;
+ QString md5;
+ };
+ typedef QList<VersionFileEntry> VersionFileList;
+
+ /*!
+ * Structure that describes an operation to perform when installing updates.
+ */
+ struct UpdateOperation
+ {
+ static UpdateOperation CopyOp(QString fsource, QString fdest, int fmode=0644) { return UpdateOperation{OP_COPY, fsource, fdest, fmode}; }
+ static UpdateOperation MoveOp(QString fsource, QString fdest, int fmode=0644) { return UpdateOperation{OP_MOVE, fsource, fdest, fmode}; }
+ static UpdateOperation DeleteOp(QString file) { return UpdateOperation{OP_DELETE, file, "", 0644}; }
+ static UpdateOperation ChmodOp(QString file, int fmode) { return UpdateOperation{OP_CHMOD, file, "", fmode}; }
+
+ //! Specifies the type of operation that this is.
+ enum Type
+ {
+ OP_COPY,
+ OP_DELETE,
+ OP_MOVE,
+ OP_CHMOD,
+ } type;
+
+ //! The file to operate on. If this is a DELETE or CHMOD operation, this is the file that will be modified.
+ QString file;
+
+ //! The destination file. If this is a DELETE or CHMOD operation, this field will be ignored.
+ QString dest;
+
+ //! The mode to change the source file to. Ignored if this isn't a CHMOD operation.
+ int mode;
+
+ // Yeah yeah, polymorphism blah blah inheritance, blah blah object oriented. I'm lazy, OK?
+ };
+ typedef QList<UpdateOperation> UpdateOperationList;
+
+protected:
+ friend class DownloadUpdateTaskTest;
+
+
+ /*!
+ * Used for arguments to parseVersionInfo and friends to specify which version info file to parse.
+ */
+ enum VersionInfoFileEnum { NEW_VERSION, CURRENT_VERSION };
+
+
+ //! Entry point for tasks.
+ virtual void executeTask();
+
+ /*!
+ * Attempts to find the version ID and repository URL for the current version.
+ * The function will look up the repository URL in the UpdateChecker's channel list.
+ * If the repository URL can't be found, this function will return false.
+ */
+ virtual void findCurrentVersionInfo();
+
+ /*!
+ * This runs after we've tried loading the channel list.
+ * If the channel list doesn't need to be loaded, this will be called immediately.
+ * If the channel list does need to be loaded, this will be called when it's done.
+ */
+ void processChannels();
+
+ /*!
+ * Downloads the version info files from the repository.
+ * The files for both the current build, and the build that we're updating to need to be downloaded.
+ * If the current version's info file can't be found, MultiMC will not delete files that
+ * were removed between versions. It will still replace files that have changed, however.
+ * Note that although the repository URL for the current version is not given to the update task,
+ * the task will attempt to look it up in the UpdateChecker's channel list.
+ * If an error occurs here, the function will call emitFailed and return false.
+ */
+ virtual void loadVersionInfo();
+
+ /*!
+ * This function is called when version information is finished downloading.
+ * This handles parsing the JSON downloaded by the version info network job and then calls processFileLists.
+ * Note that this function will sometimes be called even if the version info download emits failed. If
+ * we couldn't download the current version's info file, we can still update. This will be called even if the
+ * current version's info file fails to download, as long as the new version's info file succeeded.
+ */
+ virtual void parseDownloadedVersionInfo();
+
+ /*!
+ * Loads the file list from the given version info JSON object into the given list.
+ */
+ virtual bool parseVersionInfo(const QByteArray &data, VersionFileList* list, QString *error);
+
+ /*!
+ * Takes a list of file entries for the current version's files and the new version's files
+ * and populates the downloadList and operationList with information about how to download and install the update.
+ */
+ virtual bool processFileLists(NetJob *job, const VersionFileList &currentVersion, const VersionFileList &newVersion, UpdateOperationList &ops);
+
+ /*!
+ * Calls \see processFileLists to populate the \see m_operationList and a NetJob, and then executes
+ * the NetJob to fetch all needed files
+ */
+ virtual void processFileLists();
+
+ /*!
+ * Takes the operations list and writes an install script for the updater to the update files directory.
+ */
+ virtual bool writeInstallScript(UpdateOperationList& opsList, QString scriptFile);
+
+ UpdateOperationList m_operationList;
+
+ VersionFileList m_nVersionFileList;
+ VersionFileList m_cVersionFileList;
+
+ //! Network job for downloading version info files.
+ NetJobPtr m_vinfoNetJob;
+
+ //! Network job for downloading update files.
+ NetJobPtr m_filesNetJob;
+
+ // Version ID and repo URL for the new version.
+ int m_nVersionId;
+ QString m_nRepoUrl;
+
+ // Version ID and repo URL for the currently installed version.
+ int m_cVersionId;
+ QString m_cRepoUrl;
+
+ /*!
+ * Temporary directory to store update files in.
+ * This will be set to not auto delete. Task will fail if this fails to be created.
+ */
+ QTemporaryDir m_updateFilesDir;
+
+ /*!
+ * Filters paths
+ * This fixes destination paths for OSX.
+ * The updater runs in MultiMC.app/Contents/MacOs by default
+ * The destination paths are such as this: MultiMC.app/blah/blah
+ *
+ * Therefore we chop off the 'MultiMC.app' prefix
+ *
+ * Returns false if the path couldn't be fixed (is invalid)
+ */
+ static bool fixPathForOSX(QString &path);
+
+protected slots:
+ void vinfoDownloadFinished();
+ void vinfoDownloadFailed();
+
+ void fileDownloadFinished();
+ void fileDownloadFailed();
+ void fileDownloadProgressChanged(qint64 current, qint64 total);
+};
+
diff --git a/logic/updater/NotificationChecker.cpp b/logic/updater/NotificationChecker.cpp
new file mode 100644
index 00000000..191e90a3
--- /dev/null
+++ b/logic/updater/NotificationChecker.cpp
@@ -0,0 +1,121 @@
+#include "NotificationChecker.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "MultiMC.h"
+#include "MultiMCVersion.h"
+#include "logic/net/CacheDownload.h"
+
+NotificationChecker::NotificationChecker(QObject *parent)
+ : QObject(parent), m_notificationsUrl(QUrl(NOTIFICATION_URL))
+{
+ // this will call checkForNotifications once the event loop is running
+ QMetaObject::invokeMethod(this, "checkForNotifications", Qt::QueuedConnection);
+}
+
+QUrl NotificationChecker::notificationsUrl() const
+{
+ return m_notificationsUrl;
+}
+void NotificationChecker::setNotificationsUrl(const QUrl &notificationsUrl)
+{
+ m_notificationsUrl = notificationsUrl;
+}
+
+QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const
+{
+ return m_entries;
+}
+
+void NotificationChecker::checkForNotifications()
+{
+ if (!m_notificationsUrl.isValid())
+ {
+ QLOG_ERROR() << "Failed to check for notifications. No notifications URL set."
+ << "If you'd like to use MultiMC's notification system, please pass the "
+ "URL to CMake at compile time.";
+ return;
+ }
+ if (m_checkJob)
+ {
+ return;
+ }
+ m_checkJob.reset(new NetJob("Checking for notifications"));
+ auto entry = MMC->metacache()->resolveEntry("root", "notifications.json");
+ entry->stale = true;
+ m_checkJob->addNetAction(m_download = CacheDownload::make(m_notificationsUrl, entry));
+ connect(m_download.get(), &CacheDownload::succeeded, this,
+ &NotificationChecker::downloadSucceeded);
+ m_checkJob->start();
+}
+
+void NotificationChecker::downloadSucceeded(int)
+{
+ m_entries.clear();
+
+ QFile file(m_download->getTargetFilepath());
+ if (file.open(QFile::ReadOnly))
+ {
+ QJsonArray root = QJsonDocument::fromJson(file.readAll()).array();
+ for (auto it = root.begin(); it != root.end(); ++it)
+ {
+ QJsonObject obj = (*it).toObject();
+ NotificationEntry entry;
+ entry.id = obj.value("id").toDouble();
+ entry.message = obj.value("message").toString();
+ entry.channel = obj.value("channel").toString();
+ entry.platform = obj.value("platform").toString();
+ entry.from = obj.value("from").toString();
+ entry.to = obj.value("to").toString();
+ const QString type = obj.value("type").toString("critical");
+ if (type == "critical")
+ {
+ entry.type = NotificationEntry::Critical;
+ }
+ else if (type == "warning")
+ {
+ entry.type = NotificationEntry::Warning;
+ }
+ else if (type == "information")
+ {
+ entry.type = NotificationEntry::Information;
+ }
+ m_entries.append(entry);
+ }
+ }
+
+ m_checkJob.reset();
+
+ emit notificationCheckFinished();
+}
+
+bool NotificationChecker::NotificationEntry::applies() const
+{
+ MultiMCVersion version = MMC->version();
+ bool channelApplies = channel.isEmpty() || channel == version.channel;
+ bool platformApplies = platform.isEmpty() || platform == version.platform;
+ bool fromApplies =
+ from.isEmpty() || from == FULL_VERSION_STR || !versionLessThan(FULL_VERSION_STR, from);
+ bool toApplies =
+ to.isEmpty() || to == FULL_VERSION_STR || !versionLessThan(to, FULL_VERSION_STR);
+ return channelApplies && platformApplies && fromApplies && toApplies;
+}
+
+bool NotificationChecker::NotificationEntry::versionLessThan(const QString &v1,
+ const QString &v2)
+{
+ QStringList l1 = v1.split('.');
+ QStringList l2 = v2.split('.');
+ while (!l1.isEmpty() && !l2.isEmpty())
+ {
+ int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt();
+ int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt();
+ if (one != two)
+ {
+ return one < two;
+ }
+ }
+ return false;
+}
diff --git a/logic/updater/NotificationChecker.h b/logic/updater/NotificationChecker.h
new file mode 100644
index 00000000..915ee54d
--- /dev/null
+++ b/logic/updater/NotificationChecker.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <QObject>
+
+#include "logic/net/NetJob.h"
+#include "logic/net/CacheDownload.h"
+
+class NotificationChecker : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit NotificationChecker(QObject *parent = 0);
+
+ QUrl notificationsUrl() const;
+ void setNotificationsUrl(const QUrl &notificationsUrl);
+
+ struct NotificationEntry
+ {
+ int id;
+ QString message;
+ enum
+ {
+ Critical,
+ Warning,
+ Information
+ } type;
+ QString channel;
+ QString platform;
+ QString from;
+ QString to;
+ bool applies() const;
+ static bool versionLessThan(const QString &v1, const QString &v2);
+ };
+
+ QList<NotificationEntry> notificationEntries() const;
+
+public
+slots:
+ void checkForNotifications();
+
+private
+slots:
+ void downloadSucceeded(int);
+
+signals:
+ void notificationCheckFinished();
+
+private:
+ QList<NotificationEntry> m_entries;
+ QUrl m_notificationsUrl;
+ NetJobPtr m_checkJob;
+ CacheDownloadPtr m_download;
+};
diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp
new file mode 100644
index 00000000..8e2aa8b3
--- /dev/null
+++ b/logic/updater/UpdateChecker.cpp
@@ -0,0 +1,263 @@
+/* Copyright 2013 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 "UpdateChecker.h"
+
+#include "MultiMC.h"
+
+#include "logger/QsLog.h"
+
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
+
+#include <settingsobject.h>
+
+#define API_VERSION 0
+#define CHANLIST_FORMAT 0
+
+UpdateChecker::UpdateChecker()
+{
+ m_channelListUrl = CHANLIST_URL;
+ m_updateChecking = false;
+ m_chanListLoading = false;
+ m_checkUpdateWaiting = false;
+ m_chanListLoaded = false;
+}
+
+QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const
+{
+ return m_channels;
+}
+
+bool UpdateChecker::hasChannels() const
+{
+ return !m_channels.isEmpty();
+}
+
+void UpdateChecker::checkForUpdate(bool notifyNoUpdate)
+{
+ QLOG_DEBUG() << "Checking for updates.";
+
+ // If the channel list hasn't loaded yet, load it and defer checking for updates until
+ // later.
+ if (!m_chanListLoaded)
+ {
+ QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring "
+ "update check.";
+ m_checkUpdateWaiting = true;
+ updateChanList();
+ return;
+ }
+
+ if (m_updateChecking)
+ {
+ QLOG_DEBUG() << "Ignoring update check request. Already checking for updates.";
+ return;
+ }
+
+ m_updateChecking = true;
+
+ // Get the channel we're checking.
+ QString updateChannel = MMC->settings()->get("UpdateChannel").toString();
+
+ // Find the desired channel within the channel list and get its repo URL. If if cannot be
+ // found, error.
+ m_repoUrl = "";
+ for (ChannelListEntry entry : m_channels)
+ {
+ if (entry.id == updateChannel)
+ m_repoUrl = entry.url;
+ }
+
+ // If we didn't find our channel, error.
+ if (m_repoUrl.isEmpty())
+ {
+ emit updateCheckFailed();
+ return;
+ }
+
+ QUrl indexUrl = QUrl(m_repoUrl).resolved(QUrl("index.json"));
+
+ auto job = new NetJob("GoUpdate Repository Index");
+ job->addNetAction(ByteArrayDownload::make(indexUrl));
+ connect(job, &NetJob::succeeded, [this, notifyNoUpdate]()
+ { updateCheckFinished(notifyNoUpdate); });
+ connect(job, SIGNAL(failed()), SLOT(updateCheckFailed()));
+ indexJob.reset(job);
+ job->start();
+}
+
+void UpdateChecker::updateCheckFinished(bool notifyNoUpdate)
+{
+ QLOG_DEBUG() << "Finished downloading repo index. Checking for new versions.";
+
+ QJsonParseError jsonError;
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl =
+ std::dynamic_pointer_cast<ByteArrayDownload>(indexJob->first());
+ data = dl->m_data;
+ indexJob.reset();
+ }
+
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject())
+ {
+ QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error"
+ << jsonError.errorString() << "at offset" << jsonError.offset;
+ return;
+ }
+
+ QJsonObject object = jsonDoc.object();
+
+ bool success = false;
+ int apiVersion = object.value("ApiVersion").toVariant().toInt(&success);
+ if (apiVersion != API_VERSION || !success)
+ {
+ QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using"
+ << API_VERSION << "server has" << apiVersion;
+ return;
+ }
+
+ QLOG_DEBUG() << "Processing repository version list.";
+ QJsonObject newestVersion;
+ QJsonArray versions = object.value("Versions").toArray();
+ for (QJsonValue versionVal : versions)
+ {
+ QJsonObject version = versionVal.toObject();
+ if (newestVersion.value("Id").toVariant().toInt() <
+ version.value("Id").toVariant().toInt())
+ {
+ newestVersion = version;
+ }
+ }
+
+ // We've got the version with the greatest ID number. Now compare it to our current build
+ // number and update if they're different.
+ int newBuildNumber = newestVersion.value("Id").toVariant().toInt();
+ if (newBuildNumber != MMC->version().build)
+ {
+ QLOG_DEBUG() << "Found newer version with ID" << newBuildNumber;
+ // Update!
+ emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(),
+ newBuildNumber);
+ }
+ else if (notifyNoUpdate)
+ {
+ emit noUpdateFound();
+ }
+
+ m_updateChecking = false;
+}
+
+void UpdateChecker::updateCheckFailed()
+{
+ // TODO: log errors better
+ QLOG_ERROR() << "Update check failed for reasons unknown.";
+}
+
+void UpdateChecker::updateChanList()
+{
+ QLOG_DEBUG() << "Loading the channel list.";
+
+ if (m_channelListUrl.isEmpty())
+ {
+ QLOG_ERROR() << "Failed to update channel list. No channel list URL set."
+ << "If you'd like to use MultiMC's update system, please pass the channel "
+ "list URL to CMake at compile time.";
+ return;
+ }
+
+ m_chanListLoading = true;
+ NetJob *job = new NetJob("Update System Channel List");
+ job->addNetAction(ByteArrayDownload::make(QUrl(m_channelListUrl)));
+ QObject::connect(job, &NetJob::succeeded, this, &UpdateChecker::chanListDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed);
+ chanListJob.reset(job);
+ job->start();
+}
+
+void UpdateChecker::chanListDownloadFinished()
+{
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl =
+ std::dynamic_pointer_cast<ByteArrayDownload>(chanListJob->first());
+ data = dl->m_data;
+ chanListJob.reset();
+ }
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ // TODO: Report errors to the user.
+ QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at"
+ << jsonError.offset;
+ return;
+ }
+
+ QJsonObject object = jsonDoc.object();
+
+ bool success = false;
+ int formatVersion = object.value("format_version").toVariant().toInt(&success);
+ if (formatVersion != CHANLIST_FORMAT || !success)
+ {
+ QLOG_ERROR()
+ << "Failed to check for updates. Channel list format version mismatch. We're using"
+ << CHANLIST_FORMAT << "server has" << formatVersion;
+ return;
+ }
+
+ // Load channels into a temporary array.
+ QList<ChannelListEntry> loadedChannels;
+ QJsonArray channelArray = object.value("channels").toArray();
+ for (QJsonValue chanVal : channelArray)
+ {
+ QJsonObject channelObj = chanVal.toObject();
+ ChannelListEntry entry{channelObj.value("id").toVariant().toString(),
+ channelObj.value("name").toVariant().toString(),
+ channelObj.value("description").toVariant().toString(),
+ channelObj.value("url").toVariant().toString()};
+ if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty())
+ {
+ QLOG_ERROR() << "Channel list entry with empty ID, name, or URL. Skipping.";
+ continue;
+ }
+ loadedChannels.append(entry);
+ }
+
+ // Swap the channel list we just loaded into the object's channel list.
+ m_channels.swap(loadedChannels);
+
+ m_chanListLoading = false;
+ m_chanListLoaded = true;
+ QLOG_INFO() << "Successfully loaded UpdateChecker channel list.";
+
+ // If we're waiting to check for updates, do that now.
+ if (m_checkUpdateWaiting)
+ checkForUpdate(false);
+
+ emit channelListLoaded();
+}
+
+void UpdateChecker::chanListDownloadFailed()
+{
+ m_chanListLoading = false;
+ QLOG_ERROR() << "Failed to download channel list.";
+ emit channelListLoaded();
+}
+
diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h
new file mode 100644
index 00000000..3b0ee28d
--- /dev/null
+++ b/logic/updater/UpdateChecker.h
@@ -0,0 +1,111 @@
+/* Copyright 2013 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 "logic/net/NetJob.h"
+
+#include <QUrl>
+
+class UpdateChecker : public QObject
+{
+ Q_OBJECT
+
+public:
+ UpdateChecker();
+ void checkForUpdate(bool notifyNoUpdate);
+
+ void setChannelListUrl(const QString &url) { m_channelListUrl = url; }
+
+ /*!
+ * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
+ * If this isn't called before checkForUpdate(), it will automatically be called.
+ */
+ void updateChanList();
+
+ /*!
+ * An entry in the channel list.
+ */
+ struct ChannelListEntry
+ {
+ QString id;
+ QString name;
+ QString description;
+ QString url;
+ };
+
+ /*!
+ * Returns a the current channel list.
+ * If the channel list hasn't been loaded, this list will be empty.
+ */
+ QList<ChannelListEntry> getChannelList() const;
+
+ /*!
+ * Returns false if the channel list is empty.
+ */
+ bool hasChannels() const;
+
+signals:
+ //! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version.
+ void updateAvailable(QString repoUrl, QString versionName, int versionId);
+
+ //! Signal emitted when the channel list finishes loading or fails to load.
+ void channelListLoaded();
+
+ void noUpdateFound();
+
+private slots:
+ void updateCheckFinished(bool notifyNoUpdate);
+ void updateCheckFailed();
+
+ void chanListDownloadFinished();
+ void chanListDownloadFailed();
+
+private:
+ friend class UpdateCheckerTest;
+
+ NetJobPtr indexJob;
+ NetJobPtr chanListJob;
+
+ QString m_repoUrl;
+
+ QString m_channelListUrl;
+
+ QList<ChannelListEntry> m_channels;
+
+ /*!
+ * True while the system is checking for updates.
+ * If checkForUpdate is called while this is true, it will be ignored.
+ */
+ bool m_updateChecking;
+
+ /*!
+ * True if the channel list has loaded.
+ * If this is false, trying to check for updates will call updateChanList first.
+ */
+ bool m_chanListLoaded;
+
+ /*!
+ * Set to true while the channel list is currently loading.
+ */
+ bool m_chanListLoading;
+
+ /*!
+ * Set to true when checkForUpdate is called while the channel list isn't loaded.
+ * When the channel list finishes loading, if this is true, the update checker will check for updates.
+ */
+ bool m_checkUpdateWaiting;
+};
+
diff --git a/main.cpp b/main.cpp
index df14e3bd..181d7299 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,98 +1,35 @@
-#include "main.h"
+#include "MultiMC.h"
+#include "gui/MainWindow.h"
-#include <QApplication>
-#include <QStandardItemModel>
-#include <QPainter>
-#include <QTime>
-
-#include "GroupView.h"
-#include "GroupedProxyModel.h"
-#include "InstanceDelegate.h"
-
-Progresser *progresser;
-
-QPixmap icon(const Qt::GlobalColor color)
-{
- QPixmap p = QPixmap(32, 32);
- p.fill(QColor(color));
- return p;
-}
-QPixmap icon(const int number)
-{
- QPixmap p = icon(Qt::white);
- QPainter painter(&p);
- QFont font = painter.font();
- font.setBold(true);
- font.setPixelSize(28);
- painter.setFont(font);
- painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter,
- QString::number(number));
- painter.end();
- return p;
-}
-QStandardItem *createItem(const Qt::GlobalColor color, const QString &text,
- const QString &category)
-{
- QStandardItem *item = new QStandardItem;
- item->setText(text);
- item->setData(icon(color), Qt::DecorationRole);
- item->setData(category, GroupViewRoles::GroupRole);
- item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
- // progresser->addTrackedIndex(item);
- return item;
-}
-QStandardItem *createItem(const int index, const QString &category)
+int main_gui(MultiMC &app)
{
- QStandardItem *item = new QStandardItem;
- item->setText(QString("Item #%1").arg(index));
- item->setData(icon(index), Qt::DecorationRole);
- item->setData(category, GroupViewRoles::GroupRole);
- item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
- // progresser->addTrackedIndex(item);
- return item;
+ // show main window
+ QIcon::setThemeName("multimc");
+ MainWindow mainWin;
+ mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
+ mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
+ mainWin.show();
+ mainWin.checkMigrateLegacyAssets();
+ mainWin.checkSetDefaultJava();
+ return app.exec();
}
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
+ // initialize Qt
+ MultiMC app(argc, argv);
- qsrand(QTime::currentTime().msec());
+ Q_INIT_RESOURCE(instances);
+ Q_INIT_RESOURCE(multimc);
+ Q_INIT_RESOURCE(backgrounds);
- progresser = new Progresser();
-
- QStandardItemModel model;
- model.setRowCount(10);
- model.setColumnCount(1);
-
- model.setItem(
- 0, createItem(Qt::red,
- "Red is a color. Some more text. I'm out of ideas. 42. What's your name?",
- "Colorful"));
- model.setItem(1, createItem(Qt::blue, "Blue", "Colorful"));
- model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful"));
-
- model.setItem(3, createItem(Qt::black, "Black", "Not Colorful"));
- model.setItem(4, createItem(Qt::darkGray, "Dark Gray", "Not Colorful"));
- model.setItem(5, createItem(Qt::gray, "Gray", "Not Colorful"));
- model.setItem(6, createItem(Qt::lightGray, "Light Gray", "Not Colorful"));
- model.setItem(7, createItem(Qt::white, "White", "Not Colorful"));
-
- model.setItem(8, createItem(Qt::darkGreen, "Dark Green", ""));
- model.setItem(9, progresser->addTrackedIndex(createItem(Qt::green, "Green", "")));
-
- for (int i = 0; i < 20; ++i)
+ switch (app.status())
{
- model.setItem(i + 10, createItem(i + 1, "Items 1-20"));
+ case MultiMC::Initialized:
+ return main_gui(app);
+ case MultiMC::Failed:
+ return 1;
+ case MultiMC::Succeeded:
+ return 0;
}
-
- GroupedProxyModel pModel;
- pModel.setSourceModel(&model);
-
- GroupView w;
- w.setItemDelegate(new ListViewDelegate);
- w.setModel(&pModel);
- w.resize(640, 480);
- w.show();
-
- return a.exec();
}
diff --git a/mmc_updater/CMakeLists.txt b/mmc_updater/CMakeLists.txt
new file mode 100644
index 00000000..971ac153
--- /dev/null
+++ b/mmc_updater/CMakeLists.txt
@@ -0,0 +1,52 @@
+project(updater)
+
+cmake_minimum_required(VERSION 2.6)
+enable_testing()
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
+
+include_directories(depends)
+
+if (WIN32)
+ include_directories(depends/win32cpp)
+
+ # static all the things. The updater must have no dependencies, or it will fail.
+ if (MINGW)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc -static")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++ -static")
+#set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static-libgcc -s")
+#set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static-libgcc -static-libstdc++ -s")
+ endif()
+
+ if(MSVC)
+ # - Link the updater binary statically with the Visual C++ runtime
+ # so that the executable can function standalone.
+ # - Enable PDB generation for release builds
+ set(CMAKE_CXX_FLAGS_DEBUG "/MT")
+ set(CMAKE_C_FLAGS_DEBUG "/MT")
+
+ set(CMAKE_CXX_FLAGS_RELEASE "/MT /Zi /O2 /Ob2 /D NDEBUG")
+ set(CMAKE_C_FLAGS_RELEASE "/MT /Zi /O2 /Ob2 /D NDEBUG")
+ remove_definitions(-DUNICODE -D_UNICODE)
+ endif()
+else()
+ # optimize for reduced code size
+ set(CMAKE_CXX_FLAGS_RELEASE "-Os")
+ set(CMAKE_C_FLAGS_RELEASE "-Os")
+endif()
+
+if (APPLE)
+ # Build the updater as a dual 32/64bit binary. If only one architecture
+ # is required, removing the other architecture will reduce the size
+ # of the updater binary
+ set(CMAKE_OSX_ARCHITECTURES i386;x86_64)
+
+ # Build the updater so that it works on OS X 10.5 and above.
+ set(MIN_OSX_VERSION 10.5)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_OSX_VERSION}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_OSX_VERSION}")
+endif()
+
+add_subdirectory(src)
+add_subdirectory(depends/AnyOption)
+add_subdirectory(depends/tinyxml)
+
diff --git a/mmc_updater/LICENSE b/mmc_updater/LICENSE
new file mode 100644
index 00000000..f71a89be
--- /dev/null
+++ b/mmc_updater/LICENSE
@@ -0,0 +1,19 @@
+
+This project is licensed under a BSD license.
+
+The Mendeley Desktop icon graphics and name are trademarks of Mendeley Ltd.
+and must be removed or replaced - see src/AppInfo.cpp and the files
+in src/resources.
+
+===
+
+Copyright (c) 2011, Mendeley Ltd.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/mmc_updater/README.md b/mmc_updater/README.md
new file mode 100644
index 00000000..fa3c41ae
--- /dev/null
+++ b/mmc_updater/README.md
@@ -0,0 +1,138 @@
+This tool is a component of a cross-platform auto-update system.
+It is responsible for performing the installation of an update after
+the necessary files have been downloaded to a temporary directory.
+
+It was originally written for use with Mendeley Desktop (see www.mendeley.com)
+
+The tool consists of a single small binary which performs update installation,
+an XML file format describing the contents of an update (an 'update script')
+and a tool to create update scripts from a directory containing an installed application.
+
+To perform an update, the application (or another separate tool) needs to download
+the updater binary, an update script and one or more compressed packages
+containing the files for the update to a temporary directory. It then needs
+to invoke the updater, specifying the location where the application is installed,
+the location of the compressed packages and the path to the update script.
+
+Once the updater has been started, it:
+
+ 1. Waits for the application to exit
+ 2. Acquires the necessary priviledges to install the updates, prompting
+ the user if necessary.
+ 3. Installs the updates, displaying progress to the user in a small dialog
+ 4. Performs cleanup and any additional actions required as part of the update
+ 5. Starts the new version of the main application.
+
+ In the event of a failure during the update, the installation is rolled back
+ to its previous state and a message is presented to the user.
+
+## Building the Updater
+
+ Create a new directory for the build and from that directory run:
+
+ cmake <path to source directory>
+ make
+
+ The updater binary will be built in the src/ directory.
+
+ You should also run the tests in src/tests to verify that the updater is
+ functioning correctly.
+
+## Preparing an Update
+
+ 1. Create a directory containing your application's files,
+ laid out in the same way and with the same permissions as they would be when installed.
+ 2. Create a config file specifying how the application's files should be
+ partitioned into packages - see tools/config-template.json
+ 3. Use the tools/create-packages.rb script to create a file_list.xml file
+ and a set of package files required for updates.
+ 4. Upload the file_list.xml file and packages to a server
+
+ After step 4 is done, you need to notify existing installs that an update
+ is available. The installed application then needs to download the
+ relevant packages, file_list.xml file and updater binary to a temporary
+ directory and invoke the updater.
+
+ See doc/update-hosting for more details on hosting and delivering the updates.
+
+## Invoking the Updater
+
+ Once the application has downloaded an update, it needs to invoke it. The syntax is:
+
+ updater --install-dir <install-dir> --package-dir <package-dir> --script <script file>
+
+ Where `<install-dir>` is the directory which the application is installed into,
+ `<package-dir>` is the directory containing the packages required for the update
+ and `<script>` is the `file_list.xml` file describing the update.
+
+ Once the updater has run, it will launch the file specified in the `file_list.xml` file
+ as being the main application binary.
+
+ See the updater test in `src/tests/test-update.rb` for an example
+ of how to invoke the updater.
+
+ You should design the process used to download and launch the updater so that new
+ versions of the updater itself can be delivered as part of the update if necessary.
+
+## Customizing the Updater
+
+ To customize the application name, organization and messages displayed by the updater:
+
+ 1. Edit the AppInfo class (in AppInfo.h, AppInfo.cpp) to set the name
+ of the application and associated organization.
+ 2. Replace the icons in src/resources
+ 3. Change the product name and organization in src/resources/updater.rc
+ 4. If you are building the updater on Windows and have a suitable Authenticode
+ certificate, use it to sign the Windows binary. This will make the application
+ show a less scary UAC prompt if administrator permissions are required
+ to complete the installation.
+
+## Updater Dependencies
+
+ The external dependencies of the updater binary are:
+
+ * The C/C++ runtime libraries (Linux, Mac),
+ * pthreads (Linux, Mac),
+ * zlib (Linux, Mac)
+ * native UI library (Win32 API on Windows, Cocoa on Mac, GTK on Linux if available)
+
+## Full and Delta Updates
+
+ The simplest auto-update implementation is for existing installs
+ to download a complete copy of the new version and install it. This is
+ appropriate if a full download and install will not take a long time for most users
+ (eg. if the application is small or they have a fast internet connection).
+
+ With this tool, a full-update involves putting all files in a build of
+ the application into a single package.
+
+ To reduce the download size, delta updates can be created which only include
+ the necessary files or components to update from the old to the new version.
+
+ The file_list.xml file format can be used to represent either a complete
+ install - in which every file that makes up the application is included,
+ or a delta update - in which case only new or updated files and packages
+ are included.
+
+ There are several ways in which this can be done:
+
+ * Pre-computed Delta Updates
+ For each release, create a full update plus delta updates from the
+ previous N releases. Users of recent releases will receive a small
+ delta update. Users of older releases will receive the full update.
+
+ * Server-computed Delta Updates
+ The server receives a request for an update from client version X and in response,
+ computes an update from version X to the current version Y, possibly
+ caching that information for future use. The client then receives the
+ delta file_list.xml file and downloads only the listed packages.
+
+ Applications such as Chrome and Firefox use a mixture of the above methods.
+
+ * Client-computed Delta Updates
+ The client downloads the file_list.xml file for the latest version and
+ computes a delta update file locally. It then downloads only the required
+ packages and invokes the updater, which installs only the changed or updated
+ files from those packages.
+
+ This is similar to Linux package management systems.
diff --git a/mmc_updater/cmake/modules/GenerateCppResourceFile.cmake b/mmc_updater/cmake/modules/GenerateCppResourceFile.cmake
new file mode 100644
index 00000000..59adfd58
--- /dev/null
+++ b/mmc_updater/cmake/modules/GenerateCppResourceFile.cmake
@@ -0,0 +1,23 @@
+
+# Convert a binary data file into a C++
+# source file for embedding into an application binary
+#
+# Currently only implemented for Unix. Requires the 'xxd'
+# tool to be installed.
+#
+# TARGET_NAME : The name of the target to generate
+#
+# INPUT_DIR : The directory containing the input binary data file
+#
+# INPUT_FILE : The name of the binary data file in ${INPUT_DIR} to be converted into a C++
+# source file. The name of the input file will be used as the basis for the
+# symbols in the generated C++ file referring to the data buffer and its length.
+#
+# CPP_FILE : The path of the C++ source file to be generated.
+# See the documentation for xxd for information on
+# the structure of the generated source file.
+#
+function (generate_cpp_resource_file TARGET_NAME INPUT_FILE CPP_FILE)
+ add_custom_command(OUTPUT ${CPP_FILE} COMMAND cd `dirname ${INPUT_FILE}` && xxd -i `basename ${INPUT_FILE}` ${CPP_FILE} DEPENDS ${INPUT_FILE})
+ add_custom_target(${TARGET_NAME} ALL DEPENDS ${CPP_FILE})
+endfunction()
diff --git a/mmc_updater/depends/AnyOption/CMakeLists.txt b/mmc_updater/depends/AnyOption/CMakeLists.txt
new file mode 100644
index 00000000..fdfd6076
--- /dev/null
+++ b/mmc_updater/depends/AnyOption/CMakeLists.txt
@@ -0,0 +1,9 @@
+project(AnyOption)
+
+cmake_minimum_required(VERSION 2.6)
+
+add_library(anyoption
+ anyoption.cpp
+ anyoption.h
+)
+
diff --git a/mmc_updater/depends/AnyOption/README b/mmc_updater/depends/AnyOption/README
new file mode 100644
index 00000000..d91d6851
--- /dev/null
+++ b/mmc_updater/depends/AnyOption/README
@@ -0,0 +1,16 @@
+
+http://www.hackorama.com/anyoption/
+
+AnyOption is a C++ class for easy parsing of complex commandline options. It also parses options from a rsourcefile in option value pair format.
+
+AnyOption implements the traditional POSIX style character options ( -n ) as well as the newer GNU style long options ( --name ). Or you can use a simpler long option version ( -name ) by asking to ignore the POSIX style options.
+
+AnyOption supports the traditional UNIX resourcefile syntax of, any line starting with "#" is a comment and the value pairs use ":" as a delimiter.
+
+An option which expects a value is considered as an option value pair, while options without a value are considered flags.
+
+Please read the header file for the documented public interface, and demo.cpp for an example of how easy it is to use AnyOption.
+
+August 2004, added bug-fixes, and updates send by Michael Peters of Sandia Lab.
+September 2006, fix from Boyan Asenov for a bug in mixing up option type indexes.
+July 2011, fix from Min KJ and Costantino G for string allocation.
diff --git a/mmc_updater/depends/AnyOption/anyoption.cpp b/mmc_updater/depends/AnyOption/anyoption.cpp
new file mode 100644
index 00000000..6792fe3e
--- /dev/null
+++ b/mmc_updater/depends/AnyOption/anyoption.cpp
@@ -0,0 +1,1176 @@
+/*
+ * AnyOption 1.3
+ *
+ * kishan at hackorama dot com www.hackorama.com JULY 2001
+ *
+ * + Acts as a common facade class for reading
+ * commandline options as well as options from
+ * an optionfile with delimited type value pairs
+ *
+ * + Handles the POSIX style single character options ( -w )
+ * as well as the newer GNU long options ( --width )
+ *
+ * + The option file assumes the traditional format of
+ * first character based comment lines and type value
+ * pairs with a delimiter , and flags which are not pairs
+ *
+ * # this is a coment
+ * # next line is an option value pair
+ * width : 100
+ * # next line is a flag
+ * noimages
+ *
+ * + Supports printing out Help and Usage
+ *
+ * + Why not just use getopt() ?
+ *
+ * getopt() Its a POSIX standard not part of ANSI-C.
+ * So it may not be available on platforms like Windows.
+ *
+ * + Why it is so long ?
+ *
+ * The actual code which does command line parsing
+ * and option file parsing are done in few methods.
+ * Most of the extra code are for providing a flexible
+ * common public interface to both a resourcefile and
+ * and command line supporting POSIX style and
+ * GNU long option as well as mixing of both.
+ *
+ * + Please see "anyoption.h" for public method descriptions
+ *
+ */
+
+/* Updated Auguest 2004
+ * Fix from Michael D Peters (mpeters at sandia.gov)
+ * to remove static local variables, allowing multiple instantiations
+ * of the reader (for using multiple configuration files). There is
+ * an error in the destructor when using multiple instances, so you
+ * cannot delete your objects (it will crash), but not calling the
+ * destructor only introduces a small memory leak, so I
+ * have not bothered tracking it down.
+ *
+ * Also updated to use modern C++ style headers, rather than
+ * depricated iostream.h (it was causing my compiler problems)
+*/
+
+/*
+ * Updated September 2006
+ * Fix from Boyan Asenov for a bug in mixing up option indexes
+ * leading to exception when mixing different options types
+ */
+
+#include "anyoption.h"
+
+#include <string.h>
+
+AnyOption::AnyOption()
+{
+ init();
+}
+
+AnyOption::AnyOption(int maxopt)
+{
+ init( maxopt , maxopt );
+}
+
+AnyOption::AnyOption(int maxopt, int maxcharopt)
+{
+ init( maxopt , maxcharopt );
+}
+
+AnyOption::~AnyOption()
+{
+ if( mem_allocated )
+ cleanup();
+}
+
+void
+AnyOption::init()
+{
+ init( DEFAULT_MAXOPTS , DEFAULT_MAXOPTS );
+}
+
+void
+AnyOption::init(int maxopt, int maxcharopt )
+{
+
+ max_options = maxopt;
+ max_char_options = maxcharopt;
+ max_usage_lines = DEFAULT_MAXUSAGE;
+ usage_lines = 0 ;
+ argc = 0;
+ argv = NULL;
+ posix_style = true;
+ verbose = false;
+ filename = NULL;
+ appname = NULL;
+ option_counter = 0;
+ optchar_counter = 0;
+ new_argv = NULL;
+ new_argc = 0 ;
+ max_legal_args = 0 ;
+ command_set = false;
+ file_set = false;
+ values = NULL;
+ g_value_counter = 0;
+ mem_allocated = false;
+ command_set = false;
+ file_set = false;
+ opt_prefix_char = '-';
+ file_delimiter_char = ':';
+ file_comment_char = '#';
+ equalsign = '=';
+ comment = '#' ;
+ delimiter = ':' ;
+ endofline = '\n';
+ whitespace = ' ' ;
+ nullterminate = '\0';
+ set = false;
+ once = true;
+ hasoptions = false;
+ autousage = false;
+
+ strcpy( long_opt_prefix , "--" );
+
+ if( alloc() == false ){
+ cout << endl << "OPTIONS ERROR : Failed allocating memory" ;
+ cout << endl ;
+ cout << "Exiting." << endl ;
+ exit (0);
+ }
+}
+
+bool
+AnyOption::alloc()
+{
+ int i = 0 ;
+ int size = 0 ;
+
+ if( mem_allocated )
+ return true;
+
+ size = (max_options+1) * sizeof(const char*);
+ options = (const char**)malloc( size );
+ optiontype = (int*) malloc( (max_options+1)*sizeof(int) );
+ optionindex = (int*) malloc( (max_options+1)*sizeof(int) );
+ if( options == NULL || optiontype == NULL || optionindex == NULL )
+ return false;
+ else
+ mem_allocated = true;
+ for( i = 0 ; i < max_options ; i++ ){
+ options[i] = NULL;
+ optiontype[i] = 0 ;
+ optionindex[i] = -1 ;
+ }
+ optionchars = (char*) malloc( (max_char_options+1)*sizeof(char) );
+ optchartype = (int*) malloc( (max_char_options+1)*sizeof(int) );
+ optcharindex = (int*) malloc( (max_char_options+1)*sizeof(int) );
+ if( optionchars == NULL ||
+ optchartype == NULL ||
+ optcharindex == NULL )
+ {
+ mem_allocated = false;
+ return false;
+ }
+ for( i = 0 ; i < max_char_options ; i++ ){
+ optionchars[i] = '0';
+ optchartype[i] = 0 ;
+ optcharindex[i] = -1 ;
+ }
+
+ size = (max_usage_lines+1) * sizeof(const char*) ;
+ usage = (const char**) malloc( size );
+
+ if( usage == NULL ){
+ mem_allocated = false;
+ return false;
+ }
+ for( i = 0 ; i < max_usage_lines ; i++ )
+ usage[i] = NULL;
+
+ return true;
+}
+
+bool
+AnyOption::doubleOptStorage()
+{
+ options = (const char**)realloc( options,
+ ((2*max_options)+1) * sizeof( const char*) );
+ optiontype = (int*) realloc( optiontype ,
+ ((2 * max_options)+1)* sizeof(int) );
+ optionindex = (int*) realloc( optionindex,
+ ((2 * max_options)+1) * sizeof(int) );
+ if( options == NULL || optiontype == NULL || optionindex == NULL )
+ return false;
+ /* init new storage */
+ for( int i = max_options ; i < 2*max_options ; i++ ){
+ options[i] = NULL;
+ optiontype[i] = 0 ;
+ optionindex[i] = -1 ;
+ }
+ max_options = 2 * max_options ;
+ return true;
+}
+
+bool
+AnyOption::doubleCharStorage()
+{
+ optionchars = (char*) realloc( optionchars,
+ ((2*max_char_options)+1)*sizeof(char) );
+ optchartype = (int*) realloc( optchartype,
+ ((2*max_char_options)+1)*sizeof(int) );
+ optcharindex = (int*) realloc( optcharindex,
+ ((2*max_char_options)+1)*sizeof(int) );
+ if( optionchars == NULL ||
+ optchartype == NULL ||
+ optcharindex == NULL )
+ return false;
+ /* init new storage */
+ for( int i = max_char_options ; i < 2*max_char_options ; i++ ){
+ optionchars[i] = '0';
+ optchartype[i] = 0 ;
+ optcharindex[i] = -1 ;
+ }
+ max_char_options = 2 * max_char_options;
+ return true;
+}
+
+bool
+AnyOption::doubleUsageStorage()
+{
+ usage = (const char**)realloc( usage,
+ ((2*max_usage_lines)+1) * sizeof( const char*) );
+ if ( usage == NULL )
+ return false;
+ for( int i = max_usage_lines ; i < 2*max_usage_lines ; i++ )
+ usage[i] = NULL;
+ max_usage_lines = 2 * max_usage_lines ;
+ return true;
+
+}
+
+
+void
+AnyOption::cleanup()
+{
+ free (options);
+ free (optiontype);
+ free (optionindex);
+ free (optionchars);
+ free (optchartype);
+ free (optcharindex);
+ free (usage);
+ if( values != NULL )
+ free (values);
+ if( new_argv != NULL )
+ free (new_argv);
+}
+
+void
+AnyOption::setCommandPrefixChar( char _prefix )
+{
+ opt_prefix_char = _prefix;
+}
+
+void
+AnyOption::setCommandLongPrefix( char *_prefix )
+{
+ if( strlen( _prefix ) > MAX_LONG_PREFIX_LENGTH ){
+ *( _prefix + MAX_LONG_PREFIX_LENGTH ) = '\0';
+ }
+
+ strcpy (long_opt_prefix, _prefix);
+}
+
+void
+AnyOption::setFileCommentChar( char _comment )
+{
+ file_delimiter_char = _comment;
+}
+
+
+void
+AnyOption::setFileDelimiterChar( char _delimiter )
+{
+ file_comment_char = _delimiter ;
+}
+
+bool
+AnyOption::CommandSet()
+{
+ return( command_set );
+}
+
+bool
+AnyOption::FileSet()
+{
+ return( file_set );
+}
+
+void
+AnyOption::noPOSIX()
+{
+ posix_style = false;
+}
+
+bool
+AnyOption::POSIX()
+{
+ return posix_style;
+}
+
+
+void
+AnyOption::setVerbose()
+{
+ verbose = true ;
+}
+
+void
+AnyOption::printVerbose()
+{
+ if( verbose )
+ cout << endl ;
+}
+void
+AnyOption::printVerbose( const char *msg )
+{
+ if( verbose )
+ cout << msg ;
+}
+
+void
+AnyOption::printVerbose( char *msg )
+{
+ if( verbose )
+ cout << msg ;
+}
+
+void
+AnyOption::printVerbose( char ch )
+{
+ if( verbose )
+ cout << ch ;
+}
+
+bool
+AnyOption::hasOptions()
+{
+ return hasoptions;
+}
+
+void
+AnyOption::autoUsagePrint(bool _autousage)
+{
+ autousage = _autousage;
+}
+
+void
+AnyOption::useCommandArgs( int _argc, char **_argv )
+{
+ argc = _argc;
+ argv = _argv;
+ command_set = true;
+ appname = argv[0];
+ if(argc > 1) hasoptions = true;
+}
+
+void
+AnyOption::useFiileName( const char *_filename )
+{
+ filename = _filename;
+ file_set = true;
+}
+
+/*
+ * set methods for options
+ */
+
+void
+AnyOption::setCommandOption( const char *opt )
+{
+ addOption( opt , COMMAND_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setCommandOption( char opt )
+{
+ addOption( opt , COMMAND_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setCommandOption( const char *opt , char optchar )
+{
+ addOption( opt , COMMAND_OPT );
+ addOption( optchar , COMMAND_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setCommandFlag( const char *opt )
+{
+ addOption( opt , COMMAND_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setCommandFlag( char opt )
+{
+ addOption( opt , COMMAND_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setCommandFlag( const char *opt , char optchar )
+{
+ addOption( opt , COMMAND_FLAG );
+ addOption( optchar , COMMAND_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFileOption( const char *opt )
+{
+ addOption( opt , FILE_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFileOption( char opt )
+{
+ addOption( opt , FILE_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFileOption( const char *opt , char optchar )
+{
+ addOption( opt , FILE_OPT );
+ addOption( optchar, FILE_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFileFlag( const char *opt )
+{
+ addOption( opt , FILE_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFileFlag( char opt )
+{
+ addOption( opt , FILE_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFileFlag( const char *opt , char optchar )
+{
+ addOption( opt , FILE_FLAG );
+ addOption( optchar , FILE_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setOption( const char *opt )
+{
+ addOption( opt , COMMON_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setOption( char opt )
+{
+ addOption( opt , COMMON_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setOption( const char *opt , char optchar )
+{
+ addOption( opt , COMMON_OPT );
+ addOption( optchar , COMMON_OPT );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFlag( const char *opt )
+{
+ addOption( opt , COMMON_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFlag( const char opt )
+{
+ addOption( opt , COMMON_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::setFlag( const char *opt , char optchar )
+{
+ addOption( opt , COMMON_FLAG );
+ addOption( optchar , COMMON_FLAG );
+ g_value_counter++;
+}
+
+void
+AnyOption::addOption( const char *opt, int type )
+{
+ if( option_counter >= max_options ){
+ if( doubleOptStorage() == false ){
+ addOptionError( opt );
+ return;
+ }
+ }
+ options[ option_counter ] = opt ;
+ optiontype[ option_counter ] = type ;
+ optionindex[ option_counter ] = g_value_counter;
+ option_counter++;
+}
+
+void
+AnyOption::addOption( char opt, int type )
+{
+ if( !POSIX() ){
+ printVerbose("Ignoring the option character \"");
+ printVerbose( opt );
+ printVerbose( "\" ( POSIX options are turned off )" );
+ printVerbose();
+ return;
+ }
+
+
+ if( optchar_counter >= max_char_options ){
+ if( doubleCharStorage() == false ){
+ addOptionError( opt );
+ return;
+ }
+ }
+ optionchars[ optchar_counter ] = opt ;
+ optchartype[ optchar_counter ] = type ;
+ optcharindex[ optchar_counter ] = g_value_counter;
+ optchar_counter++;
+}
+
+void
+AnyOption::addOptionError( const char *opt )
+{
+ cout << endl ;
+ cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ;
+ cout << "While adding the option : \""<< opt << "\"" << endl;
+ cout << "Exiting." << endl ;
+ cout << endl ;
+ exit(0);
+}
+
+void
+AnyOption::addOptionError( char opt )
+{
+ cout << endl ;
+ cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ;
+ cout << "While adding the option: \""<< opt << "\"" << endl;
+ cout << "Exiting." << endl ;
+ cout << endl ;
+ exit(0);
+}
+
+void
+AnyOption::processOptions()
+{
+ if( ! valueStoreOK() )
+ return;
+}
+
+void
+AnyOption::processCommandArgs(int max_args)
+{
+ max_legal_args = max_args;
+ processCommandArgs();
+}
+
+void
+AnyOption::processCommandArgs( int _argc, char **_argv, int max_args )
+{
+ max_legal_args = max_args;
+ processCommandArgs( _argc, _argv );
+}
+
+void
+AnyOption::processCommandArgs( int _argc, char **_argv )
+{
+ useCommandArgs( _argc, _argv );
+ processCommandArgs();
+}
+
+void
+AnyOption::processCommandArgs()
+{
+ if( ! ( valueStoreOK() && CommandSet() ) )
+ return;
+
+ if( max_legal_args == 0 )
+ max_legal_args = argc;
+ new_argv = (int*) malloc( (max_legal_args+1) * sizeof(int) );
+ for( int i = 1 ; i < argc ; i++ ){/* ignore first argv */
+ if( argv[i][0] == long_opt_prefix[0] &&
+ argv[i][1] == long_opt_prefix[1] ) { /* long GNU option */
+ int match_at = parseGNU( argv[i]+2 ); /* skip -- */
+ if( match_at >= 0 && i < argc-1 ) /* found match */
+ setValue( options[match_at] , argv[++i] );
+ }else if( argv[i][0] == opt_prefix_char ) { /* POSIX char */
+ if( POSIX() ){
+ char ch = parsePOSIX( argv[i]+1 );/* skip - */
+ if( ch != '0' && i < argc-1 ) /* matching char */
+ setValue( ch , argv[++i] );
+ } else { /* treat it as GNU option with a - */
+ int match_at = parseGNU( argv[i]+1 ); /* skip - */
+ if( match_at >= 0 && i < argc-1 ) /* found match */
+ setValue( options[match_at] , argv[++i] );
+ }
+ }else { /* not option but an argument keep index */
+ if( new_argc < max_legal_args ){
+ new_argv[ new_argc ] = i ;
+ new_argc++;
+ }else{ /* ignore extra arguments */
+ printVerbose( "Ignoring extra argument: " );
+ printVerbose( argv[i] );
+ printVerbose( );
+ printAutoUsage();
+ }
+ printVerbose( "Unknown command argument option : " );
+ printVerbose( argv[i] );
+ printVerbose( );
+ printAutoUsage();
+ }
+ }
+}
+
+char
+AnyOption::parsePOSIX( char* arg )
+{
+
+ for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){
+ char ch = arg[i] ;
+ if( matchChar(ch) ) { /* keep matching flags till an option */
+ /*if last char argv[++i] is the value */
+ if( i == strlen(arg)-1 ){
+ return ch;
+ }else{/* else the rest of arg is the value */
+ i++; /* skip any '=' and ' ' */
+ while( arg[i] == whitespace
+ || arg[i] == equalsign )
+ i++;
+ setValue( ch , arg+i );
+ return '0';
+ }
+ }
+ }
+ printVerbose( "Unknown command argument option : " );
+ printVerbose( arg );
+ printVerbose( );
+ printAutoUsage();
+ return '0';
+}
+
+int
+AnyOption::parseGNU( char *arg )
+{
+ int split_at = 0;
+ /* if has a '=' sign get value */
+ for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){
+ if(arg[i] == equalsign ){
+ split_at = i ; /* store index */
+ i = strlen(arg); /* get out of loop */
+ }
+ }
+ if( split_at > 0 ){ /* it is an option value pair */
+ char* tmp = (char*) malloc( (split_at+1)*sizeof(char) );
+ for( int i = 0 ; i < split_at ; i++ )
+ tmp[i] = arg[i];
+ tmp[split_at] = '\0';
+
+ if ( matchOpt( tmp ) >= 0 ){
+ setValue( options[matchOpt(tmp)] , arg+split_at+1 );
+ free (tmp);
+ }else{
+ printVerbose( "Unknown command argument option : " );
+ printVerbose( arg );
+ printVerbose( );
+ printAutoUsage();
+ free (tmp);
+ return -1;
+ }
+ }else{ /* regular options with no '=' sign */
+ return matchOpt(arg);
+ }
+ return -1;
+}
+
+
+int
+AnyOption::matchOpt( char *opt )
+{
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], opt ) == 0 ){
+ if( optiontype[i] == COMMON_OPT ||
+ optiontype[i] == COMMAND_OPT )
+ { /* found option return index */
+ return i;
+ }else if( optiontype[i] == COMMON_FLAG ||
+ optiontype[i] == COMMAND_FLAG )
+ { /* found flag, set it */
+ setFlagOn( opt );
+ return -1;
+ }
+ }
+ }
+ printVerbose( "Unknown command argument option : " );
+ printVerbose( opt ) ;
+ printVerbose( );
+ printAutoUsage();
+ return -1;
+}
+bool
+AnyOption::matchChar( char c )
+{
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == c ) { /* found match */
+ if(optchartype[i] == COMMON_OPT ||
+ optchartype[i] == COMMAND_OPT )
+ { /* an option store and stop scanning */
+ return true;
+ }else if( optchartype[i] == COMMON_FLAG ||
+ optchartype[i] == COMMAND_FLAG ) { /* a flag store and keep scanning */
+ setFlagOn( c );
+ return false;
+ }
+ }
+ }
+ printVerbose( "Unknown command argument option : " );
+ printVerbose( c ) ;
+ printVerbose( );
+ printAutoUsage();
+ return false;
+}
+
+bool
+AnyOption::valueStoreOK( )
+{
+ int size= 0;
+ if( !set ){
+ if( g_value_counter > 0 ){
+ size = g_value_counter * sizeof(char*);
+ values = (char**)malloc( size );
+ for( int i = 0 ; i < g_value_counter ; i++)
+ values[i] = NULL;
+ set = true;
+ }
+ }
+ return set;
+}
+
+/*
+ * public get methods
+ */
+char*
+AnyOption::getValue( const char *option )
+{
+ if( !valueStoreOK() )
+ return NULL;
+
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], option ) == 0 )
+ return values[ optionindex[i] ];
+ }
+ return NULL;
+}
+
+bool
+AnyOption::getFlag( const char *option )
+{
+ if( !valueStoreOK() )
+ return false;
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], option ) == 0 )
+ return findFlag( values[ optionindex[i] ] );
+ }
+ return false;
+}
+
+char*
+AnyOption::getValue( char option )
+{
+ if( !valueStoreOK() )
+ return NULL;
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == option )
+ return values[ optcharindex[i] ];
+ }
+ return NULL;
+}
+
+bool
+AnyOption::getFlag( char option )
+{
+ if( !valueStoreOK() )
+ return false;
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == option )
+ return findFlag( values[ optcharindex[i] ] ) ;
+ }
+ return false;
+}
+
+bool
+AnyOption::findFlag( char* val )
+{
+ if( val == NULL )
+ return false;
+
+ if( strcmp( TRUE_FLAG , val ) == 0 )
+ return true;
+
+ return false;
+}
+
+/*
+ * private set methods
+ */
+bool
+AnyOption::setValue( const char *option , char *value )
+{
+ if( !valueStoreOK() )
+ return false;
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], option ) == 0 ){
+ values[ optionindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char));
+ strcpy( values[ optionindex[i] ], value );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+AnyOption::setFlagOn( const char *option )
+{
+ if( !valueStoreOK() )
+ return false;
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], option ) == 0 ){
+ values[ optionindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char));
+ strcpy( values[ optionindex[i] ] , TRUE_FLAG );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+AnyOption::setValue( char option , char *value )
+{
+ if( !valueStoreOK() )
+ return false;
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == option ){
+ values[ optcharindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char));
+ strcpy( values[ optcharindex[i] ], value );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+AnyOption::setFlagOn( char option )
+{
+ if( !valueStoreOK() )
+ return false;
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == option ){
+ values[ optcharindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char));
+ strcpy( values[ optcharindex[i] ] , TRUE_FLAG );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+int
+AnyOption::getArgc( )
+{
+ return new_argc;
+}
+
+char*
+AnyOption::getArgv( int index )
+{
+ if( index < new_argc ){
+ return ( argv[ new_argv[ index ] ] );
+ }
+ return NULL;
+}
+
+/* dotfile sub routines */
+
+bool
+AnyOption::processFile()
+{
+ if( ! (valueStoreOK() && FileSet()) )
+ return false;
+ return ( consumeFile(readFile()) );
+}
+
+bool
+AnyOption::processFile( const char *filename )
+{
+ useFiileName(filename );
+ return ( processFile() );
+}
+
+char*
+AnyOption::readFile()
+{
+ return ( readFile(filename) );
+}
+
+/*
+ * read the file contents to a character buffer
+ */
+
+char*
+AnyOption::readFile( const char* fname )
+{
+ int length;
+ char *buffer;
+ ifstream is;
+ is.open ( fname , ifstream::in );
+ if( ! is.good() ){
+ is.close();
+ return NULL;
+ }
+ is.seekg (0, ios::end);
+ length = is.tellg();
+ is.seekg (0, ios::beg);
+ buffer = (char*) malloc(length*sizeof(char));
+ is.read (buffer,length);
+ is.close();
+ return buffer;
+}
+
+/*
+ * scans a char* buffer for lines that does not
+ * start with the specified comment character.
+ */
+bool
+AnyOption::consumeFile( char *buffer )
+{
+
+ if( buffer == NULL )
+ return false;
+
+ char *cursor = buffer;/* preserve the ptr */
+ char *pline = NULL ;
+ int linelength = 0;
+ bool newline = true;
+ for( unsigned int i = 0 ; i < strlen( buffer ) ; i++ ){
+ if( *cursor == endofline ) { /* end of line */
+ if( pline != NULL ) /* valid line */
+ processLine( pline, linelength );
+ pline = NULL;
+ newline = true;
+ }else if( newline ){ /* start of line */
+ newline = false;
+ if( (*cursor != comment ) ){ /* not a comment */
+ pline = cursor ;
+ linelength = 0 ;
+ }
+ }
+ cursor++; /* keep moving */
+ linelength++;
+ }
+ free (buffer);
+ return true;
+}
+
+
+/*
+ * find a valid type value pair separated by a delimiter
+ * character and pass it to valuePairs()
+ * any line which is not valid will be considered a value
+ * and will get passed on to justValue()
+ *
+ * assuming delimiter is ':' the behaviour will be,
+ *
+ * width:10 - valid pair valuePairs( width, 10 );
+ * width : 10 - valid pair valuepairs( width, 10 );
+ *
+ * :::: - not valid
+ * width - not valid
+ * :10 - not valid
+ * width: - not valid
+ * :: - not valid
+ * : - not valid
+ *
+ */
+
+void
+AnyOption::processLine( char *theline, int length )
+{
+ bool found = false;
+ char *pline = (char*) malloc( (length+1)*sizeof(char) );
+ for( int i = 0 ; i < length ; i ++ )
+ pline[i]= *(theline++);
+ pline[length] = nullterminate;
+ char *cursor = pline ; /* preserve the ptr */
+ if( *cursor == delimiter || *(cursor+length-1) == delimiter ){
+ justValue( pline );/* line with start/end delimiter */
+ }else{
+ for( int i = 1 ; i < length-1 && !found ; i++){/* delimiter */
+ if( *cursor == delimiter ){
+ *(cursor-1) = nullterminate; /* two strings */
+ found = true;
+ valuePairs( pline , cursor+1 );
+ }
+ cursor++;
+ }
+ cursor++;
+ if( !found ) /* not a pair */
+ justValue( pline );
+ }
+ free (pline);
+}
+
+/*
+ * removes trailing and preceeding whitespaces from a string
+ */
+char*
+AnyOption::chomp( char *str )
+{
+ while( *str == whitespace )
+ str++;
+ char *end = str+strlen(str)-1;
+ while( *end == whitespace )
+ end--;
+ *(end+1) = nullterminate;
+ return str;
+}
+
+void
+AnyOption::valuePairs( char *type, char *value )
+{
+ if ( strlen(chomp(type)) == 1 ){ /* this is a char option */
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == type[0] ){ /* match */
+ if( optchartype[i] == COMMON_OPT ||
+ optchartype[i] == FILE_OPT )
+ {
+ setValue( type[0] , chomp(value) );
+ return;
+ }
+ }
+ }
+ }
+ /* if no char options matched */
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], type ) == 0 ){ /* match */
+ if( optiontype[i] == COMMON_OPT ||
+ optiontype[i] == FILE_OPT )
+ {
+ setValue( type , chomp(value) );
+ return;
+ }
+ }
+ }
+ printVerbose( "Unknown option in resourcefile : " );
+ printVerbose( type );
+ printVerbose( );
+}
+
+void
+AnyOption::justValue( char *type )
+{
+
+ if ( strlen(chomp(type)) == 1 ){ /* this is a char option */
+ for( int i = 0 ; i < optchar_counter ; i++ ){
+ if( optionchars[i] == type[0] ){ /* match */
+ if( optchartype[i] == COMMON_FLAG ||
+ optchartype[i] == FILE_FLAG )
+ {
+ setFlagOn( type[0] );
+ return;
+ }
+ }
+ }
+ }
+ /* if no char options matched */
+ for( int i = 0 ; i < option_counter ; i++ ){
+ if( strcmp( options[i], type ) == 0 ){ /* match */
+ if( optiontype[i] == COMMON_FLAG ||
+ optiontype[i] == FILE_FLAG )
+ {
+ setFlagOn( type );
+ return;
+ }
+ }
+ }
+ printVerbose( "Unknown option in resourcefile : " );
+ printVerbose( type );
+ printVerbose( );
+}
+
+/*
+ * usage and help
+ */
+
+
+void
+AnyOption::printAutoUsage()
+{
+ if( autousage ) printUsage();
+}
+
+void
+AnyOption::printUsage()
+{
+
+ if( once ) {
+ once = false ;
+ cout << endl ;
+ for( int i = 0 ; i < usage_lines ; i++ )
+ cout << usage[i] << endl ;
+ cout << endl ;
+ }
+}
+
+
+void
+AnyOption::addUsage( const char *line )
+{
+ if( usage_lines >= max_usage_lines ){
+ if( doubleUsageStorage() == false ){
+ addUsageError( line );
+ exit(1);
+ }
+ }
+ usage[ usage_lines ] = line ;
+ usage_lines++;
+}
+
+void
+AnyOption::addUsageError( const char *line )
+{
+ cout << endl ;
+ cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ;
+ cout << "While adding the usage/help : \""<< line << "\"" << endl;
+ cout << "Exiting." << endl ;
+ cout << endl ;
+ exit(0);
+
+}
diff --git a/mmc_updater/depends/AnyOption/anyoption.h b/mmc_updater/depends/AnyOption/anyoption.h
new file mode 100644
index 00000000..01501df2
--- /dev/null
+++ b/mmc_updater/depends/AnyOption/anyoption.h
@@ -0,0 +1,270 @@
+#ifndef _ANYOPTION_H
+#define _ANYOPTION_H
+
+#include <iostream>
+#include <fstream>
+#include <stdlib.h>
+#include <string>
+
+#define COMMON_OPT 1
+#define COMMAND_OPT 2
+#define FILE_OPT 3
+#define COMMON_FLAG 4
+#define COMMAND_FLAG 5
+#define FILE_FLAG 6
+
+#define COMMAND_OPTION_TYPE 1
+#define COMMAND_FLAG_TYPE 2
+#define FILE_OPTION_TYPE 3
+#define FILE_FLAG_TYPE 4
+#define UNKNOWN_TYPE 5
+
+#define DEFAULT_MAXOPTS 10
+#define MAX_LONG_PREFIX_LENGTH 2
+
+#define DEFAULT_MAXUSAGE 3
+#define DEFAULT_MAXHELP 10
+
+#define TRUE_FLAG "true"
+
+using namespace std;
+
+class AnyOption
+{
+
+public: /* the public interface */
+ AnyOption();
+ AnyOption(int maxoptions );
+ AnyOption(int maxoptions , int maxcharoptions);
+ ~AnyOption();
+
+ /*
+ * following set methods specifies the
+ * special characters and delimiters
+ * if not set traditional defaults will be used
+ */
+
+ void setCommandPrefixChar( char _prefix ); /* '-' in "-w" */
+ void setCommandLongPrefix( char *_prefix ); /* '--' in "--width" */
+ void setFileCommentChar( char _comment ); /* '#' in shellscripts */
+ void setFileDelimiterChar( char _delimiter );/* ':' in "width : 100" */
+
+ /*
+ * provide the input for the options
+ * like argv[] for commndline and the
+ * option file name to use;
+ */
+
+ void useCommandArgs( int _argc, char **_argv );
+ void useFiileName( const char *_filename );
+
+ /*
+ * turn off the POSIX style options
+ * this means anything starting with a '-' or "--"
+ * will be considered a valid option
+ * which alo means you cannot add a bunch of
+ * POIX options chars together like "-lr" for "-l -r"
+ *
+ */
+
+ void noPOSIX();
+
+ /*
+ * prints warning verbose if you set anything wrong
+ */
+ void setVerbose();
+
+
+ /*
+ * there are two types of options
+ *
+ * Option - has an associated value ( -w 100 )
+ * Flag - no value, just a boolean flag ( -nogui )
+ *
+ * the options can be either a string ( GNU style )
+ * or a character ( traditional POSIX style )
+ * or both ( --width, -w )
+ *
+ * the options can be common to the commandline and
+ * the optionfile, or can belong only to either of
+ * commandline and optionfile
+ *
+ * following set methods, handle all the aboove
+ * cases of options.
+ */
+
+ /* options comman to command line and option file */
+ void setOption( const char *opt_string );
+ void setOption( char opt_char );
+ void setOption( const char *opt_string , char opt_char );
+ void setFlag( const char *opt_string );
+ void setFlag( char opt_char );
+ void setFlag( const char *opt_string , char opt_char );
+
+ /* options read from commandline only */
+ void setCommandOption( const char *opt_string );
+ void setCommandOption( char opt_char );
+ void setCommandOption( const char *opt_string , char opt_char );
+ void setCommandFlag( const char *opt_string );
+ void setCommandFlag( char opt_char );
+ void setCommandFlag( const char *opt_string , char opt_char );
+
+ /* options read from an option file only */
+ void setFileOption( const char *opt_string );
+ void setFileOption( char opt_char );
+ void setFileOption( const char *opt_string , char opt_char );
+ void setFileFlag( const char *opt_string );
+ void setFileFlag( char opt_char );
+ void setFileFlag( const char *opt_string , char opt_char );
+
+ /*
+ * process the options, registerd using
+ * useCommandArgs() and useFileName();
+ */
+ void processOptions();
+ void processCommandArgs();
+ void processCommandArgs( int max_args );
+ bool processFile();
+
+ /*
+ * process the specified options
+ */
+ void processCommandArgs( int _argc, char **_argv );
+ void processCommandArgs( int _argc, char **_argv, int max_args );
+ bool processFile( const char *_filename );
+
+ /*
+ * get the value of the options
+ * will return NULL if no value is set
+ */
+ char *getValue( const char *_option );
+ bool getFlag( const char *_option );
+ char *getValue( char _optchar );
+ bool getFlag( char _optchar );
+
+ /*
+ * Print Usage
+ */
+ void printUsage();
+ void printAutoUsage();
+ void addUsage( const char *line );
+ void printHelp();
+ /* print auto usage printing for unknown options or flag */
+ void autoUsagePrint(bool flag);
+
+ /*
+ * get the argument count and arguments sans the options
+ */
+ int getArgc();
+ char* getArgv( int index );
+ bool hasOptions();
+
+private: /* the hidden data structure */
+ int argc; /* commandline arg count */
+ char **argv; /* commndline args */
+ const char* filename; /* the option file */
+ char* appname; /* the application name from argv[0] */
+
+ int *new_argv; /* arguments sans options (index to argv) */
+ int new_argc; /* argument count sans the options */
+ int max_legal_args; /* ignore extra arguments */
+
+
+ /* option strings storage + indexing */
+ int max_options; /* maximum number of options */
+ const char **options; /* storage */
+ int *optiontype; /* type - common, command, file */
+ int *optionindex; /* index into value storage */
+ int option_counter; /* counter for added options */
+
+ /* option chars storage + indexing */
+ int max_char_options; /* maximum number options */
+ char *optionchars; /* storage */
+ int *optchartype; /* type - common, command, file */
+ int *optcharindex; /* index into value storage */
+ int optchar_counter; /* counter for added options */
+
+ /* values */
+ char **values; /* common value storage */
+ int g_value_counter; /* globally updated value index LAME! */
+
+ /* help and usage */
+ const char **usage; /* usage */
+ int max_usage_lines; /* max usage lines reseverd */
+ int usage_lines; /* number of usage lines */
+
+ bool command_set; /* if argc/argv were provided */
+ bool file_set; /* if a filename was provided */
+ bool mem_allocated; /* if memory allocated in init() */
+ bool posix_style; /* enables to turn off POSIX style options */
+ bool verbose; /* silent|verbose */
+ bool print_usage; /* usage verbose */
+ bool print_help; /* help verbose */
+
+ char opt_prefix_char; /* '-' in "-w" */
+ char long_opt_prefix[MAX_LONG_PREFIX_LENGTH + 1]; /* '--' in "--width" */
+ char file_delimiter_char; /* ':' in width : 100 */
+ char file_comment_char; /* '#' in "#this is a comment" */
+ char equalsign;
+ char comment;
+ char delimiter;
+ char endofline;
+ char whitespace;
+ char nullterminate;
+
+ bool set; //was static member
+ bool once; //was static member
+
+ bool hasoptions;
+ bool autousage;
+
+private: /* the hidden utils */
+ void init();
+ void init(int maxopt, int maxcharopt );
+ bool alloc();
+ void cleanup();
+ bool valueStoreOK();
+
+ /* grow storage arrays as required */
+ bool doubleOptStorage();
+ bool doubleCharStorage();
+ bool doubleUsageStorage();
+
+ bool setValue( const char *option , char *value );
+ bool setFlagOn( const char *option );
+ bool setValue( char optchar , char *value);
+ bool setFlagOn( char optchar );
+
+ void addOption( const char* option , int type );
+ void addOption( char optchar , int type );
+ void addOptionError( const char *opt);
+ void addOptionError( char opt);
+ bool findFlag( char* value );
+ void addUsageError( const char *line );
+ bool CommandSet();
+ bool FileSet();
+ bool POSIX();
+
+ char parsePOSIX( char* arg );
+ int parseGNU( char *arg );
+ bool matchChar( char c );
+ int matchOpt( char *opt );
+
+ /* dot file methods */
+ char *readFile();
+ char *readFile( const char* fname );
+ bool consumeFile( char *buffer );
+ void processLine( char *theline, int length );
+ char *chomp( char *str );
+ void valuePairs( char *type, char *value );
+ void justValue( char *value );
+
+ void printVerbose( const char *msg );
+ void printVerbose( char *msg );
+ void printVerbose( char ch );
+ void printVerbose( );
+
+
+};
+
+#endif /* ! _ANYOPTION_H */
diff --git a/mmc_updater/depends/tinyxml/CMakeLists.txt b/mmc_updater/depends/tinyxml/CMakeLists.txt
new file mode 100644
index 00000000..25c9bb92
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/CMakeLists.txt
@@ -0,0 +1,24 @@
+# TinyXML 1.0.1
+project(tinyxml)
+
+cmake_minimum_required(VERSION 2.6)
+
+Add_definitions(-DTIXML_USE_STL)
+
+set(SOURCES
+ tinystr.cpp
+ tinyxml.cpp
+ tinyxmlerror.cpp
+ tinyxmlparser.cpp
+)
+
+set(HEADERS
+ tinystr.h
+ tinyxml.h
+)
+
+add_library(tinyxml
+ ${SOURCES}
+ ${HEADERS}
+)
+
diff --git a/mmc_updater/depends/tinyxml/readme.txt b/mmc_updater/depends/tinyxml/readme.txt
new file mode 100644
index 00000000..89d9e8d3
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/readme.txt
@@ -0,0 +1,530 @@
+/** @mainpage
+
+<h1> TinyXML </h1>
+
+TinyXML is a simple, small, C++ XML parser that can be easily
+integrated into other programs.
+
+<h2> What it does. </h2>
+
+In brief, TinyXML parses an XML document, and builds from that a
+Document Object Model (DOM) that can be read, modified, and saved.
+
+XML stands for "eXtensible Markup Language." It allows you to create
+your own document markups. Where HTML does a very good job of marking
+documents for browsers, XML allows you to define any kind of document
+markup, for example a document that describes a "to do" list for an
+organizer application. XML is a very structured and convenient format.
+All those random file formats created to store application data can
+all be replaced with XML. One parser for everything.
+
+The best place for the complete, correct, and quite frankly hard to
+read spec is at <a href="http://www.w3.org/TR/2004/REC-xml-20040204/">
+http://www.w3.org/TR/2004/REC-xml-20040204/</a>. An intro to XML
+(that I really like) can be found at
+<a href="http://skew.org/xml/tutorial/">http://skew.org/xml/tutorial</a>.
+
+There are different ways to access and interact with XML data.
+TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed
+into a C++ objects that can be browsed and manipulated, and then
+written to disk or another output stream. You can also construct an XML document
+from scratch with C++ objects and write this to disk or another output
+stream.
+
+TinyXML is designed to be easy and fast to learn. It is two headers
+and four cpp files. Simply add these to your project and off you go.
+There is an example file - xmltest.cpp - to get you started.
+
+TinyXML is released under the ZLib license,
+so you can use it in open source or commercial code. The details
+of the license are at the top of every source file.
+
+TinyXML attempts to be a flexible parser, but with truly correct and
+compliant XML output. TinyXML should compile on any reasonably C++
+compliant system. It does not rely on exceptions or RTTI. It can be
+compiled with or without STL support. TinyXML fully supports
+the UTF-8 encoding, and the first 64k character entities.
+
+
+<h2> What it doesn't do. </h2>
+
+TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs
+(eXtensible Stylesheet Language.) There are other parsers out there
+(check out www.sourceforge.org, search for XML) that are much more fully
+featured. But they are also much bigger, take longer to set up in
+your project, have a higher learning curve, and often have a more
+restrictive license. If you are working with browsers or have more
+complete XML needs, TinyXML is not the parser for you.
+
+The following DTD syntax will not parse at this time in TinyXML:
+
+@verbatim
+ <!DOCTYPE Archiv [
+ <!ELEMENT Comment (#PCDATA)>
+ ]>
+@endverbatim
+
+because TinyXML sees this as a !DOCTYPE node with an illegally
+embedded !ELEMENT node. This may be addressed in the future.
+
+<h2> Tutorials. </h2>
+
+For the impatient, here is a tutorial to get you going. A great way to get started,
+but it is worth your time to read this (very short) manual completely.
+
+- @subpage tutorial0
+
+<h2> Code Status. </h2>
+
+TinyXML is mature, tested code. It is very stable. If you find
+bugs, please file a bug report on the sourceforge web site
+(www.sourceforge.net/projects/tinyxml). We'll get them straightened
+out as soon as possible.
+
+There are some areas of improvement; please check sourceforge if you are
+interested in working on TinyXML.
+
+<h2> Related Projects </h2>
+
+TinyXML projects you may find useful! (Descriptions provided by the projects.)
+
+<ul>
+<li> <b>TinyXPath</b> (http://tinyxpath.sourceforge.net). TinyXPath is a small footprint
+ XPath syntax decoder, written in C++.</li>
+<li> <b>TinyXML++</b> (http://code.google.com/p/ticpp/). TinyXML++ is a completely new
+ interface to TinyXML that uses MANY of the C++ strengths. Templates,
+ exceptions, and much better error handling.</li>
+</ul>
+
+<h2> Features </h2>
+
+<h3> Using STL </h3>
+
+TinyXML can be compiled to use or not use STL. When using STL, TinyXML
+uses the std::string class, and fully supports std::istream, std::ostream,
+operator<<, and operator>>. Many API methods have both 'const char*' and
+'const std::string&' forms.
+
+When STL support is compiled out, no STL files are included whatsoever. All
+the string classes are implemented by TinyXML itself. API methods
+all use the 'const char*' form for input.
+
+Use the compile time #define:
+
+ TIXML_USE_STL
+
+to compile one version or the other. This can be passed by the compiler,
+or set as the first line of "tinyxml.h".
+
+Note: If compiling the test code in Linux, setting the environment
+variable TINYXML_USE_STL=YES/NO will control STL compilation. In the
+Windows project file, STL and non STL targets are provided. In your project,
+It's probably easiest to add the line "#define TIXML_USE_STL" as the first
+line of tinyxml.h.
+
+<h3> UTF-8 </h3>
+
+TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML
+also supports "legacy mode" - the encoding used before UTF-8 support and
+probably best described as "extended ascii".
+
+Normally, TinyXML will try to detect the correct encoding and use it. However,
+by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML
+can be forced to always use one encoding.
+
+TinyXML will assume Legacy Mode until one of the following occurs:
+<ol>
+ <li> If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf)
+ begin the file or data stream, TinyXML will read it as UTF-8. </li>
+ <li> If the declaration tag is read, and it has an encoding="UTF-8", then
+ TinyXML will read it as UTF-8. </li>
+ <li> If the declaration tag is read, and it has no encoding specified, then TinyXML will
+ read it as UTF-8. </li>
+ <li> If the declaration tag is read, and it has an encoding="something else", then TinyXML
+ will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's
+ not clear what that mode does exactly, but old content should keep working.</li>
+ <li> Until one of the above criteria is met, TinyXML runs in Legacy Mode.</li>
+</ol>
+
+What happens if the encoding is incorrectly set or detected? TinyXML will try
+to read and pass through text seen as improperly encoded. You may get some strange results or
+mangled characters. You may want to force TinyXML to the correct mode.
+
+You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or
+LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all
+the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may
+force it to TIXML_ENCODING_UTF8 with the same technique.
+
+For English users, using English XML, UTF-8 is the same as low-ASCII. You
+don't need to be aware of UTF-8 or change your code in any way. You can think
+of UTF-8 as a "superset" of ASCII.
+
+UTF-8 is not a double byte format - but it is a standard encoding of Unicode!
+TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time.
+It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding
+of unicode. This is a source of confusion.
+
+For "high-ascii" languages - everything not English, pretty much - TinyXML can
+handle all languages, at the same time, as long as the XML is encoded
+in UTF-8. That can be a little tricky, older programs and operating systems
+tend to use the "default" or "traditional" code page. Many apps (and almost all
+modern ones) can output UTF-8, but older or stubborn (or just broken) ones
+still output text in the default code page.
+
+For example, Japanese systems traditionally use SHIFT-JIS encoding.
+Text encoded as SHIFT-JIS can not be read by TinyXML.
+A good text editor can import SHIFT-JIS and then save as UTF-8.
+
+The <a href="http://skew.org/xml/tutorial/">Skew.org link</a> does a great
+job covering the encoding issue.
+
+The test file "utf8test.xml" is an XML containing English, Spanish, Russian,
+and Simplified Chinese. (Hopefully they are translated correctly). The file
+"utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that
+if you don't have the correct fonts (Simplified Chinese or Russian) on your
+system, you won't see output that matches the GIF file even if you can parse
+it correctly. Also note that (at least on my Windows machine) console output
+is in a Western code page, so that Print() or printf() cannot correctly display
+the file. This is not a bug in TinyXML - just an OS issue. No data is lost or
+destroyed by TinyXML. The console just doesn't render UTF-8.
+
+
+<h3> Entities </h3>
+TinyXML recognizes the pre-defined "character entities", meaning special
+characters. Namely:
+
+@verbatim
+ &amp; &
+ &lt; <
+ &gt; >
+ &quot; "
+ &apos; '
+@endverbatim
+
+These are recognized when the XML document is read, and translated to there
+UTF-8 equivalents. For instance, text with the XML of:
+
+@verbatim
+ Far &amp; Away
+@endverbatim
+
+will have the Value() of "Far & Away" when queried from the TiXmlText object,
+and will be written back to the XML stream/file as an ampersand. Older versions
+of TinyXML "preserved" character entities, but the newer versions will translate
+them into characters.
+
+Additionally, any character can be specified by its Unicode code point:
+The syntax "&#xA0;" or "&#160;" are both to the non-breaking space characher.
+
+<h3> Printing </h3>
+TinyXML can print output in several different ways that all have strengths and limitations.
+
+- Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout.
+ - "Pretty prints", but you don't have control over printing options.
+ - The output is streamed directly to the FILE object, so there is no memory overhead
+ in the TinyXML code.
+ - used by Print() and SaveFile()
+
+- operator<<. Output to a c++ stream.
+ - Integrates with standart C++ iostreams.
+ - Outputs in "network printing" mode without line breaks. Good for network transmission
+ and moving XML between C++ objects, but hard for a human to read.
+
+- TiXmlPrinter. Output to a std::string or memory buffer.
+ - API is less concise
+ - Future printing options will be put here.
+ - Printing may change slightly in future versions as it is refined and expanded.
+
+<h3> Streams </h3>
+With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well
+as C (FILE*) streams. There are some differences that you may need to be aware of.
+
+C style output:
+ - based on FILE*
+ - the Print() and SaveFile() methods
+
+ Generates formatted output, with plenty of white space, intended to be as
+ human-readable as possible. They are very fast, and tolerant of ill formed
+ XML documents. For example, an XML document that contains 2 root elements
+ and 2 declarations, will still print.
+
+C style input:
+ - based on FILE*
+ - the Parse() and LoadFile() methods
+
+ A fast, tolerant read. Use whenever you don't need the C++ streams.
+
+C++ style output:
+ - based on std::ostream
+ - operator<<
+
+ Generates condensed output, intended for network transmission rather than
+ readability. Depending on your system's implementation of the ostream class,
+ these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML:
+ a document should contain the correct one root element. Additional root level
+ elements will not be streamed out.
+
+C++ style input:
+ - based on std::istream
+ - operator>>
+
+ Reads XML from a stream, making it useful for network transmission. The tricky
+ part is knowing when the XML document is complete, since there will almost
+ certainly be other data in the stream. TinyXML will assume the XML data is
+ complete after it reads the root element. Put another way, documents that
+ are ill-constructed with more than one root element will not read correctly.
+ Also note that operator>> is somewhat slower than Parse, due to both
+ implementation of the STL and limitations of TinyXML.
+
+<h3> White space </h3>
+The world simply does not agree on whether white space should be kept, or condensed.
+For example, pretend the '_' is a space, and look at "Hello____world". HTML, and
+at least some XML parsers, will interpret this as "Hello_world". They condense white
+space. Some XML parsers do not, and will leave it as "Hello____world". (Remember
+to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become
+Hello___world.
+
+It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the
+first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior.
+The default is to condense white space.
+
+If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool )
+before making any calls to Parse XML data, and I don't recommend changing it after
+it has been set.
+
+
+<h3> Handles </h3>
+
+Where browsing an XML document in a robust way, it is important to check
+for null returns from method calls. An error safe implementation can
+generate a lot of code like:
+
+@verbatim
+TiXmlElement* root = document.FirstChildElement( "Document" );
+if ( root )
+{
+ TiXmlElement* element = root->FirstChildElement( "Element" );
+ if ( element )
+ {
+ TiXmlElement* child = element->FirstChildElement( "Child" );
+ if ( child )
+ {
+ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
+ if ( child2 )
+ {
+ // Finally do something useful.
+@endverbatim
+
+Handles have been introduced to clean this up. Using the TiXmlHandle class,
+the previous code reduces to:
+
+@verbatim
+TiXmlHandle docHandle( &document );
+TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
+if ( child2 )
+{
+ // do something useful
+@endverbatim
+
+Which is much easier to deal with. See TiXmlHandle for more information.
+
+
+<h3> Row and Column tracking </h3>
+Being able to track nodes and attributes back to their origin location
+in source files can be very important for some applications. Additionally,
+knowing where parsing errors occured in the original source can be very
+time saving.
+
+TinyXML can tracks the row and column origin of all nodes and attributes
+in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return
+the origin of the node in the source text. The correct tabs can be
+configured in TiXmlDocument::SetTabSize().
+
+
+<h2> Using and Installing </h2>
+
+To Compile and Run xmltest:
+
+A Linux Makefile and a Windows Visual C++ .dsw file is provided.
+Simply compile and run. It will write the file demotest.xml to your
+disk and generate output on the screen. It also tests walking the
+DOM by printing out the number of nodes found using different
+techniques.
+
+The Linux makefile is very generic and runs on many systems - it
+is currently tested on mingw and
+MacOSX. You do not need to run 'make depend'. The dependecies have been
+hard coded.
+
+<h3>Windows project file for VC6</h3>
+<ul>
+<li>tinyxml: tinyxml library, non-STL </li>
+<li>tinyxmlSTL: tinyxml library, STL </li>
+<li>tinyXmlTest: test app, non-STL </li>
+<li>tinyXmlTestSTL: test app, STL </li>
+</ul>
+
+<h3>Makefile</h3>
+At the top of the makefile you can set:
+
+PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in
+the makefile.
+
+In the tinyxml directory, type "make clean" then "make". The executable
+file 'xmltest' will be created.
+
+
+
+<h3>To Use in an Application:</h3>
+
+Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your
+project or make file. That's it! It should compile on any reasonably
+compliant C++ system. You do not need to enable exceptions or
+RTTI for TinyXML.
+
+
+<h2> How TinyXML works. </h2>
+
+An example is probably the best way to go. Take:
+@verbatim
+ <?xml version="1.0" standalone=no>
+ <!-- Our to do list data -->
+ <ToDo>
+ <Item priority="1"> Go to the <bold>Toy store!</bold></Item>
+ <Item priority="2"> Do bills</Item>
+ </ToDo>
+@endverbatim
+
+Its not much of a To Do list, but it will do. To read this file
+(say "demo.xml") you would create a document, and parse it in:
+@verbatim
+ TiXmlDocument doc( "demo.xml" );
+ doc.LoadFile();
+@endverbatim
+
+And its ready to go. Now lets look at some lines and how they
+relate to the DOM.
+
+@verbatim
+<?xml version="1.0" standalone=no>
+@endverbatim
+
+ The first line is a declaration, and gets turned into the
+ TiXmlDeclaration class. It will be the first child of the
+ document node.
+
+ This is the only directive/special tag parsed by TinyXML.
+ Generally directive tags are stored in TiXmlUnknown so the
+ commands wont be lost when it is saved back to disk.
+
+@verbatim
+<!-- Our to do list data -->
+@endverbatim
+
+ A comment. Will become a TiXmlComment object.
+
+@verbatim
+<ToDo>
+@endverbatim
+
+ The "ToDo" tag defines a TiXmlElement object. This one does not have
+ any attributes, but does contain 2 other elements.
+
+@verbatim
+<Item priority="1">
+@endverbatim
+
+ Creates another TiXmlElement which is a child of the "ToDo" element.
+ This element has 1 attribute, with the name "priority" and the value
+ "1".
+
+@verbatim
+Go to the
+@endverbatim
+
+ A TiXmlText. This is a leaf node and cannot contain other nodes.
+ It is a child of the "Item" TiXmlElement.
+
+@verbatim
+<bold>
+@endverbatim
+
+
+ Another TiXmlElement, this one a child of the "Item" element.
+
+Etc.
+
+Looking at the entire object tree, you end up with:
+@verbatim
+TiXmlDocument "demo.xml"
+ TiXmlDeclaration "version='1.0'" "standalone=no"
+ TiXmlComment " Our to do list data"
+ TiXmlElement "ToDo"
+ TiXmlElement "Item" Attribtutes: priority = 1
+ TiXmlText "Go to the "
+ TiXmlElement "bold"
+ TiXmlText "Toy store!"
+ TiXmlElement "Item" Attributes: priority=2
+ TiXmlText "Do bills"
+@endverbatim
+
+<h2> Documentation </h2>
+
+The documentation is build with Doxygen, using the 'dox'
+configuration file.
+
+<h2> License </h2>
+
+TinyXML is released under the zlib license:
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+
+<h2> References </h2>
+
+The World Wide Web Consortium is the definitive standard body for
+XML, and their web pages contain huge amounts of information.
+
+The definitive spec: <a href="http://www.w3.org/TR/2004/REC-xml-20040204/">
+http://www.w3.org/TR/2004/REC-xml-20040204/</a>
+
+I also recommend "XML Pocket Reference" by Robert Eckstein and published by
+OReilly...the book that got the whole thing started.
+
+<h2> Contributors, Contacts, and a Brief History </h2>
+
+Thanks very much to everyone who sends suggestions, bugs, ideas, and
+encouragement. It all helps, and makes this project fun. A special thanks
+to the contributors on the web pages that keep it lively.
+
+So many people have sent in bugs and ideas, that rather than list here
+we try to give credit due in the "changes.txt" file.
+
+TinyXML was originally written by Lee Thomason. (Often the "I" still
+in the documentation.) Lee reviews changes and releases new versions,
+with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community.
+
+We appreciate your suggestions, and would love to know if you
+use TinyXML. Hopefully you will enjoy it and find it useful.
+Please post questions, comments, file bugs, or contact us at:
+
+www.sourceforge.net/projects/tinyxml
+
+Lee Thomason, Yves Berquin, Andrew Ellerton
+*/
diff --git a/mmc_updater/depends/tinyxml/tinystr.cpp b/mmc_updater/depends/tinyxml/tinystr.cpp
new file mode 100644
index 00000000..06657682
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/tinystr.cpp
@@ -0,0 +1,111 @@
+/*
+www.sourceforge.net/projects/tinyxml
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TIXML_USE_STL
+
+#include "tinystr.h"
+
+// Error value for find primitive
+const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
+
+
+// Null rep.
+TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
+
+
+void TiXmlString::reserve (size_type cap)
+{
+ if (cap > capacity())
+ {
+ TiXmlString tmp;
+ tmp.init(length(), cap);
+ memcpy(tmp.start(), data(), length());
+ swap(tmp);
+ }
+}
+
+
+TiXmlString& TiXmlString::assign(const char* str, size_type len)
+{
+ size_type cap = capacity();
+ if (len > cap || cap > 3*(len + 8))
+ {
+ TiXmlString tmp;
+ tmp.init(len);
+ memcpy(tmp.start(), str, len);
+ swap(tmp);
+ }
+ else
+ {
+ memmove(start(), str, len);
+ set_size(len);
+ }
+ return *this;
+}
+
+
+TiXmlString& TiXmlString::append(const char* str, size_type len)
+{
+ size_type newsize = length() + len;
+ if (newsize > capacity())
+ {
+ reserve (newsize + capacity());
+ }
+ memmove(finish(), str, len);
+ set_size(newsize);
+ return *this;
+}
+
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ tmp.reserve(a.length() + b.length());
+ tmp += a;
+ tmp += b;
+ return tmp;
+}
+
+TiXmlString operator + (const TiXmlString & a, const char* b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
+ tmp.reserve(a.length() + b_len);
+ tmp += a;
+ tmp.append(b, b_len);
+ return tmp;
+}
+
+TiXmlString operator + (const char* a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
+ tmp.reserve(a_len + b.length());
+ tmp.append(a, a_len);
+ tmp += b;
+ return tmp;
+}
+
+
+#endif // TIXML_USE_STL
diff --git a/mmc_updater/depends/tinyxml/tinystr.h b/mmc_updater/depends/tinyxml/tinystr.h
new file mode 100644
index 00000000..89cca334
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/tinystr.h
@@ -0,0 +1,305 @@
+/*
+www.sourceforge.net/projects/tinyxml
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TIXML_USE_STL
+
+#ifndef TIXML_STRING_INCLUDED
+#define TIXML_STRING_INCLUDED
+
+#include <assert.h>
+#include <string.h>
+
+/* The support for explicit isn't that universal, and it isn't really
+ required - it is used to check that the TiXmlString class isn't incorrectly
+ used. Be nice to old compilers and macro it here:
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
+ // Microsoft visual studio, version 6 and higher.
+ #define TIXML_EXPLICIT explicit
+#elif defined(__GNUC__) && (__GNUC__ >= 3 )
+ // GCC version 3 and higher.s
+ #define TIXML_EXPLICIT explicit
+#else
+ #define TIXML_EXPLICIT
+#endif
+
+
+/*
+ TiXmlString is an emulation of a subset of the std::string template.
+ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
+ Only the member functions relevant to the TinyXML project have been implemented.
+ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
+ a string and there's no more room, we allocate a buffer twice as big as we need.
+*/
+class TiXmlString
+{
+ public :
+ // The size type used
+ typedef size_t size_type;
+
+ // Error value for find primitive
+ static const size_type npos; // = -1;
+
+
+ // TiXmlString empty constructor
+ TiXmlString () : rep_(&nullrep_)
+ {
+ }
+
+ // TiXmlString copy constructor
+ TiXmlString ( const TiXmlString & copy) : rep_(0)
+ {
+ init(copy.length());
+ memcpy(start(), copy.data(), length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
+ {
+ init( static_cast<size_type>( strlen(copy) ));
+ memcpy(start(), copy, length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
+ {
+ init(len);
+ memcpy(start(), str, len);
+ }
+
+ // TiXmlString destructor
+ ~TiXmlString ()
+ {
+ quit();
+ }
+
+ TiXmlString& operator = (const char * copy)
+ {
+ return assign( copy, (size_type)strlen(copy));
+ }
+
+ TiXmlString& operator = (const TiXmlString & copy)
+ {
+ return assign(copy.start(), copy.length());
+ }
+
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const char * suffix)
+ {
+ return append(suffix, static_cast<size_type>( strlen(suffix) ));
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (char single)
+ {
+ return append(&single, 1);
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const TiXmlString & suffix)
+ {
+ return append(suffix.data(), suffix.length());
+ }
+
+
+ // Convert a TiXmlString into a null-terminated char *
+ const char * c_str () const { return rep_->str; }
+
+ // Convert a TiXmlString into a char * (need not be null terminated).
+ const char * data () const { return rep_->str; }
+
+ // Return the length of a TiXmlString
+ size_type length () const { return rep_->size; }
+
+ // Alias for length()
+ size_type size () const { return rep_->size; }
+
+ // Checks if a TiXmlString is empty
+ bool empty () const { return rep_->size == 0; }
+
+ // Return capacity of string
+ size_type capacity () const { return rep_->capacity; }
+
+
+ // single char extraction
+ const char& at (size_type index) const
+ {
+ assert( index < length() );
+ return rep_->str[ index ];
+ }
+
+ // [] operator
+ char& operator [] (size_type index) const
+ {
+ assert( index < length() );
+ return rep_->str[ index ];
+ }
+
+ // find a char in a string. Return TiXmlString::npos if not found
+ size_type find (char lookup) const
+ {
+ return find(lookup, 0);
+ }
+
+ // find a char in a string from an offset. Return TiXmlString::npos if not found
+ size_type find (char tofind, size_type offset) const
+ {
+ if (offset >= length()) return npos;
+
+ for (const char* p = c_str() + offset; *p != '\0'; ++p)
+ {
+ if (*p == tofind) return static_cast< size_type >( p - c_str() );
+ }
+ return npos;
+ }
+
+ void clear ()
+ {
+ //Lee:
+ //The original was just too strange, though correct:
+ // TiXmlString().swap(*this);
+ //Instead use the quit & re-init:
+ quit();
+ init(0,0);
+ }
+
+ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
+ function DOES NOT clear the content of the TiXmlString if any exists.
+ */
+ void reserve (size_type cap);
+
+ TiXmlString& assign (const char* str, size_type len);
+
+ TiXmlString& append (const char* str, size_type len);
+
+ void swap (TiXmlString& other)
+ {
+ Rep* r = rep_;
+ rep_ = other.rep_;
+ other.rep_ = r;
+ }
+
+ private:
+
+ void init(size_type sz) { init(sz, sz); }
+ void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
+ char* start() const { return rep_->str; }
+ char* finish() const { return rep_->str + rep_->size; }
+
+ struct Rep
+ {
+ size_type size, capacity;
+ char str[1];
+ };
+
+ void init(size_type sz, size_type cap)
+ {
+ if (cap)
+ {
+ // Lee: the original form:
+ // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
+ // doesn't work in some cases of new being overloaded. Switching
+ // to the normal allocation, although use an 'int' for systems
+ // that are overly picky about structure alignment.
+ const size_type bytesNeeded = sizeof(Rep) + cap;
+ const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
+ rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
+
+ rep_->str[ rep_->size = sz ] = '\0';
+ rep_->capacity = cap;
+ }
+ else
+ {
+ rep_ = &nullrep_;
+ }
+ }
+
+ void quit()
+ {
+ if (rep_ != &nullrep_)
+ {
+ // The rep_ is really an array of ints. (see the allocator, above).
+ // Cast it back before delete, so the compiler won't incorrectly call destructors.
+ delete [] ( reinterpret_cast<int*>( rep_ ) );
+ }
+ }
+
+ Rep * rep_;
+ static Rep nullrep_;
+
+} ;
+
+
+inline bool operator == (const TiXmlString & a, const TiXmlString & b)
+{
+ return ( a.length() == b.length() ) // optimization on some platforms
+ && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
+}
+inline bool operator < (const TiXmlString & a, const TiXmlString & b)
+{
+ return strcmp(a.c_str(), b.c_str()) < 0;
+}
+
+inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
+inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
+inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
+inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
+
+inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
+inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
+inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
+inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
+TiXmlString operator + (const TiXmlString & a, const char* b);
+TiXmlString operator + (const char* a, const TiXmlString & b);
+
+
+/*
+ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
+ Only the operators that we need for TinyXML have been developped.
+*/
+class TiXmlOutStream : public TiXmlString
+{
+public :
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const TiXmlString & in)
+ {
+ *this += in;
+ return *this;
+ }
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const char * in)
+ {
+ *this += in;
+ return *this;
+ }
+
+} ;
+
+#endif // TIXML_STRING_INCLUDED
+#endif // TIXML_USE_STL
diff --git a/mmc_updater/depends/tinyxml/tinyxml.cpp b/mmc_updater/depends/tinyxml/tinyxml.cpp
new file mode 100644
index 00000000..9c161dfc
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/tinyxml.cpp
@@ -0,0 +1,1886 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include <ctype.h>
+
+#ifdef TIXML_USE_STL
+#include <sstream>
+#include <iostream>
+#endif
+
+#include "tinyxml.h"
+
+FILE* TiXmlFOpen( const char* filename, const char* mode );
+
+bool TiXmlBase::condenseWhiteSpace = true;
+
+// Microsoft compiler security
+FILE* TiXmlFOpen( const char* filename, const char* mode )
+{
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ FILE* fp = 0;
+ errno_t err = fopen_s( &fp, filename, mode );
+ if ( !err && fp )
+ return fp;
+ return 0;
+ #else
+ return fopen( filename, mode );
+ #endif
+}
+
+void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
+{
+ int i=0;
+
+ while( i<(int)str.length() )
+ {
+ unsigned char c = (unsigned char) str[i];
+
+ if ( c == '&'
+ && i < ( (int)str.length() - 2 )
+ && str[i+1] == '#'
+ && str[i+2] == 'x' )
+ {
+ // Hexadecimal character reference.
+ // Pass through unchanged.
+ // &#xA9; -- copyright symbol, for example.
+ //
+ // The -1 is a bug fix from Rob Laveaux. It keeps
+ // an overflow from happening if there is no ';'.
+ // There are actually 2 ways to exit this loop -
+ // while fails (error case) and break (semicolon found).
+ // However, there is no mechanism (currently) for
+ // this function to return an error.
+ while ( i<(int)str.length()-1 )
+ {
+ outString->append( str.c_str() + i, 1 );
+ ++i;
+ if ( str[i] == ';' )
+ break;
+ }
+ }
+ else if ( c == '&' )
+ {
+ outString->append( entity[0].str, entity[0].strLength );
+ ++i;
+ }
+ else if ( c == '<' )
+ {
+ outString->append( entity[1].str, entity[1].strLength );
+ ++i;
+ }
+ else if ( c == '>' )
+ {
+ outString->append( entity[2].str, entity[2].strLength );
+ ++i;
+ }
+ else if ( c == '\"' )
+ {
+ outString->append( entity[3].str, entity[3].strLength );
+ ++i;
+ }
+ else if ( c == '\'' )
+ {
+ outString->append( entity[4].str, entity[4].strLength );
+ ++i;
+ }
+ else if ( c < 32 )
+ {
+ // Easy pass at non-alpha/numeric/symbol
+ // Below 32 is symbolic.
+ char buf[ 32 ];
+
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) );
+ #else
+ sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
+ #endif
+
+ //*ME: warning C4267: convert 'size_t' to 'int'
+ //*ME: Int-Cast to make compiler happy ...
+ outString->append( buf, (int)strlen( buf ) );
+ ++i;
+ }
+ else
+ {
+ //char realc = (char) c;
+ //outString->append( &realc, 1 );
+ *outString += (char) c; // somewhat more efficient function call.
+ ++i;
+ }
+ }
+}
+
+
+TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
+{
+ parent = 0;
+ type = _type;
+ firstChild = 0;
+ lastChild = 0;
+ prev = 0;
+ next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+}
+
+
+void TiXmlNode::CopyTo( TiXmlNode* target ) const
+{
+ target->SetValue (value.c_str() );
+ target->userData = userData;
+ target->location = location;
+}
+
+
+void TiXmlNode::Clear()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+
+ firstChild = 0;
+ lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
+{
+ assert( node->parent == 0 || node->parent == this );
+ assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
+
+ if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ delete node;
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ node->parent = this;
+
+ node->prev = lastChild;
+ node->next = 0;
+
+ if ( lastChild )
+ lastChild->next = node;
+ else
+ firstChild = node; // it was an empty list.
+
+ lastChild = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
+{
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+
+ return LinkEndChild( node );
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
+{
+ if ( !beforeThis || beforeThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->next = beforeThis;
+ node->prev = beforeThis->prev;
+ if ( beforeThis->prev )
+ {
+ beforeThis->prev->next = node;
+ }
+ else
+ {
+ assert( firstChild == beforeThis );
+ firstChild = node;
+ }
+ beforeThis->prev = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
+{
+ if ( !afterThis || afterThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->prev = afterThis;
+ node->next = afterThis->next;
+ if ( afterThis->next )
+ {
+ afterThis->next->prev = node;
+ }
+ else
+ {
+ assert( lastChild == afterThis );
+ lastChild = node;
+ }
+ afterThis->next = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
+{
+ if ( !replaceThis )
+ return 0;
+
+ if ( replaceThis->parent != this )
+ return 0;
+
+ if ( withThis.ToDocument() ) {
+ // A document can never be a child. Thanks to Noam.
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = withThis.Clone();
+ if ( !node )
+ return 0;
+
+ node->next = replaceThis->next;
+ node->prev = replaceThis->prev;
+
+ if ( replaceThis->next )
+ replaceThis->next->prev = node;
+ else
+ lastChild = node;
+
+ if ( replaceThis->prev )
+ replaceThis->prev->next = node;
+ else
+ firstChild = node;
+
+ delete replaceThis;
+ node->parent = this;
+ return node;
+}
+
+
+bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
+{
+ if ( !removeThis ) {
+ return false;
+ }
+
+ if ( removeThis->parent != this )
+ {
+ assert( 0 );
+ return false;
+ }
+
+ if ( removeThis->next )
+ removeThis->next->prev = removeThis->prev;
+ else
+ lastChild = removeThis->prev;
+
+ if ( removeThis->prev )
+ removeThis->prev->next = removeThis->next;
+ else
+ firstChild = removeThis->next;
+
+ delete removeThis;
+ return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = firstChild; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = lastChild; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild();
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling();
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild( val );
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling( val );
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = next; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = prev; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+void TiXmlElement::RemoveAttribute( const char * name )
+{
+ #ifdef TIXML_USE_STL
+ TIXML_STRING str( name );
+ TiXmlAttribute* node = attributeSet.Find( str );
+ #else
+ TiXmlAttribute* node = attributeSet.Find( name );
+ #endif
+ if ( node )
+ {
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+ const TiXmlNode* node;
+
+ for( node = this; node; node = node->parent )
+ {
+ if ( node->ToDocument() )
+ return node->ToDocument();
+ }
+ return 0;
+}
+
+
+TiXmlElement::TiXmlElement (const char * _value)
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement( const std::string& _value )
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement( const TiXmlElement& copy)
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ copy.CopyTo( this );
+}
+
+
+TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base )
+{
+ ClearThis();
+ base.CopyTo( this );
+ return *this;
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+ ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+ Clear();
+ while( attributeSet.First() )
+ {
+ TiXmlAttribute* node = attributeSet.First();
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+
+const char* TiXmlElement::Attribute( const char* name ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( node )
+ return node->Value();
+ return 0;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( attrib )
+ return &attrib->ValueStr();
+ return 0;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+#endif
+
+
+int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+
+
+int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ int ival = 0;
+ int result = node->QueryIntValue( &ival );
+ *value = (unsigned)ival;
+ return result;
+}
+
+
+int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ int result = TIXML_WRONG_TYPE;
+ if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) )
+ {
+ *bval = true;
+ result = TIXML_SUCCESS;
+ }
+ else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) )
+ {
+ *bval = false;
+ result = TIXML_SUCCESS;
+ }
+ return result;
+}
+
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+#endif
+
+
+int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute( const char * name, double val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetDoubleAttribute( const std::string& name, double val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
+ if ( attrib ) {
+ attrib->SetValue( cvalue );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
+ if ( attrib ) {
+ attrib->SetValue( _value );
+ }
+}
+#endif
+
+
+void TiXmlElement::Print( FILE* cfile, int depth ) const
+{
+ int i;
+ assert( cfile );
+ for ( i=0; i<depth; i++ ) {
+ fprintf( cfile, " " );
+ }
+
+ fprintf( cfile, "<%s", value.c_str() );
+
+ const TiXmlAttribute* attrib;
+ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
+ {
+ fprintf( cfile, " " );
+ attrib->Print( cfile, depth );
+ }
+
+ // There are 3 different formatting approaches:
+ // 1) An element without children is printed as a <foo /> node
+ // 2) An element with only a text child is printed as <foo> text </foo>
+ // 3) An element with children is printed on multiple lines.
+ TiXmlNode* node;
+ if ( !firstChild )
+ {
+ fprintf( cfile, " />" );
+ }
+ else if ( firstChild == lastChild && firstChild->ToText() )
+ {
+ fprintf( cfile, ">" );
+ firstChild->Print( cfile, depth + 1 );
+ fprintf( cfile, "</%s>", value.c_str() );
+ }
+ else
+ {
+ fprintf( cfile, ">" );
+
+ for ( node = firstChild; node; node=node->NextSibling() )
+ {
+ if ( !node->ToText() )
+ {
+ fprintf( cfile, "\n" );
+ }
+ node->Print( cfile, depth+1 );
+ }
+ fprintf( cfile, "\n" );
+ for( i=0; i<depth; ++i ) {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "</%s>", value.c_str() );
+ }
+}
+
+
+void TiXmlElement::CopyTo( TiXmlElement* target ) const
+{
+ // superclass:
+ TiXmlNode::CopyTo( target );
+
+ // Element class:
+ // Clone the attributes, then clone the children.
+ const TiXmlAttribute* attribute = 0;
+ for( attribute = attributeSet.First();
+ attribute;
+ attribute = attribute->Next() )
+ {
+ target->SetAttribute( attribute->Name(), attribute->Value() );
+ }
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this, attributeSet.First() ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+ TiXmlElement* clone = new TiXmlElement( Value() );
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+ const TiXmlNode* child = this->FirstChild();
+ if ( child ) {
+ const TiXmlText* childText = child->ToText();
+ if ( childText ) {
+ return childText->Value();
+ }
+ }
+ return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ ClearError();
+}
+
+TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+ return *this;
+}
+
+
+bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
+{
+ return LoadFile( Value(), encoding );
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+ return SaveFile( Value() );
+}
+
+bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
+{
+ TIXML_STRING filename( _filename );
+ value = filename;
+
+ // reading in binary mode so that tinyxml can normalize the EOL
+ FILE* file = TiXmlFOpen( value.c_str (), "rb" );
+
+ if ( file )
+ {
+ bool result = LoadFile( file, encoding );
+ fclose( file );
+ return result;
+ }
+ else
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+}
+
+bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
+{
+ if ( !file )
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Delete the existing data:
+ Clear();
+ location.Clear();
+
+ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
+ long length = 0;
+ fseek( file, 0, SEEK_END );
+ length = ftell( file );
+ fseek( file, 0, SEEK_SET );
+
+ // Strange case, but good to handle up front.
+ if ( length <= 0 )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Subtle bug here. TinyXml did use fgets. But from the XML spec:
+ // 2.11 End-of-Line Handling
+ // <snip>
+ // <quote>
+ // ...the XML processor MUST behave as if it normalized all line breaks in external
+ // parsed entities (including the document entity) on input, before parsing, by translating
+ // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
+ // a single #xA character.
+ // </quote>
+ //
+ // It is not clear fgets does that, and certainly isn't clear it works cross platform.
+ // Generally, you expect fgets to translate from the convention of the OS to the c/unix
+ // convention, and not work generally.
+
+ /*
+ while( fgets( buf, sizeof(buf), file ) )
+ {
+ data += buf;
+ }
+ */
+
+ char* buf = new char[ length+1 ];
+ buf[0] = 0;
+
+ if ( fread( buf, length, 1, file ) != 1 ) {
+ delete [] buf;
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Process the buffer in place to normalize new lines. (See comment above.)
+ // Copies from the 'p' to 'q' pointer, where p can advance faster if
+ // a newline-carriage return is hit.
+ //
+ // Wikipedia:
+ // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
+ // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
+ // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
+ // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
+ // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
+
+ const char* p = buf; // the read head
+ char* q = buf; // the write head
+ const char CR = 0x0d;
+ const char LF = 0x0a;
+
+ buf[length] = 0;
+ while( *p ) {
+ assert( p < (buf+length) );
+ assert( q <= (buf+length) );
+ assert( q <= p );
+
+ if ( *p == CR ) {
+ *q++ = LF;
+ p++;
+ if ( *p == LF ) { // check for CR+LF (and skip LF)
+ p++;
+ }
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ assert( q <= (buf+length) );
+ *q = 0;
+
+ Parse( buf, 0, encoding );
+
+ delete [] buf;
+ return !Error();
+}
+
+
+bool TiXmlDocument::SaveFile( const char * filename ) const
+{
+ // The old c stuff lives on...
+ FILE* fp = TiXmlFOpen( filename, "w" );
+ if ( fp )
+ {
+ bool result = SaveFile( fp );
+ fclose( fp );
+ return result;
+ }
+ return false;
+}
+
+
+bool TiXmlDocument::SaveFile( FILE* fp ) const
+{
+ if ( useMicrosoftBOM )
+ {
+ const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+ const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+ const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+ fputc( TIXML_UTF_LEAD_0, fp );
+ fputc( TIXML_UTF_LEAD_1, fp );
+ fputc( TIXML_UTF_LEAD_2, fp );
+ }
+ Print( fp, 0 );
+ return (ferror(fp) == 0);
+}
+
+
+void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->error = error;
+ target->errorId = errorId;
+ target->errorDesc = errorDesc;
+ target->tabsize = tabsize;
+ target->errorLocation = errorLocation;
+ target->useMicrosoftBOM = useMicrosoftBOM;
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+ TiXmlDocument* clone = new TiXmlDocument();
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlDocument::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ node->Print( cfile, depth );
+ fprintf( cfile, "\n" );
+ }
+}
+
+
+bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+*/
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+*/
+
+void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ TIXML_STRING n, v;
+
+ EncodeString( name, &n );
+ EncodeString( value, &v );
+
+ if (value.find ('\"') == TIXML_STRING::npos) {
+ if ( cfile ) {
+ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
+ }
+ }
+ else {
+ if ( cfile ) {
+ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
+ }
+ }
+}
+
+
+int TiXmlAttribute::QueryIntValue( int* ival ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue( double* dval ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue( int _value )
+{
+ char buf [64];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+ #else
+ sprintf (buf, "%d", _value);
+ #endif
+ SetValue (buf);
+}
+
+void TiXmlAttribute::SetDoubleValue( double _value )
+{
+ char buf [256];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value);
+ #else
+ sprintf (buf, "%g", _value);
+ #endif
+ SetValue (buf);
+}
+
+int TiXmlAttribute::IntValue() const
+{
+ return atoi (value.c_str ());
+}
+
+double TiXmlAttribute::DoubleValue() const
+{
+ return atof (value.c_str ());
+}
+
+
+TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base )
+{
+ Clear();
+ base.CopyTo( this );
+ return *this;
+}
+
+
+void TiXmlComment::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( int i=0; i<depth; i++ )
+ {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "<!--%s-->", value.c_str() );
+}
+
+
+void TiXmlComment::CopyTo( TiXmlComment* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+ TiXmlComment* clone = new TiXmlComment();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlText::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ if ( cdata )
+ {
+ int i;
+ fprintf( cfile, "\n" );
+ for ( i=0; i<depth; i++ ) {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
+ }
+ else
+ {
+ TIXML_STRING buffer;
+ EncodeString( value, &buffer );
+ fprintf( cfile, "%s", buffer.c_str() );
+ }
+}
+
+
+void TiXmlText::CopyTo( TiXmlText* target ) const
+{
+ TiXmlNode::CopyTo( target );
+ target->cdata = cdata;
+}
+
+
+bool TiXmlText::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{
+ TiXmlText* clone = 0;
+ clone = new TiXmlText( "" );
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration( const char * _version,
+ const char * _encoding,
+ const char * _standalone )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+ return *this;
+}
+
+
+void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ if ( cfile ) fprintf( cfile, "<?xml " );
+ if ( str ) (*str) += "<?xml ";
+
+ if ( !version.empty() ) {
+ if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ());
+ if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
+ }
+ if ( !encoding.empty() ) {
+ if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
+ if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
+ }
+ if ( !standalone.empty() ) {
+ if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
+ if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
+ }
+ if ( cfile ) fprintf( cfile, "?>" );
+ if ( str ) (*str) += "?>";
+}
+
+
+void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->version = version;
+ target->encoding = encoding;
+ target->standalone = standalone;
+}
+
+
+bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{
+ TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlUnknown::Print( FILE* cfile, int depth ) const
+{
+ for ( int i=0; i<depth; i++ )
+ fprintf( cfile, " " );
+ fprintf( cfile, "<%s>", value.c_str() );
+}
+
+
+void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+ TiXmlUnknown* clone = new TiXmlUnknown();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+ sentinel.next = &sentinel;
+ sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+ assert( sentinel.next == &sentinel );
+ assert( sentinel.prev == &sentinel );
+}
+
+
+void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
+{
+ #ifdef TIXML_USE_STL
+ assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set.
+ #else
+ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
+ #endif
+
+ addMe->next = &sentinel;
+ addMe->prev = sentinel.prev;
+
+ sentinel.prev->next = addMe;
+ sentinel.prev = addMe;
+}
+
+void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
+{
+ TiXmlAttribute* node;
+
+ for( node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node == removeMe )
+ {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+ return;
+ }
+ }
+ assert( 0 ); // we tried to remove a non-linked attribute.
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node->name == name )
+ return node;
+ }
+ return 0;
+}
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+#endif
+
+
+TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( strcmp( node->name.c_str(), name ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+
+
+#ifdef TIXML_USE_STL
+std::istream& operator>> (std::istream & in, TiXmlNode & base)
+{
+ TIXML_STRING tag;
+ tag.reserve( 8 * 1000 );
+ base.StreamIn( &in, &tag );
+
+ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
+ return in;
+}
+#endif
+
+
+#ifdef TIXML_USE_STL
+std::ostream& operator<< (std::ostream & out, const TiXmlNode & base)
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out << printer.Str();
+
+ return out;
+}
+
+
+std::string& operator<< (std::string& out, const TiXmlNode& base )
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out.append( printer.Str() );
+
+ return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const
+{
+ if ( node )
+ {
+ TiXmlElement* child = node->FirstChildElement();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlElement* child = node->FirstChildElement( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild();
+ for ( i=0;
+ child && i<count;
+ child = child->NextSibling(), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild( value );
+ for ( i=0;
+ child && i<count;
+ child = child->NextSibling( value ), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement();
+ for ( i=0;
+ child && i<count;
+ child = child->NextSiblingElement(), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement( value );
+ for ( i=0;
+ child && i<count;
+ child = child->NextSiblingElement( value ), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+bool TiXmlPrinter::VisitEnter( const TiXmlDocument& )
+{
+ return true;
+}
+
+bool TiXmlPrinter::VisitExit( const TiXmlDocument& )
+{
+ return true;
+}
+
+bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
+{
+ DoIndent();
+ buffer += "<";
+ buffer += element.Value();
+
+ for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() )
+ {
+ buffer += " ";
+ attrib->Print( 0, 0, &buffer );
+ }
+
+ if ( !element.FirstChild() )
+ {
+ buffer += " />";
+ DoLineBreak();
+ }
+ else
+ {
+ buffer += ">";
+ if ( element.FirstChild()->ToText()
+ && element.LastChild() == element.FirstChild()
+ && element.FirstChild()->ToText()->CDATA() == false )
+ {
+ simpleTextPrint = true;
+ // no DoLineBreak()!
+ }
+ else
+ {
+ DoLineBreak();
+ }
+ }
+ ++depth;
+ return true;
+}
+
+
+bool TiXmlPrinter::VisitExit( const TiXmlElement& element )
+{
+ --depth;
+ if ( !element.FirstChild() )
+ {
+ // nothing.
+ }
+ else
+ {
+ if ( simpleTextPrint )
+ {
+ simpleTextPrint = false;
+ }
+ else
+ {
+ DoIndent();
+ }
+ buffer += "</";
+ buffer += element.Value();
+ buffer += ">";
+ DoLineBreak();
+ }
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlText& text )
+{
+ if ( text.CDATA() )
+ {
+ DoIndent();
+ buffer += "<![CDATA[";
+ buffer += text.Value();
+ buffer += "]]>";
+ DoLineBreak();
+ }
+ else if ( simpleTextPrint )
+ {
+ TIXML_STRING str;
+ TiXmlBase::EncodeString( text.ValueTStr(), &str );
+ buffer += str;
+ }
+ else
+ {
+ DoIndent();
+ TIXML_STRING str;
+ TiXmlBase::EncodeString( text.ValueTStr(), &str );
+ buffer += str;
+ DoLineBreak();
+ }
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration )
+{
+ DoIndent();
+ declaration.Print( 0, 0, &buffer );
+ DoLineBreak();
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlComment& comment )
+{
+ DoIndent();
+ buffer += "<!--";
+ buffer += comment.Value();
+ buffer += "-->";
+ DoLineBreak();
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown )
+{
+ DoIndent();
+ buffer += "<";
+ buffer += unknown.Value();
+ buffer += ">";
+ DoLineBreak();
+ return true;
+}
+
diff --git a/mmc_updater/depends/tinyxml/tinyxml.h b/mmc_updater/depends/tinyxml/tinyxml.h
new file mode 100644
index 00000000..a3589e5b
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/tinyxml.h
@@ -0,0 +1,1805 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TINYXML_INCLUDED
+#define TINYXML_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4530 )
+#pragma warning( disable : 4786 )
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+// Help out windows:
+#if defined( _DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+#ifdef TIXML_USE_STL
+ #include <string>
+ #include <iostream>
+ #include <sstream>
+ #define TIXML_STRING std::string
+#else
+ #include "tinystr.h"
+ #define TIXML_STRING TiXmlString
+#endif
+
+// Deprecated library function hell. Compilers want to use the
+// new safe versions. This probably doesn't fully address the problem,
+// but it gets closer. There are too many compilers for me to fully
+// test. If you get compilation troubles, undefine TIXML_SAFE
+#define TIXML_SAFE
+
+#ifdef TIXML_SAFE
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ // Microsoft visual studio, version 2005 and higher.
+ #define TIXML_SNPRINTF _snprintf_s
+ #define TIXML_SSCANF sscanf_s
+ #elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
+ // Microsoft visual studio, version 6 and higher.
+ //#pragma message( "Using _sn* functions." )
+ #define TIXML_SNPRINTF _snprintf
+ #define TIXML_SSCANF sscanf
+ #elif defined(__GNUC__) && (__GNUC__ >= 3 )
+ // GCC version 3 and higher.s
+ //#warning( "Using sn* functions." )
+ #define TIXML_SNPRINTF snprintf
+ #define TIXML_SSCANF sscanf
+ #else
+ #define TIXML_SNPRINTF snprintf
+ #define TIXML_SSCANF sscanf
+ #endif
+#endif
+
+class TiXmlDocument;
+class TiXmlElement;
+class TiXmlComment;
+class TiXmlUnknown;
+class TiXmlAttribute;
+class TiXmlText;
+class TiXmlDeclaration;
+class TiXmlParsingData;
+
+const int TIXML_MAJOR_VERSION = 2;
+const int TIXML_MINOR_VERSION = 6;
+const int TIXML_PATCH_VERSION = 2;
+
+/* Internal structure for tracking location of items
+ in the XML file.
+*/
+struct TiXmlCursor
+{
+ TiXmlCursor() { Clear(); }
+ void Clear() { row = col = -1; }
+
+ int row; // 0 based.
+ int col; // 0 based.
+};
+
+
+/**
+ Implements the interface to the "Visitor pattern" (see the Accept() method.)
+ If you call the Accept() method, it requires being passed a TiXmlVisitor
+ class to handle callbacks. For nodes that contain other nodes (Document, Element)
+ you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
+ are simply called with Visit().
+
+ If you return 'true' from a Visit method, recursive parsing will continue. If you return
+ false, <b>no children of this node or its sibilings</b> will be Visited.
+
+ All flavors of Visit methods have a default implementation that returns 'true' (continue
+ visiting). You need to only override methods that are interesting to you.
+
+ Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.
+
+ You should never change the document from a callback.
+
+ @sa TiXmlNode::Accept()
+*/
+class TiXmlVisitor
+{
+public:
+ virtual ~TiXmlVisitor() {}
+
+ /// Visit a document.
+ virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; }
+ /// Visit a document.
+ virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; }
+
+ /// Visit an element.
+ virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; }
+ /// Visit an element.
+ virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; }
+
+ /// Visit a declaration
+ virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; }
+ /// Visit a text node
+ virtual bool Visit( const TiXmlText& /*text*/ ) { return true; }
+ /// Visit a comment node
+ virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; }
+ /// Visit an unknown node
+ virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; }
+};
+
+// Only used by Attribute::Query functions
+enum
+{
+ TIXML_SUCCESS,
+ TIXML_NO_ATTRIBUTE,
+ TIXML_WRONG_TYPE
+};
+
+
+// Used by the parsing routines.
+enum TiXmlEncoding
+{
+ TIXML_ENCODING_UNKNOWN,
+ TIXML_ENCODING_UTF8,
+ TIXML_ENCODING_LEGACY
+};
+
+const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
+
+/** TiXmlBase is a base class for every class in TinyXml.
+ It does little except to establish that TinyXml classes
+ can be printed and provide some utility functions.
+
+ In XML, the document and elements can contain
+ other elements and other types of nodes.
+
+ @verbatim
+ A Document can contain: Element (container or leaf)
+ Comment (leaf)
+ Unknown (leaf)
+ Declaration( leaf )
+
+ An Element can contain: Element (container or leaf)
+ Text (leaf)
+ Attributes (not on tree)
+ Comment (leaf)
+ Unknown (leaf)
+
+ A Decleration contains: Attributes (not on tree)
+ @endverbatim
+*/
+class TiXmlBase
+{
+ friend class TiXmlNode;
+ friend class TiXmlElement;
+ friend class TiXmlDocument;
+
+public:
+ TiXmlBase() : userData(0) {}
+ virtual ~TiXmlBase() {}
+
+ /** All TinyXml classes can print themselves to a filestream
+ or the string class (TiXmlString in non-STL mode, std::string
+ in STL mode.) Either or both cfile and str can be null.
+
+ This is a formatted print, and will insert
+ tabs and newlines.
+
+ (For an unformatted stream, use the << operator.)
+ */
+ virtual void Print( FILE* cfile, int depth ) const = 0;
+
+ /** The world does not agree on whether white space should be kept or
+ not. In order to make everyone happy, these global, static functions
+ are provided to set whether or not TinyXml will condense all white space
+ into a single space or not. The default is to condense. Note changing this
+ value is not thread safe.
+ */
+ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; }
+
+ /// Return the current white space setting.
+ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
+
+ /** Return the position, in the original source file, of this node or attribute.
+ The row and column are 1-based. (That is the first row and first column is
+ 1,1). If the returns values are 0 or less, then the parser does not have
+ a row and column value.
+
+ Generally, the row and column value will be set when the TiXmlDocument::Load(),
+ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
+ when the DOM was created from operator>>.
+
+ The values reflect the initial load. Once the DOM is modified programmatically
+ (by adding or changing nodes and attributes) the new values will NOT update to
+ reflect changes in the document.
+
+ There is a minor performance cost to computing the row and column. Computation
+ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
+
+ @sa TiXmlDocument::SetTabSize()
+ */
+ int Row() const { return location.row + 1; }
+ int Column() const { return location.col + 1; } ///< See Row()
+
+ void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data.
+ void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data.
+ const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data.
+
+ // Table that returs, for a given lead byte, the total number of bytes
+ // in the UTF-8 sequence.
+ static const int utf8ByteTable[256];
+
+ virtual const char* Parse( const char* p,
+ TiXmlParsingData* data,
+ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
+
+ /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc,
+ or they will be transformed into entities!
+ */
+ static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
+
+ enum
+ {
+ TIXML_NO_ERROR = 0,
+ TIXML_ERROR,
+ TIXML_ERROR_OPENING_FILE,
+ TIXML_ERROR_PARSING_ELEMENT,
+ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
+ TIXML_ERROR_READING_ELEMENT_VALUE,
+ TIXML_ERROR_READING_ATTRIBUTES,
+ TIXML_ERROR_PARSING_EMPTY,
+ TIXML_ERROR_READING_END_TAG,
+ TIXML_ERROR_PARSING_UNKNOWN,
+ TIXML_ERROR_PARSING_COMMENT,
+ TIXML_ERROR_PARSING_DECLARATION,
+ TIXML_ERROR_DOCUMENT_EMPTY,
+ TIXML_ERROR_EMBEDDED_NULL,
+ TIXML_ERROR_PARSING_CDATA,
+ TIXML_ERROR_DOCUMENT_TOP_ONLY,
+
+ TIXML_ERROR_STRING_COUNT
+ };
+
+protected:
+
+ static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
+
+ inline static bool IsWhiteSpace( char c )
+ {
+ return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
+ }
+ inline static bool IsWhiteSpace( int c )
+ {
+ if ( c < 256 )
+ return IsWhiteSpace( (char) c );
+ return false; // Again, only truly correct for English/Latin...but usually works.
+ }
+
+ #ifdef TIXML_USE_STL
+ static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
+ static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
+ #endif
+
+ /* Reads an XML name into the string provided. Returns
+ a pointer just past the last character of the name,
+ or 0 if the function has an error.
+ */
+ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
+
+ /* Reads text. Returns a pointer past the given end tag.
+ Wickedly complex options, but it keeps the (sensitive) code in one place.
+ */
+ static const char* ReadText( const char* in, // where to start
+ TIXML_STRING* text, // the string read
+ bool ignoreWhiteSpace, // whether to keep the white space
+ const char* endTag, // what ends this text
+ bool ignoreCase, // whether to ignore case in the end tag
+ TiXmlEncoding encoding ); // the current encoding
+
+ // If an entity has been found, transform it into a character.
+ static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
+
+ // Get a character, while interpreting entities.
+ // The length can be from 0 to 4 bytes.
+ inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
+ {
+ assert( p );
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ *length = utf8ByteTable[ *((const unsigned char*)p) ];
+ assert( *length >= 0 && *length < 5 );
+ }
+ else
+ {
+ *length = 1;
+ }
+
+ if ( *length == 1 )
+ {
+ if ( *p == '&' )
+ return GetEntity( p, _value, length, encoding );
+ *_value = *p;
+ return p+1;
+ }
+ else if ( *length )
+ {
+ //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe),
+ // and the null terminator isn't needed
+ for( int i=0; p[i] && i<*length; ++i ) {
+ _value[i] = p[i];
+ }
+ return p + (*length);
+ }
+ else
+ {
+ // Not valid text.
+ return 0;
+ }
+ }
+
+ // Return true if the next characters in the stream are any of the endTag sequences.
+ // Ignore case only works for english, and should only be relied on when comparing
+ // to English words: StringEqual( p, "version", true ) is fine.
+ static bool StringEqual( const char* p,
+ const char* endTag,
+ bool ignoreCase,
+ TiXmlEncoding encoding );
+
+ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
+
+ TiXmlCursor location;
+
+ /// Field containing a generic user pointer
+ void* userData;
+
+ // None of these methods are reliable for any language except English.
+ // Good for approximation, not great for accuracy.
+ static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
+ static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
+ inline static int ToLower( int v, TiXmlEncoding encoding )
+ {
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ if ( v < 128 ) return tolower( v );
+ return v;
+ }
+ else
+ {
+ return tolower( v );
+ }
+ }
+ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+private:
+ TiXmlBase( const TiXmlBase& ); // not implemented.
+ void operator=( const TiXmlBase& base ); // not allowed.
+
+ struct Entity
+ {
+ const char* str;
+ unsigned int strLength;
+ char chr;
+ };
+ enum
+ {
+ NUM_ENTITY = 5,
+ MAX_ENTITY_LENGTH = 6
+
+ };
+ static Entity entity[ NUM_ENTITY ];
+ static bool condenseWhiteSpace;
+};
+
+
+/** The parent class for everything in the Document Object Model.
+ (Except for attributes).
+ Nodes have siblings, a parent, and children. A node can be
+ in a document, or stand on its own. The type of a TiXmlNode
+ can be queried, and it can be cast to its more defined type.
+*/
+class TiXmlNode : public TiXmlBase
+{
+ friend class TiXmlDocument;
+ friend class TiXmlElement;
+
+public:
+ #ifdef TIXML_USE_STL
+
+ /** An input stream operator, for every class. Tolerant of newlines and
+ formatting, but doesn't expect them.
+ */
+ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
+
+ /** An output stream operator, for every class. Note that this outputs
+ without any newlines or formatting, as opposed to Print(), which
+ includes tabs and new lines.
+
+ The operator<< and operator>> are not completely symmetric. Writing
+ a node to a stream is very well defined. You'll get a nice stream
+ of output, without any extra whitespace or newlines.
+
+ But reading is not as well defined. (As it always is.) If you create
+ a TiXmlElement (for example) and read that from an input stream,
+ the text needs to define an element or junk will result. This is
+ true of all input streams, but it's worth keeping in mind.
+
+ A TiXmlDocument will read nodes until it reads a root element, and
+ all the children of that root element.
+ */
+ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
+
+ /// Appends the XML node or attribute to a std::string.
+ friend std::string& operator<< (std::string& out, const TiXmlNode& base );
+
+ #endif
+
+ /** The types of XML nodes supported by TinyXml. (All the
+ unsupported types are picked up by UNKNOWN.)
+ */
+ enum NodeType
+ {
+ TINYXML_DOCUMENT,
+ TINYXML_ELEMENT,
+ TINYXML_COMMENT,
+ TINYXML_UNKNOWN,
+ TINYXML_TEXT,
+ TINYXML_DECLARATION,
+ TINYXML_TYPECOUNT
+ };
+
+ virtual ~TiXmlNode();
+
+ /** The meaning of 'value' changes for the specific type of
+ TiXmlNode.
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+
+ The subclasses will wrap this function.
+ */
+ const char *Value() const { return value.c_str (); }
+
+ #ifdef TIXML_USE_STL
+ /** Return Value() as a std::string. If you only use STL,
+ this is more efficient than calling Value().
+ Only available in STL mode.
+ */
+ const std::string& ValueStr() const { return value; }
+ #endif
+
+ const TIXML_STRING& ValueTStr() const { return value; }
+
+ /** Changes the value of the node. Defined as:
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+ */
+ void SetValue(const char * _value) { value = _value;}
+
+ #ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetValue( const std::string& _value ) { value = _value; }
+ #endif
+
+ /// Delete all the children of this node. Does not affect 'this'.
+ void Clear();
+
+ /// One step up the DOM.
+ TiXmlNode* Parent() { return parent; }
+ const TiXmlNode* Parent() const { return parent; }
+
+ const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
+ TiXmlNode* FirstChild() { return firstChild; }
+ const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
+ /// The first child of this node with the matching 'value'. Will be null if none found.
+ TiXmlNode* FirstChild( const char * _value ) {
+ // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)
+ // call the method, cast the return back to non-const.
+ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
+ }
+ const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
+ TiXmlNode* LastChild() { return lastChild; }
+
+ const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children.
+ TiXmlNode* LastChild( const char * _value ) {
+ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /** An alternate way to walk the children of a node.
+ One way to iterate over nodes is:
+ @verbatim
+ for( child = parent->FirstChild(); child; child = child->NextSibling() )
+ @endverbatim
+
+ IterateChildren does the same thing with the syntax:
+ @verbatim
+ child = 0;
+ while( child = parent->IterateChildren( child ) )
+ @endverbatim
+
+ IterateChildren takes the previous child as input and finds
+ the next one. If the previous child is null, it returns the
+ first. IterateChildren will return null when done.
+ */
+ const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
+ TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
+ }
+
+ /// This flavor of IterateChildren searches for children with a particular 'value'
+ const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
+ TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ #endif
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
+
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+
+ NOTE: the node to be added is passed by pointer, and will be
+ henceforth owned (and deleted) by tinyXml. This method is efficient
+ and avoids an extra copy, but should be used with care as it
+ uses a different memory model than the other insert functions.
+
+ @sa InsertEndChild
+ */
+ TiXmlNode* LinkEndChild( TiXmlNode* addThis );
+
+ /** Add a new node related to this. Adds a child before the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
+
+ /** Add a new node related to this. Adds a child after the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
+
+ /** Replace a child of this node.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
+
+ /// Delete a child of this node.
+ bool RemoveChild( TiXmlNode* removeThis );
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling() const { return prev; }
+ TiXmlNode* PreviousSibling() { return prev; }
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling( const char * ) const;
+ TiXmlNode* PreviousSibling( const char *_prev ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* NextSibling() const { return next; }
+ TiXmlNode* NextSibling() { return next; }
+
+ /// Navigate to a sibling node with the given 'value'.
+ const TiXmlNode* NextSibling( const char * ) const;
+ TiXmlNode* NextSibling( const char* _next ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
+ }
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement() const;
+ TiXmlElement* NextSiblingElement() {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
+ }
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement( const char * ) const;
+ TiXmlElement* NextSiblingElement( const char *_next ) {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement() const;
+ TiXmlElement* FirstChildElement() {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
+ }
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement( const char * _value ) const;
+ TiXmlElement* FirstChildElement( const char * _value ) {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /** Query the type (as an enumerated value, above) of this node.
+ The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT,
+ TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION.
+ */
+ int Type() const { return type; }
+
+ /** Return a pointer to the Document this node lives in.
+ Returns null if not in a document.
+ */
+ const TiXmlDocument* GetDocument() const;
+ TiXmlDocument* GetDocument() {
+ return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
+ }
+
+ /// Returns true if this node has no children.
+ bool NoChildren() const { return !firstChild; }
+
+ virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ /** Create an exact duplicate of this node and return it. The memory must be deleted
+ by the caller.
+ */
+ virtual TiXmlNode* Clone() const = 0;
+
+ /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the
+ XML tree will be conditionally visited and the host will be called back
+ via the TiXmlVisitor interface.
+
+ This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
+ the XML for the callbacks, so the performance of TinyXML is unchanged by using this
+ interface versus any other.)
+
+ The interface has been based on ideas from:
+
+ - http://www.saxproject.org/
+ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+ Which are both good references for "visiting".
+
+ An example of using Accept():
+ @verbatim
+ TiXmlPrinter printer;
+ tinyxmlDoc.Accept( &printer );
+ const char* xmlcstr = printer.CStr();
+ @endverbatim
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
+
+protected:
+ TiXmlNode( NodeType _type );
+
+ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
+ // and the assignment operator.
+ void CopyTo( TiXmlNode* target ) const;
+
+ #ifdef TIXML_USE_STL
+ // The real work of the input operator.
+ virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
+ #endif
+
+ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
+ TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
+
+ TiXmlNode* parent;
+ NodeType type;
+
+ TiXmlNode* firstChild;
+ TiXmlNode* lastChild;
+
+ TIXML_STRING value;
+
+ TiXmlNode* prev;
+ TiXmlNode* next;
+
+private:
+ TiXmlNode( const TiXmlNode& ); // not implemented.
+ void operator=( const TiXmlNode& base ); // not allowed.
+};
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+ number of attributes, each with a unique name.
+
+ @note The attributes are not TiXmlNodes, since they are not
+ part of the tinyXML document object model. There are other
+ suggested ways to look at this problem.
+*/
+class TiXmlAttribute : public TiXmlBase
+{
+ friend class TiXmlAttributeSet;
+
+public:
+ /// Construct an empty attribute.
+ TiXmlAttribute() : TiXmlBase()
+ {
+ document = 0;
+ prev = next = 0;
+ }
+
+ #ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlAttribute( const std::string& _name, const std::string& _value )
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+ #endif
+
+ /// Construct an attribute with a name and value.
+ TiXmlAttribute( const char * _name, const char * _value )
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+
+ const char* Name() const { return name.c_str(); } ///< Return the name of this attribute.
+ const char* Value() const { return value.c_str(); } ///< Return the value of this attribute.
+ #ifdef TIXML_USE_STL
+ const std::string& ValueStr() const { return value; } ///< Return the value of this attribute.
+ #endif
+ int IntValue() const; ///< Return the value of this attribute, converted to an integer.
+ double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
+
+ // Get the tinyxml string representation
+ const TIXML_STRING& NameTStr() const { return name; }
+
+ /** QueryIntValue examines the value string. It is an alternative to the
+ IntValue() method with richer error checking.
+ If the value is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE.
+
+ A specialized but useful call. Note that for success it returns 0,
+ which is the opposite of almost all other TinyXml calls.
+ */
+ int QueryIntValue( int* _value ) const;
+ /// QueryDoubleValue examines the value string. See QueryIntValue().
+ int QueryDoubleValue( double* _value ) const;
+
+ void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute.
+ void SetValue( const char* _value ) { value = _value; } ///< Set the value.
+
+ void SetIntValue( int _value ); ///< Set the value from an integer.
+ void SetDoubleValue( double _value ); ///< Set the value from a double.
+
+ #ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetName( const std::string& _name ) { name = _name; }
+ /// STL std::string form.
+ void SetValue( const std::string& _value ) { value = _value; }
+ #endif
+
+ /// Get the next sibling attribute in the DOM. Returns null at end.
+ const TiXmlAttribute* Next() const;
+ TiXmlAttribute* Next() {
+ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() );
+ }
+
+ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
+ const TiXmlAttribute* Previous() const;
+ TiXmlAttribute* Previous() {
+ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() );
+ }
+
+ bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
+ bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; }
+ bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; }
+
+ /* Attribute parsing starts: first letter of the name
+ returns: the next char after the value end quote
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ // Prints this Attribute to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const {
+ Print( cfile, depth, 0 );
+ }
+ void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+
+ // [internal use]
+ // Set the document pointer so the attribute can report errors.
+ void SetDocument( TiXmlDocument* doc ) { document = doc; }
+
+private:
+ TiXmlAttribute( const TiXmlAttribute& ); // not implemented.
+ void operator=( const TiXmlAttribute& base ); // not allowed.
+
+ TiXmlDocument* document; // A pointer back to a document, for error reporting.
+ TIXML_STRING name;
+ TIXML_STRING value;
+ TiXmlAttribute* prev;
+ TiXmlAttribute* next;
+};
+
+
+/* A class used to manage a group of attributes.
+ It is only used internally, both by the ELEMENT and the DECLARATION.
+
+ The set can be changed transparent to the Element and Declaration
+ classes that use it, but NOT transparent to the Attribute
+ which has to implement a next() and previous() method. Which makes
+ it a bit problematic and prevents the use of STL.
+
+ This version is implemented with circular lists because:
+ - I like circular lists
+ - it demonstrates some independence from the (typical) doubly linked list.
+*/
+class TiXmlAttributeSet
+{
+public:
+ TiXmlAttributeSet();
+ ~TiXmlAttributeSet();
+
+ void Add( TiXmlAttribute* attribute );
+ void Remove( TiXmlAttribute* attribute );
+
+ const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+ TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+ const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+ TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+
+ TiXmlAttribute* Find( const char* _name ) const;
+ TiXmlAttribute* FindOrCreate( const char* _name );
+
+# ifdef TIXML_USE_STL
+ TiXmlAttribute* Find( const std::string& _name ) const;
+ TiXmlAttribute* FindOrCreate( const std::string& _name );
+# endif
+
+
+private:
+ //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
+ //*ME: this class must be also use a hidden/disabled copy-constructor !!!
+ TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed
+ void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute)
+
+ TiXmlAttribute sentinel;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+ and can contain other elements, text, comments, and unknowns.
+ Elements also contain an arbitrary number of attributes.
+*/
+class TiXmlElement : public TiXmlNode
+{
+public:
+ /// Construct an element.
+ TiXmlElement (const char * in_value);
+
+ #ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlElement( const std::string& _value );
+ #endif
+
+ TiXmlElement( const TiXmlElement& );
+
+ TiXmlElement& operator=( const TiXmlElement& base );
+
+ virtual ~TiXmlElement();
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ */
+ const char* Attribute( const char* name ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an integer,
+ the integer value will be put in the return 'i', if 'i'
+ is non-null.
+ */
+ const char* Attribute( const char* name, int* i ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an double,
+ the double value will be put in the return 'd', if 'd'
+ is non-null.
+ */
+ const char* Attribute( const char* name, double* d ) const;
+
+ /** QueryIntAttribute examines the attribute - it is an alternative to the
+ Attribute() method with richer error checking.
+ If the attribute is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE. If the attribute
+ does not exist, then TIXML_NO_ATTRIBUTE is returned.
+ */
+ int QueryIntAttribute( const char* name, int* _value ) const;
+ /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute().
+ int QueryUnsignedAttribute( const char* name, unsigned* _value ) const;
+ /** QueryBoolAttribute examines the attribute - see QueryIntAttribute().
+ Note that '1', 'true', or 'yes' are considered true, while '0', 'false'
+ and 'no' are considered false.
+ */
+ int QueryBoolAttribute( const char* name, bool* _value ) const;
+ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
+ int QueryDoubleAttribute( const char* name, double* _value ) const;
+ /// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
+ int QueryFloatAttribute( const char* name, float* _value ) const {
+ double d;
+ int result = QueryDoubleAttribute( name, &d );
+ if ( result == TIXML_SUCCESS ) {
+ *_value = (float)d;
+ }
+ return result;
+ }
+
+ #ifdef TIXML_USE_STL
+ /// QueryStringAttribute examines the attribute - see QueryIntAttribute().
+ int QueryStringAttribute( const char* name, std::string* _value ) const {
+ const char* cstr = Attribute( name );
+ if ( cstr ) {
+ *_value = std::string( cstr );
+ return TIXML_SUCCESS;
+ }
+ return TIXML_NO_ATTRIBUTE;
+ }
+
+ /** Template form of the attribute query which will try to read the
+ attribute into the specified type. Very easy, very powerful, but
+ be careful to make sure to call this with the correct type.
+
+ NOTE: This method doesn't work correctly for 'string' types that contain spaces.
+
+ @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
+ */
+ template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ std::stringstream sstream( node->ValueStr() );
+ sstream >> *outValue;
+ if ( !sstream.fail() )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+ }
+
+ int QueryValueAttribute( const std::string& name, std::string* outValue ) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+ *outValue = node->ValueStr();
+ return TIXML_SUCCESS;
+ }
+ #endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute( const char* name, const char * _value );
+
+ #ifdef TIXML_USE_STL
+ const std::string* Attribute( const std::string& name ) const;
+ const std::string* Attribute( const std::string& name, int* i ) const;
+ const std::string* Attribute( const std::string& name, double* d ) const;
+ int QueryIntAttribute( const std::string& name, int* _value ) const;
+ int QueryDoubleAttribute( const std::string& name, double* _value ) const;
+
+ /// STL std::string form.
+ void SetAttribute( const std::string& name, const std::string& _value );
+ ///< STL std::string form.
+ void SetAttribute( const std::string& name, int _value );
+ ///< STL std::string form.
+ void SetDoubleAttribute( const std::string& name, double value );
+ #endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute( const char * name, int value );
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetDoubleAttribute( const char * name, double value );
+
+ /** Deletes an attribute with the given name.
+ */
+ void RemoveAttribute( const char * name );
+ #ifdef TIXML_USE_STL
+ void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
+ #endif
+
+ const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
+ TiXmlAttribute* FirstAttribute() { return attributeSet.First(); }
+ const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
+ TiXmlAttribute* LastAttribute() { return attributeSet.Last(); }
+
+ /** Convenience function for easy access to the text inside an element. Although easy
+ and concise, GetText() is limited compared to getting the TiXmlText child
+ and accessing it directly.
+
+ If the first child of 'this' is a TiXmlText, the GetText()
+ returns the character string of the Text node, else null is returned.
+
+ This is a convenient method for getting the text of simple contained text:
+ @verbatim
+ <foo>This is text</foo>
+ const char* str = fooElement->GetText();
+ @endverbatim
+
+ 'str' will be a pointer to "This is text".
+
+ Note that this function can be misleading. If the element foo was created from
+ this XML:
+ @verbatim
+ <foo><b>This is text</b></foo>
+ @endverbatim
+
+ then the value of str would be null. The first child node isn't a text node, it is
+ another element. From this XML:
+ @verbatim
+ <foo>This is <b>text</b></foo>
+ @endverbatim
+ GetText() will return "This is ".
+
+ WARNING: GetText() accesses a child node - don't become confused with the
+ similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are
+ safe type casts on the referenced node.
+ */
+ const char* GetText() const;
+
+ /// Creates a new Element and returns it - the returned element is a copy.
+ virtual TiXmlNode* Clone() const;
+ // Print the Element to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /* Attribtue parsing starts: next char past '<'
+ returns: next char past '>'
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+
+ void CopyTo( TiXmlElement* target ) const;
+ void ClearThis(); // like clear, but initializes 'this' object as well
+
+ // Used to be public [internal use]
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+ /* [internal use]
+ Reads the "value" of the element -- another element, or text.
+ This should terminate with the current end tag.
+ */
+ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+private:
+ TiXmlAttributeSet attributeSet;
+};
+
+
+/** An XML comment.
+*/
+class TiXmlComment : public TiXmlNode
+{
+public:
+ /// Constructs an empty comment.
+ TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {}
+ /// Construct a comment from text.
+ TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {
+ SetValue( _value );
+ }
+ TiXmlComment( const TiXmlComment& );
+ TiXmlComment& operator=( const TiXmlComment& base );
+
+ virtual ~TiXmlComment() {}
+
+ /// Returns a copy of this Comment.
+ virtual TiXmlNode* Clone() const;
+ // Write this Comment to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /* Attribtue parsing starts: at the ! of the !--
+ returns: next char past '>'
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+ void CopyTo( TiXmlComment* target ) const;
+
+ // used to be public
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+// virtual void StreamOut( TIXML_OSTREAM * out ) const;
+
+private:
+
+};
+
+
+/** XML text. A text node can have 2 ways to output the next. "normal" output
+ and CDATA. It will default to the mode it was parsed from the XML file and
+ you generally want to leave it alone, but you can change the output mode with
+ SetCDATA() and query it with CDATA().
+*/
+class TiXmlText : public TiXmlNode
+{
+ friend class TiXmlElement;
+public:
+ /** Constructor for text element. By default, it is treated as
+ normal, encoded text. If you want it be output as a CDATA text
+ element, set the parameter _cdata to 'true'
+ */
+ TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
+ {
+ SetValue( initValue );
+ cdata = false;
+ }
+ virtual ~TiXmlText() {}
+
+ #ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
+ {
+ SetValue( initValue );
+ cdata = false;
+ }
+ #endif
+
+ TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); }
+ TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; }
+
+ // Write this text object to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /// Queries whether this represents text using a CDATA section.
+ bool CDATA() const { return cdata; }
+ /// Turns on or off a CDATA representation of text.
+ void SetCDATA( bool _cdata ) { cdata = _cdata; }
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+ /// [internal use] Creates a new Element and returns it.
+ virtual TiXmlNode* Clone() const;
+ void CopyTo( TiXmlText* target ) const;
+
+ bool Blank() const; // returns true if all white space and new lines
+ // [internal use]
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+ bool cdata; // true if this should be input and output as a CDATA style text element
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+ @verbatim
+ <?xml version="1.0" standalone="yes"?>
+ @endverbatim
+
+ TinyXml will happily read or write files without a declaration,
+ however. There are 3 possible attributes to the declaration:
+ version, encoding, and standalone.
+
+ Note: In this version of the code, the attributes are
+ handled as special cases, not generic attributes, simply
+ because there can only be at most 3 and they are always the same.
+*/
+class TiXmlDeclaration : public TiXmlNode
+{
+public:
+ /// Construct an empty declaration.
+ TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {}
+
+#ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone );
+#endif
+
+ /// Construct.
+ TiXmlDeclaration( const char* _version,
+ const char* _encoding,
+ const char* _standalone );
+
+ TiXmlDeclaration( const TiXmlDeclaration& copy );
+ TiXmlDeclaration& operator=( const TiXmlDeclaration& copy );
+
+ virtual ~TiXmlDeclaration() {}
+
+ /// Version. Will return an empty string if none was found.
+ const char *Version() const { return version.c_str (); }
+ /// Encoding. Will return an empty string if none was found.
+ const char *Encoding() const { return encoding.c_str (); }
+ /// Is this a standalone document?
+ const char *Standalone() const { return standalone.c_str (); }
+
+ /// Creates a copy of this Declaration and returns it.
+ virtual TiXmlNode* Clone() const;
+ // Print this declaration to a FILE stream.
+ virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+ virtual void Print( FILE* cfile, int depth ) const {
+ Print( cfile, depth, 0 );
+ }
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+ void CopyTo( TiXmlDeclaration* target ) const;
+ // used to be public
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+
+ TIXML_STRING version;
+ TIXML_STRING encoding;
+ TIXML_STRING standalone;
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+ unknown. It is a tag of text, but should not be modified.
+ It will be written back to the XML, unchanged, when the file
+ is saved.
+
+ DTD tags get thrown into TiXmlUnknowns.
+*/
+class TiXmlUnknown : public TiXmlNode
+{
+public:
+ TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {}
+ virtual ~TiXmlUnknown() {}
+
+ TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); }
+ TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; }
+
+ /// Creates a copy of this Unknown and returns it.
+ virtual TiXmlNode* Clone() const;
+ // Print this Unknown to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected:
+ void CopyTo( TiXmlUnknown* target ) const;
+
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+
+};
+
+
+/** Always the top level node. A document binds together all the
+ XML pieces. It can be saved, loaded, and printed to the screen.
+ The 'value' of a document node is the xml file name.
+*/
+class TiXmlDocument : public TiXmlNode
+{
+public:
+ /// Create an empty document, that has no name.
+ TiXmlDocument();
+ /// Create a document with a name. The name of the document is also the filename of the xml.
+ TiXmlDocument( const char * documentName );
+
+ #ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDocument( const std::string& documentName );
+ #endif
+
+ TiXmlDocument( const TiXmlDocument& copy );
+ TiXmlDocument& operator=( const TiXmlDocument& copy );
+
+ virtual ~TiXmlDocument() {}
+
+ /** Load a file using the current document value.
+ Returns true if successful. Will delete any existing
+ document data before loading.
+ */
+ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the current document value. Returns true if successful.
+ bool SaveFile() const;
+ /// Load a file using the given filename. Returns true if successful.
+ bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the given filename. Returns true if successful.
+ bool SaveFile( const char * filename ) const;
+ /** Load a file using the given FILE*. Returns true if successful. Note that this method
+ doesn't stream - the entire object pointed at by the FILE*
+ will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
+ file location. Streaming may be added in the future.
+ */
+ bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the given FILE*. Returns true if successful.
+ bool SaveFile( FILE* ) const;
+
+ #ifdef TIXML_USE_STL
+ bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
+ {
+ return LoadFile( filename.c_str(), encoding );
+ }
+ bool SaveFile( const std::string& filename ) const ///< STL std::string version.
+ {
+ return SaveFile( filename.c_str() );
+ }
+ #endif
+
+ /** Parse the given null terminated block of xml data. Passing in an encoding to this
+ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
+ to use that encoding, regardless of what TinyXml might otherwise try to detect.
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+
+ /** Get the root element -- the only top level element -- of the document.
+ In well formed XML, there should only be one. TinyXml is tolerant of
+ multiple elements at the document level.
+ */
+ const TiXmlElement* RootElement() const { return FirstChildElement(); }
+ TiXmlElement* RootElement() { return FirstChildElement(); }
+
+ /** If an error occurs, Error will be set to true. Also,
+ - The ErrorId() will contain the integer identifier of the error (not generally useful)
+ - The ErrorDesc() method will return the name of the error. (very useful)
+ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
+ */
+ bool Error() const { return error; }
+
+ /// Contains a textual (english) description of the error if one occurs.
+ const char * ErrorDesc() const { return errorDesc.c_str (); }
+
+ /** Generally, you probably want the error string ( ErrorDesc() ). But if you
+ prefer the ErrorId, this function will fetch it.
+ */
+ int ErrorId() const { return errorId; }
+
+ /** Returns the location (if known) of the error. The first column is column 1,
+ and the first row is row 1. A value of 0 means the row and column wasn't applicable
+ (memory errors, for example, have no row/column) or the parser lost the error. (An
+ error in the error reporting, in that case.)
+
+ @sa SetTabSize, Row, Column
+ */
+ int ErrorRow() const { return errorLocation.row+1; }
+ int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
+
+ /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
+ to report the correct values for row and column. It does not change the output
+ or input in any way.
+
+ By calling this method, with a tab size
+ greater than 0, the row and column of each node and attribute is stored
+ when the file is loaded. Very useful for tracking the DOM back in to
+ the source file.
+
+ The tab size is required for calculating the location of nodes. If not
+ set, the default of 4 is used. The tabsize is set per document. Setting
+ the tabsize to 0 disables row/column tracking.
+
+ Note that row and column tracking is not supported when using operator>>.
+
+ The tab size needs to be enabled before the parse or load. Correct usage:
+ @verbatim
+ TiXmlDocument doc;
+ doc.SetTabSize( 8 );
+ doc.Load( "myfile.xml" );
+ @endverbatim
+
+ @sa Row, Column
+ */
+ void SetTabSize( int _tabsize ) { tabsize = _tabsize; }
+
+ int TabSize() const { return tabsize; }
+
+ /** If you have handled the error, it can be reset with this call. The error
+ state is automatically cleared if you Parse a new XML block.
+ */
+ void ClearError() { error = false;
+ errorId = 0;
+ errorDesc = "";
+ errorLocation.row = errorLocation.col = 0;
+ //errorLocation.last = 0;
+ }
+
+ /** Write the document to standard out using formatted printing ("pretty print"). */
+ void Print() const { Print( stdout, 0 ); }
+
+ /* Write the document to a string using formatted printing ("pretty print"). This
+ will allocate a character array (new char[]) and return it as a pointer. The
+ calling code pust call delete[] on the return char* to avoid a memory leak.
+ */
+ //char* PrintToMemory() const;
+
+ /// Print this Document to a FILE stream.
+ virtual void Print( FILE* cfile, int depth = 0 ) const;
+ // [internal use]
+ void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+ virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+ // [internal use]
+ virtual TiXmlNode* Clone() const;
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+ void CopyTo( TiXmlDocument* target ) const;
+
+ bool error;
+ int errorId;
+ TIXML_STRING errorDesc;
+ int tabsize;
+ TiXmlCursor errorLocation;
+ bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write.
+};
+
+
+/**
+ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
+ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
+ DOM structure. It is a separate utility class.
+
+ Take an example:
+ @verbatim
+ <Document>
+ <Element attributeA = "valueA">
+ <Child attributeB = "value1" />
+ <Child attributeB = "value2" />
+ </Element>
+ <Document>
+ @endverbatim
+
+ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+ easy to write a *lot* of code that looks like:
+
+ @verbatim
+ TiXmlElement* root = document.FirstChildElement( "Document" );
+ if ( root )
+ {
+ TiXmlElement* element = root->FirstChildElement( "Element" );
+ if ( element )
+ {
+ TiXmlElement* child = element->FirstChildElement( "Child" );
+ if ( child )
+ {
+ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
+ if ( child2 )
+ {
+ // Finally do something useful.
+ @endverbatim
+
+ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
+ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
+ and correct to use:
+
+ @verbatim
+ TiXmlHandle docHandle( &document );
+ TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
+ if ( child2 )
+ {
+ // do something useful
+ @endverbatim
+
+ Which is MUCH more concise and useful.
+
+ It is also safe to copy handles - internally they are nothing more than node pointers.
+ @verbatim
+ TiXmlHandle handleCopy = handle;
+ @endverbatim
+
+ What they should not be used for is iteration:
+
+ @verbatim
+ int i=0;
+ while ( true )
+ {
+ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement();
+ if ( !child )
+ break;
+ // do something
+ ++i;
+ }
+ @endverbatim
+
+ It seems reasonable, but it is in fact two embedded while loops. The Child method is
+ a linear walk to find the element, so this code would iterate much more than it needs
+ to. Instead, prefer:
+
+ @verbatim
+ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement();
+
+ for( child; child; child=child->NextSiblingElement() )
+ {
+ // do something
+ }
+ @endverbatim
+*/
+class TiXmlHandle
+{
+public:
+ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+ TiXmlHandle( TiXmlNode* _node ) { this->node = _node; }
+ /// Copy constructor
+ TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; }
+ TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; }
+
+ /// Return a handle to the first child node.
+ TiXmlHandle FirstChild() const;
+ /// Return a handle to the first child node with the given name.
+ TiXmlHandle FirstChild( const char * value ) const;
+ /// Return a handle to the first child element.
+ TiXmlHandle FirstChildElement() const;
+ /// Return a handle to the first child element with the given name.
+ TiXmlHandle FirstChildElement( const char * value ) const;
+
+ /** Return a handle to the "index" child with the given name.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child( const char* value, int index ) const;
+ /** Return a handle to the "index" child.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child( int index ) const;
+ /** Return a handle to the "index" child element with the given name.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement( const char* value, int index ) const;
+ /** Return a handle to the "index" child element.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement( int index ) const;
+
+ #ifdef TIXML_USE_STL
+ TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); }
+ TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); }
+
+ TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); }
+ TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); }
+ #endif
+
+ /** Return the handle as a TiXmlNode. This may return null.
+ */
+ TiXmlNode* ToNode() const { return node; }
+ /** Return the handle as a TiXmlElement. This may return null.
+ */
+ TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
+ /** Return the handle as a TiXmlText. This may return null.
+ */
+ TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
+ /** Return the handle as a TiXmlUnknown. This may return null.
+ */
+ TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
+
+ /** @deprecated use ToNode.
+ Return the handle as a TiXmlNode. This may return null.
+ */
+ TiXmlNode* Node() const { return ToNode(); }
+ /** @deprecated use ToElement.
+ Return the handle as a TiXmlElement. This may return null.
+ */
+ TiXmlElement* Element() const { return ToElement(); }
+ /** @deprecated use ToText()
+ Return the handle as a TiXmlText. This may return null.
+ */
+ TiXmlText* Text() const { return ToText(); }
+ /** @deprecated use ToUnknown()
+ Return the handle as a TiXmlUnknown. This may return null.
+ */
+ TiXmlUnknown* Unknown() const { return ToUnknown(); }
+
+private:
+ TiXmlNode* node;
+};
+
+
+/** Print to memory functionality. The TiXmlPrinter is useful when you need to:
+
+ -# Print to memory (especially in non-STL mode)
+ -# Control formatting (line endings, etc.)
+
+ When constructed, the TiXmlPrinter is in its default "pretty printing" mode.
+ Before calling Accept() you can call methods to control the printing
+ of the XML document. After TiXmlNode::Accept() is called, the printed document can
+ be accessed via the CStr(), Str(), and Size() methods.
+
+ TiXmlPrinter uses the Visitor API.
+ @verbatim
+ TiXmlPrinter printer;
+ printer.SetIndent( "\t" );
+
+ doc.Accept( &printer );
+ fprintf( stdout, "%s", printer.CStr() );
+ @endverbatim
+*/
+class TiXmlPrinter : public TiXmlVisitor
+{
+public:
+ TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),
+ buffer(), indent( " " ), lineBreak( "\n" ) {}
+
+ virtual bool VisitEnter( const TiXmlDocument& doc );
+ virtual bool VisitExit( const TiXmlDocument& doc );
+
+ virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
+ virtual bool VisitExit( const TiXmlElement& element );
+
+ virtual bool Visit( const TiXmlDeclaration& declaration );
+ virtual bool Visit( const TiXmlText& text );
+ virtual bool Visit( const TiXmlComment& comment );
+ virtual bool Visit( const TiXmlUnknown& unknown );
+
+ /** Set the indent characters for printing. By default 4 spaces
+ but tab (\t) is also useful, or null/empty string for no indentation.
+ */
+ void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; }
+ /// Query the indention string.
+ const char* Indent() { return indent.c_str(); }
+ /** Set the line breaking string. By default set to newline (\n).
+ Some operating systems prefer other characters, or can be
+ set to the null/empty string for no indenation.
+ */
+ void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; }
+ /// Query the current line breaking string.
+ const char* LineBreak() { return lineBreak.c_str(); }
+
+ /** Switch over to "stream printing" which is the most dense formatting without
+ linebreaks. Common when the XML is needed for network transmission.
+ */
+ void SetStreamPrinting() { indent = "";
+ lineBreak = "";
+ }
+ /// Return the result.
+ const char* CStr() { return buffer.c_str(); }
+ /// Return the length of the result string.
+ size_t Size() { return buffer.size(); }
+
+ #ifdef TIXML_USE_STL
+ /// Return the result.
+ const std::string& Str() { return buffer; }
+ #endif
+
+private:
+ void DoIndent() {
+ for( int i=0; i<depth; ++i )
+ buffer += indent;
+ }
+ void DoLineBreak() {
+ buffer += lineBreak;
+ }
+
+ int depth;
+ bool simpleTextPrint;
+ TIXML_STRING buffer;
+ TIXML_STRING indent;
+ TIXML_STRING lineBreak;
+};
+
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+#endif
diff --git a/mmc_updater/depends/tinyxml/tinyxmlerror.cpp b/mmc_updater/depends/tinyxml/tinyxmlerror.cpp
new file mode 100644
index 00000000..538c21d0
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/tinyxmlerror.cpp
@@ -0,0 +1,52 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml.h"
+
+// The goal of the seperate error file is to make the first
+// step towards localization. tinyxml (currently) only supports
+// english error messages, but the could now be translated.
+//
+// It also cleans up the code a bit.
+//
+
+const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] =
+{
+ "No error",
+ "Error",
+ "Failed to open file",
+ "Error parsing Element.",
+ "Failed to read Element name",
+ "Error reading Element value.",
+ "Error reading Attributes.",
+ "Error: empty tag.",
+ "Error reading end tag.",
+ "Error parsing Unknown.",
+ "Error parsing Comment.",
+ "Error parsing Declaration.",
+ "Error document empty.",
+ "Error null (0) or unexpected EOF found in input stream.",
+ "Error parsing CDATA.",
+ "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
+};
diff --git a/mmc_updater/depends/tinyxml/tinyxmlparser.cpp b/mmc_updater/depends/tinyxml/tinyxmlparser.cpp
new file mode 100644
index 00000000..81b7eae9
--- /dev/null
+++ b/mmc_updater/depends/tinyxml/tinyxmlparser.cpp
@@ -0,0 +1,1638 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include <ctype.h>
+#include <stddef.h>
+
+#include "tinyxml.h"
+
+//#define DEBUG_PARSER
+#if defined( DEBUG_PARSER )
+# if defined( DEBUG ) && defined( _MSC_VER )
+# include <windows.h>
+# define TIXML_LOG OutputDebugString
+# else
+# define TIXML_LOG printf
+# endif
+#endif
+
+// Note tha "PutString" hardcodes the same list. This
+// is less flexible than it appears. Changing the entries
+// or order will break putstring.
+TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] =
+{
+ { "&amp;", 5, '&' },
+ { "&lt;", 4, '<' },
+ { "&gt;", 4, '>' },
+ { "&quot;", 6, '\"' },
+ { "&apos;", 6, '\'' }
+};
+
+// Bunch of unicode info at:
+// http://www.unicode.org/faq/utf_bom.html
+// Including the basic of this table, which determines the #bytes in the
+// sequence from the lead byte. 1 placed for invalid sequences --
+// although the result will be junk, pass it through as much as possible.
+// Beware of the non-characters in UTF-8:
+// ef bb bf (Microsoft "lead bytes")
+// ef bf be
+// ef bf bf
+
+const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+const int TiXmlBase::utf8ByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte
+ 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
+};
+
+
+void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+ const unsigned long BYTE_MASK = 0xBF;
+ const unsigned long BYTE_MARK = 0x80;
+ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+ if (input < 0x80)
+ *length = 1;
+ else if ( input < 0x800 )
+ *length = 2;
+ else if ( input < 0x10000 )
+ *length = 3;
+ else if ( input < 0x200000 )
+ *length = 4;
+ else
+ { *length = 0; return; } // This code won't covert this correctly anyway.
+
+ output += *length;
+
+ // Scary scary fall throughs.
+ switch (*length)
+ {
+ case 4:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 3:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 2:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 1:
+ --output;
+ *output = (char)(input | FIRST_BYTE_MARK[*length]);
+ }
+}
+
+
+/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
+{
+ // This will only work for low-ascii, everything else is assumed to be a valid
+ // letter. I'm not sure this is the best approach, but it is quite tricky trying
+ // to figure out alhabetical vs. not across encoding. So take a very
+ // conservative approach.
+
+// if ( encoding == TIXML_ENCODING_UTF8 )
+// {
+ if ( anyByte < 127 )
+ return isalpha( anyByte );
+ else
+ return 1; // What else to do? The unicode set is huge...get the english ones right.
+// }
+// else
+// {
+// return isalpha( anyByte );
+// }
+}
+
+
+/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
+{
+ // This will only work for low-ascii, everything else is assumed to be a valid
+ // letter. I'm not sure this is the best approach, but it is quite tricky trying
+ // to figure out alhabetical vs. not across encoding. So take a very
+ // conservative approach.
+
+// if ( encoding == TIXML_ENCODING_UTF8 )
+// {
+ if ( anyByte < 127 )
+ return isalnum( anyByte );
+ else
+ return 1; // What else to do? The unicode set is huge...get the english ones right.
+// }
+// else
+// {
+// return isalnum( anyByte );
+// }
+}
+
+
+class TiXmlParsingData
+{
+ friend class TiXmlDocument;
+ public:
+ void Stamp( const char* now, TiXmlEncoding encoding );
+
+ const TiXmlCursor& Cursor() const { return cursor; }
+
+ private:
+ // Only used by the document!
+ TiXmlParsingData( const char* start, int _tabsize, int row, int col )
+ {
+ assert( start );
+ stamp = start;
+ tabsize = _tabsize;
+ cursor.row = row;
+ cursor.col = col;
+ }
+
+ TiXmlCursor cursor;
+ const char* stamp;
+ int tabsize;
+};
+
+
+void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
+{
+ assert( now );
+
+ // Do nothing if the tabsize is 0.
+ if ( tabsize < 1 )
+ {
+ return;
+ }
+
+ // Get the current row, column.
+ int row = cursor.row;
+ int col = cursor.col;
+ const char* p = stamp;
+ assert( p );
+
+ while ( p < now )
+ {
+ // Treat p as unsigned, so we have a happy compiler.
+ const unsigned char* pU = (const unsigned char*)p;
+
+ // Code contributed by Fletcher Dunn: (modified by lee)
+ switch (*pU) {
+ case 0:
+ // We *should* never get here, but in case we do, don't
+ // advance past the terminating null character, ever
+ return;
+
+ case '\r':
+ // bump down to the next line
+ ++row;
+ col = 0;
+ // Eat the character
+ ++p;
+
+ // Check for \r\n sequence, and treat this as a single character
+ if (*p == '\n') {
+ ++p;
+ }
+ break;
+
+ case '\n':
+ // bump down to the next line
+ ++row;
+ col = 0;
+
+ // Eat the character
+ ++p;
+
+ // Check for \n\r sequence, and treat this as a single
+ // character. (Yes, this bizarre thing does occur still
+ // on some arcane platforms...)
+ if (*p == '\r') {
+ ++p;
+ }
+ break;
+
+ case '\t':
+ // Eat the character
+ ++p;
+
+ // Skip to next tab stop
+ col = (col / tabsize + 1) * tabsize;
+ break;
+
+ case TIXML_UTF_LEAD_0:
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ if ( *(p+1) && *(p+2) )
+ {
+ // In these cases, don't advance the column. These are
+ // 0-width spaces.
+ if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 )
+ p += 3;
+ else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU )
+ p += 3;
+ else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU )
+ p += 3;
+ else
+ { p +=3; ++col; } // A normal character.
+ }
+ }
+ else
+ {
+ ++p;
+ ++col;
+ }
+ break;
+
+ default:
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ // Eat the 1 to 4 byte utf8 character.
+ int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)];
+ if ( step == 0 )
+ step = 1; // Error case from bad encoding, but handle gracefully.
+ p += step;
+
+ // Just advance one column, of course.
+ ++col;
+ }
+ else
+ {
+ ++p;
+ ++col;
+ }
+ break;
+ }
+ }
+ cursor.row = row;
+ cursor.col = col;
+ assert( cursor.row >= -1 );
+ assert( cursor.col >= -1 );
+ stamp = p;
+ assert( stamp );
+}
+
+
+const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
+{
+ if ( !p || !*p )
+ {
+ return 0;
+ }
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ while ( *p )
+ {
+ const unsigned char* pU = (const unsigned char*)p;
+
+ // Skip the stupid Microsoft UTF-8 Byte order marks
+ if ( *(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==TIXML_UTF_LEAD_1
+ && *(pU+2)==TIXML_UTF_LEAD_2 )
+ {
+ p += 3;
+ continue;
+ }
+ else if(*(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==0xbfU
+ && *(pU+2)==0xbeU )
+ {
+ p += 3;
+ continue;
+ }
+ else if(*(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==0xbfU
+ && *(pU+2)==0xbfU )
+ {
+ p += 3;
+ continue;
+ }
+
+ if ( IsWhiteSpace( *p ) ) // Still using old rules for white space.
+ ++p;
+ else
+ break;
+ }
+ }
+ else
+ {
+ while ( *p && IsWhiteSpace( *p ) )
+ ++p;
+ }
+
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag )
+{
+ for( ;; )
+ {
+ if ( !in->good() ) return false;
+
+ int c = in->peek();
+ // At this scope, we can't get to a document. So fail silently.
+ if ( !IsWhiteSpace( c ) || c <= 0 )
+ return true;
+
+ *tag += (char) in->get();
+ }
+}
+
+/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag )
+{
+ //assert( character > 0 && character < 128 ); // else it won't work in utf-8
+ while ( in->good() )
+ {
+ int c = in->peek();
+ if ( c == character )
+ return true;
+ if ( c <= 0 ) // Silent failure: can't get document at this scope
+ return false;
+
+ in->get();
+ *tag += (char) c;
+ }
+ return false;
+}
+#endif
+
+// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The
+// "assign" optimization removes over 10% of the execution time.
+//
+const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
+{
+ // Oddly, not supported on some comilers,
+ //name->clear();
+ // So use this:
+ *name = "";
+ assert( p );
+
+ // Names start with letters or underscores.
+ // Of course, in unicode, tinyxml has no idea what a letter *is*. The
+ // algorithm is generous.
+ //
+ // After that, they can be letters, underscores, numbers,
+ // hyphens, or colons. (Colons are valid ony for namespaces,
+ // but tinyxml can't tell namespaces from names.)
+ if ( p && *p
+ && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
+ {
+ const char* start = p;
+ while( p && *p
+ && ( IsAlphaNum( (unsigned char ) *p, encoding )
+ || *p == '_'
+ || *p == '-'
+ || *p == '.'
+ || *p == ':' ) )
+ {
+ //(*name) += *p; // expensive
+ ++p;
+ }
+ if ( p-start > 0 ) {
+ name->assign( start, p-start );
+ }
+ return p;
+ }
+ return 0;
+}
+
+const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
+{
+ // Presume an entity, and pull it out.
+ TIXML_STRING ent;
+ int i;
+ *length = 0;
+
+ if ( *(p+1) && *(p+1) == '#' && *(p+2) )
+ {
+ unsigned long ucs = 0;
+ ptrdiff_t delta = 0;
+ unsigned mult = 1;
+
+ if ( *(p+2) == 'x' )
+ {
+ // Hexadecimal.
+ if ( !*(p+3) ) return 0;
+
+ const char* q = p+3;
+ q = strchr( q, ';' );
+
+ if ( !q || !*q ) return 0;
+
+ delta = q-p;
+ --q;
+
+ while ( *q != 'x' )
+ {
+ if ( *q >= '0' && *q <= '9' )
+ ucs += mult * (*q - '0');
+ else if ( *q >= 'a' && *q <= 'f' )
+ ucs += mult * (*q - 'a' + 10);
+ else if ( *q >= 'A' && *q <= 'F' )
+ ucs += mult * (*q - 'A' + 10 );
+ else
+ return 0;
+ mult *= 16;
+ --q;
+ }
+ }
+ else
+ {
+ // Decimal.
+ if ( !*(p+2) ) return 0;
+
+ const char* q = p+2;
+ q = strchr( q, ';' );
+
+ if ( !q || !*q ) return 0;
+
+ delta = q-p;
+ --q;
+
+ while ( *q != '#' )
+ {
+ if ( *q >= '0' && *q <= '9' )
+ ucs += mult * (*q - '0');
+ else
+ return 0;
+ mult *= 10;
+ --q;
+ }
+ }
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ // convert the UCS to UTF-8
+ ConvertUTF32ToUTF8( ucs, value, length );
+ }
+ else
+ {
+ *value = (char)ucs;
+ *length = 1;
+ }
+ return p + delta + 1;
+ }
+
+ // Now try to match it.
+ for( i=0; i<NUM_ENTITY; ++i )
+ {
+ if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
+ {
+ assert( strlen( entity[i].str ) == entity[i].strLength );
+ *value = entity[i].chr;
+ *length = 1;
+ return ( p + entity[i].strLength );
+ }
+ }
+
+ // So it wasn't an entity, its unrecognized, or something like that.
+ *value = *p; // Don't put back the last one, since we return it!
+ //*length = 1; // Leave unrecognized entities - this doesn't really work.
+ // Just writes strange XML.
+ return p+1;
+}
+
+
+bool TiXmlBase::StringEqual( const char* p,
+ const char* tag,
+ bool ignoreCase,
+ TiXmlEncoding encoding )
+{
+ assert( p );
+ assert( tag );
+ if ( !p || !*p )
+ {
+ assert( 0 );
+ return false;
+ }
+
+ const char* q = p;
+
+ if ( ignoreCase )
+ {
+ while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
+ {
+ ++q;
+ ++tag;
+ }
+
+ if ( *tag == 0 )
+ return true;
+ }
+ else
+ {
+ while ( *q && *tag && *q == *tag )
+ {
+ ++q;
+ ++tag;
+ }
+
+ if ( *tag == 0 ) // Have we found the end of the tag, and everything equal?
+ return true;
+ }
+ return false;
+}
+
+const char* TiXmlBase::ReadText( const char* p,
+ TIXML_STRING * text,
+ bool trimWhiteSpace,
+ const char* endTag,
+ bool caseInsensitive,
+ TiXmlEncoding encoding )
+{
+ *text = "";
+ if ( !trimWhiteSpace // certain tags always keep whitespace
+ || !condenseWhiteSpace ) // if true, whitespace is always kept
+ {
+ // Keep all the white space.
+ while ( p && *p
+ && !StringEqual( p, endTag, caseInsensitive, encoding )
+ )
+ {
+ int len;
+ char cArr[4] = { 0, 0, 0, 0 };
+ p = GetChar( p, cArr, &len, encoding );
+ text->append( cArr, len );
+ }
+ }
+ else
+ {
+ bool whitespace = false;
+
+ // Remove leading white space:
+ p = SkipWhiteSpace( p, encoding );
+ while ( p && *p
+ && !StringEqual( p, endTag, caseInsensitive, encoding ) )
+ {
+ if ( *p == '\r' || *p == '\n' )
+ {
+ whitespace = true;
+ ++p;
+ }
+ else if ( IsWhiteSpace( *p ) )
+ {
+ whitespace = true;
+ ++p;
+ }
+ else
+ {
+ // If we've found whitespace, add it before the
+ // new character. Any whitespace just becomes a space.
+ if ( whitespace )
+ {
+ (*text) += ' ';
+ whitespace = false;
+ }
+ int len;
+ char cArr[4] = { 0, 0, 0, 0 };
+ p = GetChar( p, cArr, &len, encoding );
+ if ( len == 1 )
+ (*text) += cArr[0]; // more efficient
+ else
+ text->append( cArr, len );
+ }
+ }
+ }
+ if ( p && *p )
+ p += strlen( endTag );
+ return ( p && *p ) ? p : 0;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ // The basic issue with a document is that we don't know what we're
+ // streaming. Read something presumed to be a tag (and hope), then
+ // identify it, and call the appropriate stream method on the tag.
+ //
+ // This "pre-streaming" will never read the closing ">" so the
+ // sub-tag can orient itself.
+
+ if ( !StreamTo( in, '<', tag ) )
+ {
+ SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ while ( in->good() )
+ {
+ int tagIndex = (int) tag->length();
+ while ( in->good() && in->peek() != '>' )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ break;
+ }
+ (*tag) += (char) c;
+ }
+
+ if ( in->good() )
+ {
+ // We now have something we presume to be a node of
+ // some sort. Identify it, and call the node to
+ // continue streaming.
+ TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
+
+ if ( node )
+ {
+ node->StreamIn( in, tag );
+ bool isElement = node->ToElement() != 0;
+ delete node;
+ node = 0;
+
+ // If this is the root element, we're done. Parsing will be
+ // done by the >> operator.
+ if ( isElement )
+ {
+ return;
+ }
+ }
+ else
+ {
+ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ }
+ }
+ // We should have returned sooner.
+ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
+}
+
+#endif
+
+const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
+{
+ ClearError();
+
+ // Parse away, at the document level. Since a document
+ // contains nothing but other tags, most of what happens
+ // here is skipping white space.
+ if ( !p || !*p )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ // Note that, for a document, this needs to come
+ // before the while space skip, so that parsing
+ // starts from the pointer we are given.
+ location.Clear();
+ if ( prevData )
+ {
+ location.row = prevData->cursor.row;
+ location.col = prevData->cursor.col;
+ }
+ else
+ {
+ location.row = 0;
+ location.col = 0;
+ }
+ TiXmlParsingData data( p, TabSize(), location.row, location.col );
+ location = data.Cursor();
+
+ if ( encoding == TIXML_ENCODING_UNKNOWN )
+ {
+ // Check for the Microsoft UTF-8 lead bytes.
+ const unsigned char* pU = (const unsigned char*)p;
+ if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
+ && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
+ && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
+ {
+ encoding = TIXML_ENCODING_UTF8;
+ useMicrosoftBOM = true;
+ }
+ }
+
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ while ( p && *p )
+ {
+ TiXmlNode* node = Identify( p, encoding );
+ if ( node )
+ {
+ p = node->Parse( p, &data, encoding );
+ LinkEndChild( node );
+ }
+ else
+ {
+ break;
+ }
+
+ // Did we get encoding info?
+ if ( encoding == TIXML_ENCODING_UNKNOWN
+ && node->ToDeclaration() )
+ {
+ TiXmlDeclaration* dec = node->ToDeclaration();
+ const char* enc = dec->Encoding();
+ assert( enc );
+
+ if ( *enc == 0 )
+ encoding = TIXML_ENCODING_UTF8;
+ else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
+ encoding = TIXML_ENCODING_UTF8;
+ else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
+ encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
+ else
+ encoding = TIXML_ENCODING_LEGACY;
+ }
+
+ p = SkipWhiteSpace( p, encoding );
+ }
+
+ // Was this empty?
+ if ( !firstChild ) {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
+ return 0;
+ }
+
+ // All is well.
+ return p;
+}
+
+void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ // The first error in a chain is more accurate - don't set again!
+ if ( error )
+ return;
+
+ assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
+ error = true;
+ errorId = err;
+ errorDesc = errorString[ errorId ];
+
+ errorLocation.Clear();
+ if ( pError && data )
+ {
+ data->Stamp( pError, encoding );
+ errorLocation = data->Cursor();
+ }
+}
+
+
+TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
+{
+ TiXmlNode* returnNode = 0;
+
+ p = SkipWhiteSpace( p, encoding );
+ if( !p || !*p || *p != '<' )
+ {
+ return 0;
+ }
+
+ p = SkipWhiteSpace( p, encoding );
+
+ if ( !p || !*p )
+ {
+ return 0;
+ }
+
+ // What is this thing?
+ // - Elements start with a letter or underscore, but xml is reserved.
+ // - Comments: <!--
+ // - Decleration: <?xml
+ // - Everthing else is unknown to tinyxml.
+ //
+
+ const char* xmlHeader = { "<?xml" };
+ const char* commentHeader = { "<!--" };
+ const char* dtdHeader = { "<!" };
+ const char* cdataHeader = { "<![CDATA[" };
+
+ if ( StringEqual( p, xmlHeader, true, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Declaration\n" );
+ #endif
+ returnNode = new TiXmlDeclaration();
+ }
+ else if ( StringEqual( p, commentHeader, false, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Comment\n" );
+ #endif
+ returnNode = new TiXmlComment();
+ }
+ else if ( StringEqual( p, cdataHeader, false, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing CDATA\n" );
+ #endif
+ TiXmlText* text = new TiXmlText( "" );
+ text->SetCDATA( true );
+ returnNode = text;
+ }
+ else if ( StringEqual( p, dtdHeader, false, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Unknown(1)\n" );
+ #endif
+ returnNode = new TiXmlUnknown();
+ }
+ else if ( IsAlpha( *(p+1), encoding )
+ || *(p+1) == '_' )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Element\n" );
+ #endif
+ returnNode = new TiXmlElement( "" );
+ }
+ else
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Unknown(2)\n" );
+ #endif
+ returnNode = new TiXmlUnknown();
+ }
+
+ if ( returnNode )
+ {
+ // Set the parent, so it can report errors
+ returnNode->parent = this;
+ }
+ return returnNode;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag)
+{
+ // We're called with some amount of pre-parsing. That is, some of "this"
+ // element is in "tag". Go ahead and stream to the closing ">"
+ while( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ (*tag) += (char) c ;
+
+ if ( c == '>' )
+ break;
+ }
+
+ if ( tag->length() < 3 ) return;
+
+ // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
+ // If not, identify and stream.
+
+ if ( tag->at( tag->length() - 1 ) == '>'
+ && tag->at( tag->length() - 2 ) == '/' )
+ {
+ // All good!
+ return;
+ }
+ else if ( tag->at( tag->length() - 1 ) == '>' )
+ {
+ // There is more. Could be:
+ // text
+ // cdata text (which looks like another node)
+ // closing tag
+ // another node.
+ for ( ;; )
+ {
+ StreamWhiteSpace( in, tag );
+
+ // Do we have text?
+ if ( in->good() && in->peek() != '<' )
+ {
+ // Yep, text.
+ TiXmlText text( "" );
+ text.StreamIn( in, tag );
+
+ // What follows text is a closing tag or another node.
+ // Go around again and figure it out.
+ continue;
+ }
+
+ // We now have either a closing tag...or another node.
+ // We should be at a "<", regardless.
+ if ( !in->good() ) return;
+ assert( in->peek() == '<' );
+ int tagIndex = (int) tag->length();
+
+ bool closingTag = false;
+ bool firstCharFound = false;
+
+ for( ;; )
+ {
+ if ( !in->good() )
+ return;
+
+ int c = in->peek();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ if ( c == '>' )
+ break;
+
+ *tag += (char) c;
+ in->get();
+
+ // Early out if we find the CDATA id.
+ if ( c == '[' && tag->size() >= 9 )
+ {
+ size_t len = tag->size();
+ const char* start = tag->c_str() + len - 9;
+ if ( strcmp( start, "<![CDATA[" ) == 0 ) {
+ assert( !closingTag );
+ break;
+ }
+ }
+
+ if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
+ {
+ firstCharFound = true;
+ if ( c == '/' )
+ closingTag = true;
+ }
+ }
+ // If it was a closing tag, then read in the closing '>' to clean up the input stream.
+ // If it was not, the streaming will be done by the tag.
+ if ( closingTag )
+ {
+ if ( !in->good() )
+ return;
+
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ assert( c == '>' );
+ *tag += (char) c;
+
+ // We are done, once we've found our closing tag.
+ return;
+ }
+ else
+ {
+ // If not a closing tag, id it, and stream.
+ const char* tagloc = tag->c_str() + tagIndex;
+ TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
+ if ( !node )
+ return;
+ node->StreamIn( in, tag );
+ delete node;
+ node = 0;
+
+ // No return: go around from the beginning: text, closing tag, or node.
+ }
+ }
+ }
+}
+#endif
+
+const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ p = SkipWhiteSpace( p, encoding );
+ TiXmlDocument* document = GetDocument();
+
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
+ return 0;
+ }
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+
+ if ( *p != '<' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
+ return 0;
+ }
+
+ p = SkipWhiteSpace( p+1, encoding );
+
+ // Read the name.
+ const char* pErr = p;
+
+ p = ReadName( p, &value, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
+ return 0;
+ }
+
+ TIXML_STRING endTag ("</");
+ endTag += value;
+
+ // Check for and read attributes. Also look for an empty
+ // tag or an end tag.
+ while ( p && *p )
+ {
+ pErr = p;
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
+ return 0;
+ }
+ if ( *p == '/' )
+ {
+ ++p;
+ // Empty tag.
+ if ( *p != '>' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
+ return 0;
+ }
+ return (p+1);
+ }
+ else if ( *p == '>' )
+ {
+ // Done with attributes (if there were any.)
+ // Read the value -- which can include other
+ // elements -- read the end tag, and return.
+ ++p;
+ p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
+ if ( !p || !*p ) {
+ // We were looking for the end tag, but found nothing.
+ // Fix for [ 1663758 ] Failure to report error on bad XML
+ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+ return 0;
+ }
+
+ // We should find the end tag now
+ // note that:
+ // </foo > and
+ // </foo>
+ // are both valid end tags.
+ if ( StringEqual( p, endTag.c_str(), false, encoding ) )
+ {
+ p += endTag.length();
+ p = SkipWhiteSpace( p, encoding );
+ if ( p && *p && *p == '>' ) {
+ ++p;
+ return p;
+ }
+ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+ return 0;
+ }
+ else
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+ return 0;
+ }
+ }
+ else
+ {
+ // Try to read an attribute:
+ TiXmlAttribute* attrib = new TiXmlAttribute();
+ if ( !attrib )
+ {
+ return 0;
+ }
+
+ attrib->SetDocument( document );
+ pErr = p;
+ p = attrib->Parse( p, data, encoding );
+
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
+ delete attrib;
+ return 0;
+ }
+
+ // Handle the strange case of double attributes:
+ #ifdef TIXML_USE_STL
+ TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
+ #else
+ TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
+ #endif
+ if ( node )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
+ delete attrib;
+ return 0;
+ }
+
+ attributeSet.Add( attrib );
+ }
+ }
+ return p;
+}
+
+
+const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ TiXmlDocument* document = GetDocument();
+
+ // Read in text and elements in any order.
+ const char* pWithWhiteSpace = p;
+ p = SkipWhiteSpace( p, encoding );
+
+ while ( p && *p )
+ {
+ if ( *p != '<' )
+ {
+ // Take what we have, make a text element.
+ TiXmlText* textNode = new TiXmlText( "" );
+
+ if ( !textNode )
+ {
+ return 0;
+ }
+
+ if ( TiXmlBase::IsWhiteSpaceCondensed() )
+ {
+ p = textNode->Parse( p, data, encoding );
+ }
+ else
+ {
+ // Special case: we want to keep the white space
+ // so that leading spaces aren't removed.
+ p = textNode->Parse( pWithWhiteSpace, data, encoding );
+ }
+
+ if ( !textNode->Blank() )
+ LinkEndChild( textNode );
+ else
+ delete textNode;
+ }
+ else
+ {
+ // We hit a '<'
+ // Have we hit a new element or an end tag? This could also be
+ // a TiXmlText in the "CDATA" style.
+ if ( StringEqual( p, "</", false, encoding ) )
+ {
+ return p;
+ }
+ else
+ {
+ TiXmlNode* node = Identify( p, encoding );
+ if ( node )
+ {
+ p = node->Parse( p, data, encoding );
+ LinkEndChild( node );
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ pWithWhiteSpace = p;
+ p = SkipWhiteSpace( p, encoding );
+ }
+
+ if ( !p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
+ }
+ return p;
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ (*tag) += (char) c;
+
+ if ( c == '>' )
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+
+const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ TiXmlDocument* document = GetDocument();
+ p = SkipWhiteSpace( p, encoding );
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+ if ( !p || !*p || *p != '<' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
+ return 0;
+ }
+ ++p;
+ value = "";
+
+ while ( p && *p && *p != '>' )
+ {
+ value += *p;
+ ++p;
+ }
+
+ if ( !p )
+ {
+ if ( document )
+ document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
+ }
+ if ( p && *p == '>' )
+ return p+1;
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ (*tag) += (char) c;
+
+ if ( c == '>'
+ && tag->at( tag->length() - 2 ) == '-'
+ && tag->at( tag->length() - 3 ) == '-' )
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+
+const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ TiXmlDocument* document = GetDocument();
+ value = "";
+
+ p = SkipWhiteSpace( p, encoding );
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+ const char* startTag = "<!--";
+ const char* endTag = "-->";
+
+ if ( !StringEqual( p, startTag, false, encoding ) )
+ {
+ if ( document )
+ document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
+ return 0;
+ }
+ p += strlen( startTag );
+
+ // [ 1475201 ] TinyXML parses entities in comments
+ // Oops - ReadText doesn't work, because we don't want to parse the entities.
+ // p = ReadText( p, &value, false, endTag, false, encoding );
+ //
+ // from the XML spec:
+ /*
+ [Definition: Comments may appear anywhere in a document outside other markup; in addition,
+ they may appear within the document type declaration at places allowed by the grammar.
+ They are not part of the document's character data; an XML processor MAY, but need not,
+ make it possible for an application to retrieve the text of comments. For compatibility,
+ the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity
+ references MUST NOT be recognized within comments.
+
+ An example of a comment:
+
+ <!-- declarations for <head> & <body> -->
+ */
+
+ value = "";
+ // Keep all the white space.
+ while ( p && *p && !StringEqual( p, endTag, false, encoding ) )
+ {
+ value.append( p, 1 );
+ ++p;
+ }
+ if ( p && *p )
+ p += strlen( endTag );
+
+ return p;
+}
+
+
+const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p ) return 0;
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+ // Read the name, the '=' and the value.
+ const char* pErr = p;
+ p = ReadName( p, &name, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
+ return 0;
+ }
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p || *p != '=' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+ return 0;
+ }
+
+ ++p; // skip '='
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+ return 0;
+ }
+
+ const char* end;
+ const char SINGLE_QUOTE = '\'';
+ const char DOUBLE_QUOTE = '\"';
+
+ if ( *p == SINGLE_QUOTE )
+ {
+ ++p;
+ end = "\'"; // single quote in string
+ p = ReadText( p, &value, false, end, false, encoding );
+ }
+ else if ( *p == DOUBLE_QUOTE )
+ {
+ ++p;
+ end = "\""; // double quote in string
+ p = ReadText( p, &value, false, end, false, encoding );
+ }
+ else
+ {
+ // All attribute values should be in single or double quotes.
+ // But this is such a common error that the parser will try
+ // its best, even without them.
+ value = "";
+ while ( p && *p // existence
+ && !IsWhiteSpace( *p ) // whitespace
+ && *p != '/' && *p != '>' ) // tag end
+ {
+ if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {
+ // [ 1451649 ] Attribute values with trailing quotes not handled correctly
+ // We did not have an opening quote but seem to have a
+ // closing one. Give up and throw an error.
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+ return 0;
+ }
+ value += *p;
+ ++p;
+ }
+ }
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->peek();
+ if ( !cdata && (c == '<' ) )
+ {
+ return;
+ }
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ (*tag) += (char) c;
+ in->get(); // "commits" the peek made above
+
+ if ( cdata && c == '>' && tag->size() >= 3 ) {
+ size_t len = tag->size();
+ if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) {
+ // terminator of cdata.
+ return;
+ }
+ }
+ }
+}
+#endif
+
+const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ value = "";
+ TiXmlDocument* document = GetDocument();
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+
+ const char* const startTag = "<![CDATA[";
+ const char* const endTag = "]]>";
+
+ if ( cdata || StringEqual( p, startTag, false, encoding ) )
+ {
+ cdata = true;
+
+ if ( !StringEqual( p, startTag, false, encoding ) )
+ {
+ if ( document )
+ document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding );
+ return 0;
+ }
+ p += strlen( startTag );
+
+ // Keep all the white space, ignore the encoding, etc.
+ while ( p && *p
+ && !StringEqual( p, endTag, false, encoding )
+ )
+ {
+ value += *p;
+ ++p;
+ }
+
+ TIXML_STRING dummy;
+ p = ReadText( p, &dummy, false, endTag, false, encoding );
+ return p;
+ }
+ else
+ {
+ bool ignoreWhite = true;
+
+ const char* end = "<";
+ p = ReadText( p, &value, ignoreWhite, end, false, encoding );
+ if ( p && *p )
+ return p-1; // don't truncate the '<'
+ return 0;
+ }
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ (*tag) += (char) c;
+
+ if ( c == '>' )
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
+{
+ p = SkipWhiteSpace( p, _encoding );
+ // Find the beginning, find the end, and look for
+ // the stuff in-between.
+ TiXmlDocument* document = GetDocument();
+ if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
+ return 0;
+ }
+ if ( data )
+ {
+ data->Stamp( p, _encoding );
+ location = data->Cursor();
+ }
+ p += 5;
+
+ version = "";
+ encoding = "";
+ standalone = "";
+
+ while ( p && *p )
+ {
+ if ( *p == '>' )
+ {
+ ++p;
+ return p;
+ }
+
+ p = SkipWhiteSpace( p, _encoding );
+ if ( StringEqual( p, "version", true, _encoding ) )
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse( p, data, _encoding );
+ version = attrib.Value();
+ }
+ else if ( StringEqual( p, "encoding", true, _encoding ) )
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse( p, data, _encoding );
+ encoding = attrib.Value();
+ }
+ else if ( StringEqual( p, "standalone", true, _encoding ) )
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse( p, data, _encoding );
+ standalone = attrib.Value();
+ }
+ else
+ {
+ // Read over whatever it is.
+ while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
+ ++p;
+ }
+ }
+ return 0;
+}
+
+bool TiXmlText::Blank() const
+{
+ for ( unsigned i=0; i<value.length(); i++ )
+ if ( !IsWhiteSpace( value[i] ) )
+ return false;
+ return true;
+}
+
diff --git a/mmc_updater/depends/win32cpp/controls.h b/mmc_updater/depends/win32cpp/controls.h
new file mode 100644
index 00000000..a15c8b5f
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/controls.h
@@ -0,0 +1,1074 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// controls.h
+// Declaration of the following classes:
+// CAnimation, CComboBox, CComboBoxEx, CProgressBar,
+// CScrollBar, CSlider, CSpinButton
+
+
+#ifndef _WIN32XX_CONTROLS_H_
+#define _WIN32XX_CONTROLS_H_
+
+#include "wincore.h"
+
+namespace Win32xx
+{
+ class CAnimation : public CWnd
+ {
+ public:
+ CAnimation() {}
+ virtual ~CAnimation() {}
+
+ BOOL Close() const;
+ BOOL Open(LPTSTR lpszName) const;
+ BOOL Play(UINT wFrom, UINT wTo, UINT cRepeat) const;
+ BOOL Seek(UINT wFrame) const;
+ BOOL Stop() const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = ANIMATE_CLASS; }
+ };
+
+
+ class CComboBox : public CWnd
+ {
+ public:
+ CComboBox() {}
+ virtual ~CComboBox() {}
+
+ int AddString(LPCTSTR lpszString) const;
+ void Clear() const;
+ void Copy() const;
+ void Cut() const;
+ int DeleteString(int nIndex) const;
+ int Dir(UINT attr, LPCTSTR lpszWildCard ) const;
+ int FindString(int nIndexStart, LPCTSTR lpszString) const;
+ int FindStringExact(int nIndexStart, LPCTSTR lpszString) const;
+ int GetCount() const;
+ int GetCurSel() const;
+ CRect GetDroppedControlRect() const;
+ BOOL GetDroppedState() const;
+ int GetDroppedWidth() const;
+ DWORD GetEditSel() const;
+ BOOL GetExtendedUI() const;
+ int GetHorizontalExtent() const;
+ DWORD GetItemData(int nIndex) const;
+ int GetItemHeight(int nIndex) const;
+ int GetLBText(int nIndex, LPTSTR lpszText) const;
+ int GetLBTextLen(int nIndex) const;
+ LCID GetLocale() const;
+ int GetTopIndex() const;
+ int InitStorage(int nItems, int nBytes) const;
+ int InsertString(int nIndex, LPCTSTR lpszString) const;
+ void LimitText(int nMaxChars) const;
+ void Paste() const;
+ void ResetContent() const;
+ int SelectString(int nStartAfter, LPCTSTR lpszString) const;
+ int SetCurSel(int nIndex) const;
+ int SetDroppedWidth(int nWidth) const;
+ BOOL SetEditSel(int nStartChar, int nEndChar) const;
+ int SetExtendedUI(BOOL bExtended = TRUE) const;
+ void SetHorizontalExtent(UINT nExtent ) const;
+ int SetItemData(int nIndex, DWORD dwItemData) const;
+ int SetItemHeight(int nIndex, UINT cyItemHeight) const;
+ LCID SetLocale( LCID NewLocale ) const;
+ int SetTopIndex(int nIndex) const;
+ void ShowDropDown(BOOL bShow = TRUE) const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = _T("ComboBox"); }
+ };
+
+
+ class CComboBoxEx : public CWnd
+ {
+ public:
+ CComboBoxEx() {}
+ virtual ~CComboBoxEx() {}
+
+ int DeleteItem(int nIndex ) const;
+ CWnd* GetComboBoxCtrl() const;
+ CWnd* GetEditCtrl() const;
+ DWORD GetExtendedStyle() const;
+ HIMAGELIST GetImageList() const;
+ BOOL GetItem(COMBOBOXEXITEM* pCBItem) const;
+ BOOL HasEditChanged () const;
+ int InsertItem(COMBOBOXEXITEM* lpcCBItem) const;
+ DWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyles ) const;
+ HIMAGELIST SetImageList(HIMAGELIST himl) const;
+ BOOL SetItem(PCOMBOBOXEXITEM lpcCBItem) const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = WC_COMBOBOXEX; }
+ };
+
+
+ class CProgressBar : public CWnd
+ {
+ public:
+ CProgressBar() {}
+ virtual ~CProgressBar() {}
+
+ int GetPos() const;
+ int GetRange(BOOL fWhichLimit, PPBRANGE ppBRange) const;
+ int OffsetPos(int nIncrement) const;
+ int SetPos(int nNewPos) const;
+ int SetRange(short nMinRange, short nMaxRange) const;
+ int SetStep(int nStepInc) const;
+ int StepIt() const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = PROGRESS_CLASS; }
+ };
+
+
+ class CScrollBar : public CWnd
+ {
+ public:
+ CScrollBar() {}
+ virtual ~CScrollBar() {}
+
+ BOOL EnableScrollBar( UINT nArrowFlags = ESB_ENABLE_BOTH ) const;
+ BOOL GetScrollInfo(LPSCROLLINFO lpsi) const;
+ int GetScrollPos() const;
+ BOOL GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos ) const;
+ BOOL SetScrollInfo(LPSCROLLINFO lpsi, BOOL bRedraw = TRUE ) const;
+ int SetScrollPos(int nPos, BOOL bRedraw) const;
+ BOOL SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw = TRUE ) const;
+ BOOL ShowScrollBar(BOOL bShow) const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = _T("SCROLLBAR"); ; }
+ };
+
+
+ class CSlider : public CWnd
+ {
+ public:
+ CSlider() {}
+ virtual ~CSlider() {}
+
+ void ClearSel() const;
+ void ClearTics(BOOL bRedraw = FALSE ) const;
+ CWnd* GetBuddy(BOOL fLocation = TRUE ) const;
+ CRect GetChannelRect() const;
+ int GetLineSize() const;
+ int GetNumTics() const;
+ int GetPageSize() const;
+ int GetPos() const;
+ int GetRangeMax() const;
+ int GetRangeMin() const;
+ int GetSelEnd() const;
+ int GetSelStart() const;
+ int GetThumbLength() const;
+ CRect GetThumbRect() const;
+ int GetTic(int nTic ) const;
+ int GetTicPos(int nTic) const;
+ CWnd* GetToolTips() const;
+ CWnd* SetBuddy(CWnd* pBuddy, BOOL fLocation = TRUE ) const;
+ int SetLineSize(int nSize) const;
+ int SetPageSize(int nSize) const;
+ void SetPos(int nPos, BOOL bRedraw = FALSE) const;
+ void SetRangeMax(int nMax, BOOL bRedraw = FALSE) const;
+ void SetRangeMin(int nMax, BOOL bRedraw = FALSE) const;
+ void SetSelection(int nMin, int nMax, BOOL bRedraw = FALSE) const;
+ BOOL SetTic(int nTic) const;
+ void SetTicFreq(int nFreq) const;
+ int SetTipSide(int nLocation) const;
+ void SetToolTips(CWnd* pToolTip) const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = TRACKBAR_CLASS; }
+ };
+
+
+ // Also known as an Up/Down control
+ class CSpinButton : public CWnd
+ {
+ public:
+ CSpinButton() {}
+ virtual ~CSpinButton() {}
+
+ int GetAccel(int cAccels, LPUDACCEL paAccels) const;
+ int GetBase() const;
+ CWnd* GetBuddy() const;
+ int GetPos() const;
+ DWORD GetRange() const;
+ BOOL SetAccel(int cAccels, LPUDACCEL paAccels) const;
+ int SetBase(int nBase) const;
+ CWnd* SetBuddy(CWnd* hwndBuddy) const;
+ int SetPos(int nPos) const;
+ void SetRange(int nLower, int nUpper) const;
+
+ protected:
+ // Overridables
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ };
+
+} // namespace Win32xx
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+
+ ////////////////////////////////////////
+ // Definitions for the CAnimation class
+ //
+ inline BOOL CAnimation::Close() const
+ // Closes an AVI clip.
+ {
+ assert(IsWindow());
+ return Animate_Close(m_hWnd);
+ }
+
+ inline BOOL CAnimation::Open(LPTSTR lpszName) const
+ // Opens an AVI clip and displays its first frame in an animation control.
+ {
+ assert(IsWindow());
+ return Animate_Open(m_hWnd, lpszName);
+ }
+
+ inline BOOL CAnimation::Play(UINT wFrom, UINT wTo, UINT cRepeat) const
+ // Plays an AVI clip in an animation control. The control plays the clip
+ // in the background while the thread continues executing.
+ {
+ assert(IsWindow());
+ return Animate_Play(m_hWnd, wFrom, wTo, cRepeat);
+ }
+
+ inline BOOL CAnimation::Seek(UINT wFrame) const
+ // Directs an animation control to display a particular frame of an AVI clip.
+ // The control displays the clip in the background while the thread continues executing.
+ {
+ assert(IsWindow());
+ return Animate_Seek(m_hWnd, wFrame);
+ }
+
+ inline BOOL CAnimation::Stop() const
+ // Stops playing an AVI clip in an animation control.
+ {
+ assert(IsWindow());
+ return Animate_Stop(m_hWnd);
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CComboBox class
+ //
+ inline int CComboBox::AddString(LPCTSTR lpszString) const
+ // Adds a string to the list box of a combo box. If the combo box does not
+ // have the CBS_SORT style, the string is added to the end of the list.
+ // Otherwise, the string is inserted into the list, and the list is sorted.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_ADDSTRING, 0, (LPARAM)lpszString);
+ }
+
+ inline void CComboBox::Clear() const
+ // Deletes the current selection, if any, from the combo box's edit control.
+ {
+ assert(IsWindow());
+ SendMessage(WM_CLEAR, 0, 0);
+ }
+
+ inline void CComboBox::Copy() const
+ // Copies the current selection to the clipboard in CF_TEXT format.
+ {
+ assert(IsWindow());
+ SendMessage(WM_COPY, 0, 0);
+ }
+
+ inline void CComboBox::Cut() const
+ // Deletes the current selection, if any, in the edit control and copies
+ // the deleted text to the clipboard in CF_TEXT format.
+ {
+ assert(IsWindow());
+ SendMessage(WM_CUT, 0, 0);
+ }
+
+ inline int CComboBox::DeleteString(int nIndex) const
+ // Deletes a string in the list box of a combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_DELETESTRING, (WPARAM)nIndex, 0);
+ }
+
+ inline int CComboBox::Dir(UINT attr, LPCTSTR lpszWildCard ) const
+ // Adds the names of directories and files that match a specified string
+ // and set of file attributes.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_DIR, (WPARAM)attr, (LPARAM)lpszWildCard);
+ }
+
+ inline int CComboBox::FindString(int nIndexStart, LPCTSTR lpszString) const
+ // Search the list box of a combo box for an item beginning with the
+ // characters in a specified string.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_FINDSTRING, (WPARAM)nIndexStart, (LPARAM)lpszString);
+ }
+
+ inline int CComboBox::FindStringExact(int nIndexStart, LPCTSTR lpszString) const
+ // Find the first list box string in a combo box that matches the string specified in lpszString.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_FINDSTRINGEXACT, (WPARAM)nIndexStart, (LPARAM)lpszString);
+ }
+
+ inline int CComboBox::GetCount() const
+ // Retrieves the number of items in the list box of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETCOUNT, 0,0);
+ }
+
+ inline int CComboBox::GetCurSel() const
+ // Retrieves the index of the currently selected item, if any, in the list box of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETCURSEL, 0,0);
+ }
+
+ inline CRect CComboBox::GetDroppedControlRect() const
+ // Retrieves the screen coordinates of the combo box in its dropped-down state.
+ {
+ assert(IsWindow());
+ CRect rc;
+ SendMessage(CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline BOOL CComboBox::GetDroppedState() const
+ // Determines whether the list box of the combo box is dropped down.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(CB_GETDROPPEDSTATE, 0, 0);
+ }
+
+ inline int CComboBox::GetDroppedWidth() const
+ // Retrieves the minimum allowable width, in pixels, of the list box of the combo box
+ // with the CBS_DROPDOWN or CBS_DROPDOWNLIST style.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETDROPPEDWIDTH, 0, 0);
+ }
+
+ inline DWORD CComboBox::GetEditSel() const
+ // Gets the starting and ending character positions of the current selection
+ // in the edit control of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETEDITSEL, 0, 0);
+ }
+
+ inline BOOL CComboBox::GetExtendedUI() const
+ // Determines whether the combo box has the default user interface or the extended user interface.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(CB_GETEXTENDEDUI, 0, 0);
+ }
+
+ inline int CComboBox::GetHorizontalExtent() const
+ // Retrieve from the combo box the width, in pixels, by which the list box can
+ // be scrolled horizontally (the scrollable width).
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETHORIZONTALEXTENT, 0, 0);
+ }
+
+ inline DWORD CComboBox::GetItemData(int nIndex) const
+ // Retrieves the application-supplied value associated with the specified item in the combo box.
+ {
+ assert(IsWindow());
+ return (DWORD)SendMessage(CB_GETITEMDATA, (WPARAM)nIndex, 0);
+ }
+
+ inline int CComboBox::GetItemHeight(int nIndex) const
+ // Determines the height of list items or the selection field in the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETITEMHEIGHT, (WPARAM)nIndex, 0);
+ }
+
+ inline int CComboBox::GetLBText(int nIndex, LPTSTR lpszText) const
+ // Retrieves a string from the list of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETLBTEXT, (WPARAM)nIndex, (LPARAM)lpszText);
+ }
+
+ inline int CComboBox::GetLBTextLen(int nIndex) const
+ // Retrieves the length, in characters, of a string in the list of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETLBTEXTLEN, (WPARAM)nIndex, 0);
+ }
+
+ inline LCID CComboBox::GetLocale() const
+ // Retrieves the current locale of the combo box.
+ {
+ assert(IsWindow());
+ return (LCID)SendMessage(CB_GETLOCALE, 0, 0);
+ }
+
+ inline int CComboBox::GetTopIndex() const
+ // Retrieves the zero-based index of the first visible item in the list box portion of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_GETTOPINDEX, 0, 0);
+ }
+
+ inline int CComboBox::InitStorage(int nItems, int nBytes) const
+ // Allocates memory for storing list box items. Use this before adding a
+ // large number of items to the list box portion of a combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_INITSTORAGE, (WPARAM)nItems, (LPARAM)nBytes);
+ }
+
+ inline int CComboBox::InsertString(int nIndex, LPCTSTR lpszString) const
+ // Inserts a string into the list box of the combo box. Unlike the AddString,
+ // a list with the CBS_SORT style is not sorted.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_INSERTSTRING, (WPARAM)nIndex, (LPARAM)lpszString);
+ }
+
+ inline void CComboBox::Paste() const
+ // Copies the current content of the clipboard to the combo box's edit control at the current caret position.
+ {
+ assert(IsWindow());
+ SendMessage(WM_PASTE, 0, 0);
+ }
+
+ inline void CComboBox::LimitText(int nMaxChars) const
+ // Limits the length of the text the user may type into the edit control of the combo box.
+ {
+ assert(IsWindow());
+ SendMessage(CB_LIMITTEXT, (WPARAM)nMaxChars, 0);
+ }
+
+ inline void CComboBox::ResetContent() const
+ // Removes all items from the list box and edit control of the combo box.
+ {
+ assert(IsWindow());
+ SendMessage(CB_RESETCONTENT, 0, 0);
+ }
+
+ inline int CComboBox::SelectString(int nStartAfter, LPCTSTR lpszString) const
+ // Searches the list of a combo box for an item that begins with the characters in a
+ // specified string. If a matching item is found, it is selected and copied to the edit control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SELECTSTRING, (WPARAM)nStartAfter, (LPARAM)lpszString);
+ }
+
+ inline int CComboBox::SetCurSel(int nIndex) const
+ // Selects a string in the list of the combo box. If necessary, the list scrolls the string into view.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SETCURSEL, (WPARAM)nIndex, 0);
+ }
+
+ inline int CComboBox::SetDroppedWidth(int nWidth) const
+ // Sets the maximum allowable width, in pixels, of the list box of the combo box with
+ // the CBS_DROPDOWN or CBS_DROPDOWNLIST style.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SETDROPPEDWIDTH, (WPARAM)nWidth, 0);
+ }
+
+ inline BOOL CComboBox::SetEditSel(int nStartChar, int nEndChar) const
+ // Selects characters in the edit control of the combo box.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(CB_SETEDITSEL, 0, (LPARAM)MAKELONG(nStartChar,nEndChar));
+ }
+
+ inline int CComboBox::SetExtendedUI(BOOL bExtended) const
+ // Selects either the default user interface or the extended user interface for the combo box that
+ // has the CBS_DROPDOWN or CBS_DROPDOWNLIST style.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SETEXTENDEDUI, (WPARAM)bExtended, 0);
+ }
+
+ inline void CComboBox::SetHorizontalExtent(UINT nExtent ) const
+ // Sets the width, in pixels, by which the list box can be scrolled horizontally (the scrollable width).
+ {
+ assert(IsWindow());
+ SendMessage(CB_SETHORIZONTALEXTENT, (WPARAM)nExtent, 0);
+ }
+
+ inline int CComboBox::SetItemData(int nIndex, DWORD dwItemData) const
+ // Sets the value associated with the specified item in the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SETITEMDATA, (WPARAM)nIndex, (LPARAM)dwItemData);
+ }
+
+ inline int CComboBox::SetItemHeight(int nIndex, UINT cyItemHeight) const
+ // Sets the height of list items or the selection field in the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SETITEMHEIGHT, (WPARAM)nIndex, (LPARAM)cyItemHeight);
+ }
+
+ inline LCID CComboBox::SetLocale( LCID NewLocale ) const
+ // Sets the current locale of the combo box.
+ {
+ assert(IsWindow());
+ return (LCID)SendMessage(CB_SETLOCALE, (WPARAM)NewLocale, 0);
+ }
+
+ inline int CComboBox::SetTopIndex(int nIndex) const
+ // Ensure that a particular item is visible in the list box of the combo box.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CB_SETTOPINDEX, (WPARAM)nIndex, 0);
+ }
+
+ inline void CComboBox::ShowDropDown(BOOL bShow) const
+ // Shows or hides the list box of the combo box that has the CBS_DROPDOWN or CBS_DROPDOWNLIST style.
+ {
+ assert(IsWindow());
+ SendMessage(CB_SHOWDROPDOWN, (WPARAM)bShow, 0);
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CComboBoxEx class
+ //
+ inline int CComboBoxEx::DeleteItem(int nIndex ) const
+ // Removes an item from the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CBEM_DELETEITEM, (WPARAM)nIndex, 0);
+ }
+
+ inline CWnd* CComboBoxEx::GetComboBoxCtrl() const
+ // Retrieves the handle to the child combo box control.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(CBEM_GETCOMBOCONTROL, 0, 0));
+ }
+
+ inline CWnd* CComboBoxEx::GetEditCtrl() const
+ // Retrieves the handle to the edit control portion of the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(CBEM_GETEDITCONTROL, 0, 0));
+ }
+
+ inline DWORD CComboBoxEx::GetExtendedStyle() const
+ // Retrieves the extended styles that are in use for the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (DWORD)SendMessage(CBEM_GETEXTENDEDSTYLE, 0, 0);
+ }
+
+ inline HIMAGELIST CComboBoxEx::GetImageList() const
+ // Retrieves the handle to an image list assigned to the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (HIMAGELIST)SendMessage(CBEM_GETIMAGELIST, 0, 0);
+ }
+
+ inline BOOL CComboBoxEx::GetItem(COMBOBOXEXITEM* pCBItem) const
+ // Retrieves item information for the given ComboBoxEx item.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(CBEM_GETITEM, 0, (LPARAM)pCBItem);
+ }
+
+ inline BOOL CComboBoxEx::HasEditChanged () const
+ // Determines whether or not the user has changed the text of the ComboBoxEx edit control.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(CBEM_HASEDITCHANGED, 0, 0);
+ }
+
+ inline int CComboBoxEx::InsertItem(COMBOBOXEXITEM* lpcCBItem) const
+ // Inserts a new item in the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(CBEM_INSERTITEM, 0, (LPARAM)lpcCBItem);
+ }
+
+ inline DWORD CComboBoxEx::SetExtendedStyle(DWORD dwExMask, DWORD dwExStyles ) const
+ // Sets extended styles within the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (DWORD)SendMessage(CBEM_SETEXTENDEDSTYLE, (WPARAM)dwExMask, (LPARAM)dwExStyles);
+ }
+
+ inline HIMAGELIST CComboBoxEx::SetImageList(HIMAGELIST himl) const
+ // Sets an image list for the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (HIMAGELIST)SendMessage(CBEM_SETIMAGELIST, 0, (LPARAM)himl);
+ }
+
+ inline BOOL CComboBoxEx::SetItem(PCOMBOBOXEXITEM lpcCBItem) const
+ // Sets the attributes for an item in the ComboBoxEx control.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(CBEM_SETITEM, 0, (LPARAM)lpcCBItem);
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CProgressBar class
+ //
+ inline int CProgressBar::GetPos() const
+ // Retrieves the current position of the progress bar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_GETPOS, 0, 0);
+ }
+
+ inline int CProgressBar::GetRange(BOOL fWhichLimit, PPBRANGE ppBRange) const
+ // Retrieves information about the current high and low limits of the progress bar control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_GETRANGE, (WPARAM)fWhichLimit, (LPARAM) (PPBRANGE) ppBRange);
+ }
+
+ inline int CProgressBar::OffsetPos(int nIncrement) const
+ // Advances the current position of the progress bar by a specified increment and redraws
+ // the bar to reflect the new position.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_DELTAPOS, (WPARAM)nIncrement, 0);
+ }
+
+ inline int CProgressBar::SetPos(int nNewPos) const
+ // Sets the current position for the progress bar and redraws the bar to reflect the new position.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_SETPOS, (WPARAM)nNewPos, 0);
+ }
+
+ inline int CProgressBar::SetRange(short nMinRange, short nMaxRange) const
+ // Sets the minimum and maximum values for the progress bar and redraws the bar to reflect the new range.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_SETRANGE, 0, (LPARAM) MAKELPARAM (nMinRange, nMaxRange));
+ }
+
+ inline int CProgressBar::SetStep(int nStepInc) const
+ // Specifies the step increment for the progress bar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_SETSTEP, (WPARAM)nStepInc, 0);
+ }
+
+ inline int CProgressBar::StepIt() const
+ // Advances the current position for a progress bar by the step increment and
+ // redraws the bar to reflect the new position.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(PBM_STEPIT, 0, 0);
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CScrollBar class
+ //
+ inline BOOL CScrollBar::EnableScrollBar( UINT nArrowFlags ) const
+ // Enables or disables the scroll bar arrows.
+ {
+ assert(IsWindow());
+ return ::EnableScrollBar(m_hWnd, SB_CTL, nArrowFlags);
+ }
+
+ inline BOOL CScrollBar::GetScrollInfo(LPSCROLLINFO lpsi) const
+ // Retrieves the parameters of a scroll bar, including the minimum and maximum
+ // scrolling positions, the page size, and the position of the scroll box (thumb).
+ {
+ assert(IsWindow());
+ return ::GetScrollInfo(m_hWnd, SB_CTL, lpsi);
+ }
+
+ inline int CScrollBar::GetScrollPos() const
+ // Retrieves the current position of the scroll box (thumb) in the scroll bar.
+ {
+ assert(IsWindow());
+ return ::GetScrollPos(m_hWnd, SB_CTL);
+ }
+
+ inline BOOL CScrollBar::GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos ) const
+ // Retrieves the current minimum and maximum scroll box (thumb) positions for the scroll bar.
+ {
+ assert(IsWindow());
+ return ::GetScrollRange(m_hWnd, SB_CTL, lpMinPos, lpMaxPos);
+ }
+
+ inline BOOL CScrollBar::SetScrollInfo(LPSCROLLINFO lpsi, BOOL bRedraw ) const
+ // Sets the parameters of the scroll bar, including the minimum and maximum scrolling positions,
+ // the page size, and the position of the scroll box (thumb).
+ {
+ assert(IsWindow());
+ return ::SetScrollInfo(m_hWnd, SB_CTL, lpsi, bRedraw);
+ }
+
+ inline int CScrollBar::SetScrollPos(int nPos, BOOL bRedraw) const
+ // Sets the position of the scroll box (thumb) in the scroll bar and, if requested,
+ // redraws the scroll bar to reflect the new position of the scroll box.
+ {
+ assert(IsWindow());
+ return ::SetScrollPos(m_hWnd, SB_CTL, nPos, bRedraw);
+ }
+
+ inline BOOL CScrollBar::SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw ) const
+ // Sets the minimum and maximum scroll box positions for the scroll bar.
+ {
+ assert(IsWindow());
+ return ::SetScrollRange(m_hWnd, SB_CTL, nMinPos, nMaxPos, bRedraw);
+ }
+
+ inline BOOL CScrollBar::ShowScrollBar(BOOL bShow) const
+ // Shows or hides the scroll bar.
+ {
+ assert(IsWindow());
+ return ::ShowScrollBar(m_hWnd, SB_CTL, bShow);
+ }
+
+ ////////////////////////////////////////
+ // Definitions for the CSlider class
+ //
+ inline void CSlider::ClearSel() const
+ // Clears the current selection range in the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_CLEARSEL, 0, 0);
+ }
+
+ inline void CSlider::ClearTics(BOOL bRedraw) const
+ // Removes the current tick marks from the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_CLEARTICS, (WPARAM)bRedraw, 0);
+ }
+
+ inline CWnd* CSlider::GetBuddy(BOOL fLocation) const
+ // Retrieves the handle to the trackbar control buddy window at a given location.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(TBM_GETBUDDY, (WPARAM)fLocation, 0));
+ }
+
+ inline CRect CSlider::GetChannelRect() const
+ // Retrieves the size and position of the bounding rectangle for the trackbar's channel.
+ {
+ assert(IsWindow());
+ CRect rc;
+ SendMessage(TBM_GETCHANNELRECT, 0, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline int CSlider::GetLineSize() const
+ // Retrieves the number of logical positions the trackbar's slider moves in response
+ // to keyboard input from the arrow keys.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETLINESIZE, 0, 0);
+ }
+
+ inline int CSlider::GetNumTics() const
+ // Retrieves the number of tick marks in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETNUMTICS, 0, 0);
+ }
+
+ inline int CSlider::GetPageSize() const
+ // Retrieves the number of logical positions the trackbar's slider moves in response to
+ // keyboard input, or mouse input, such as clicks in the trackbar's channel.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETPAGESIZE, 0, 0);
+ }
+
+ inline int CSlider::GetPos() const
+ // Retrieves the current logical position of the slider in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETPOS, 0, 0);
+ }
+
+ inline int CSlider::GetRangeMax() const
+ // Retrieves the maximum position for the slider in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETRANGEMAX, 0, 0);
+ }
+
+ inline int CSlider::GetRangeMin() const
+ // Retrieves the minimum position for the slider in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETRANGEMIN, 0, 0);
+ }
+
+ inline int CSlider::GetSelEnd() const
+ // Retrieves the ending position of the current selection range in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETSELEND, 0, 0);
+ }
+
+ inline int CSlider::GetSelStart() const
+ // Retrieves the starting position of the current selection range in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETSELSTART, 0, 0);
+ }
+
+ inline int CSlider::GetThumbLength() const
+ // Retrieves the length of the slider in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETTHUMBLENGTH, 0, 0);
+ }
+
+ inline CRect CSlider::GetThumbRect() const
+ // Retrieves the size and position of the bounding rectangle for the slider in the trackbar.
+ {
+ CRect rc;
+ SendMessage(TBM_GETTHUMBRECT, 0, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline int CSlider::GetTic(int nTic ) const
+ // Retrieves the logical position of a tick mark in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETTIC, (WPARAM)nTic, 0);
+ }
+
+ inline int CSlider::GetTicPos(int nTic) const
+ // Retrieves the current physical position of a tick mark in the trackbar.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_GETTICPOS, (WPARAM)nTic, 0);
+ }
+
+ inline CWnd* CSlider::GetToolTips() const
+ // Retrieves the handle to the ToolTip control assigned to the trackbar, if any.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(TBM_GETTOOLTIPS, 0, 0));
+ }
+
+ inline CWnd* CSlider::SetBuddy(CWnd* pBuddy, BOOL fLocation /*= TRUE*/ ) const
+ // Assigns a window as the buddy window for the trackbar control.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(TBM_SETBUDDY, (WPARAM)fLocation, (LPARAM)pBuddy->GetHwnd()));
+ }
+
+ inline int CSlider::SetLineSize(int nSize) const
+ // Sets the number of logical positions the trackbar's slider moves in response to
+ // keyboard input from the arrow keys.
+ {
+ assert(IsWindow());
+ return(int)SendMessage(TBM_SETLINESIZE, 0, (LPARAM)nSize);
+ }
+
+ inline int CSlider::SetPageSize(int nSize) const
+ // Sets the number of logical positions the trackbar's slider moves in response to
+ // keyboard input, or mouse input such as clicks in the trackbar's channel.
+ {
+ assert(IsWindow());
+ return(int)SendMessage(TBM_SETPAGESIZE, 0, (LPARAM)nSize);
+ }
+
+ inline void CSlider::SetPos(int nPos, BOOL bRedraw) const
+ // Sets the current logical position of the slider in the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_SETPOS, (WPARAM)bRedraw, (LPARAM)nPos);
+ }
+
+ inline void CSlider::SetRangeMax(int nMax, BOOL bRedraw) const
+ // Sets the maximum logical position for the slider in the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_SETRANGEMAX, (WPARAM)bRedraw, (LPARAM)nMax);
+ }
+
+ inline void CSlider::SetRangeMin(int nMax, BOOL bRedraw) const
+ // Sets the minimum logical position for the slider in the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_SETRANGEMIN, (WPARAM)bRedraw, (LPARAM)nMax);
+ }
+
+ inline void CSlider::SetSelection(int nMin, int nMax, BOOL bRedraw) const
+ // Sets the starting and ending positions for the available selection range in the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_SETSEL, (WPARAM)bRedraw, (LPARAM)MAKELONG(nMax, nMin));
+ }
+
+ inline BOOL CSlider::SetTic(int nTic) const
+ // Sets a tick mark in the trackbar at the specified logical position.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(TBM_SETTIC, 0, nTic);
+ }
+
+ inline void CSlider::SetTicFreq(int nFreq) const
+ // Sets the interval frequency for tick marks in the trackbar.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_SETTICFREQ, (WPARAM)nFreq, 0);
+ }
+
+ inline int CSlider::SetTipSide(int nLocation) const
+ // Positions a ToolTip control used by the trackbar control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(TBM_SETTIPSIDE, (WPARAM)nLocation, 0);
+ }
+
+ inline void CSlider::SetToolTips(CWnd* pToolTip) const
+ // Assigns a ToolTip control to the trackbar control.
+ {
+ assert(IsWindow());
+ SendMessage(TBM_SETTOOLTIPS, (WPARAM)pToolTip->GetHwnd(), 0);
+ }
+
+ ////////////////////////////////////////
+ // Definitions for the CSpinButton class
+ //
+ inline int CSpinButton::GetAccel(int cAccels, LPUDACCEL paAccels) const
+ // Retrieves acceleration information for the up-down control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(UDM_GETACCEL, (WPARAM)cAccels, (LPARAM)paAccels);
+ }
+
+ inline int CSpinButton::GetBase() const
+ // Retrieves the current radix base (that is, either base 10 or 16) for the up-down control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(UDM_GETBASE, 0, 0);
+ }
+
+ inline CWnd* CSpinButton::GetBuddy() const
+ // Retrieves the handle to the current buddy window.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(UDM_GETBUDDY, 0, 0));
+ }
+
+ inline int CSpinButton::GetPos() const
+ // Retrieves the current position of the up-down control with 16-bit precision.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(UDM_GETPOS, 0, 0);
+ }
+
+ inline DWORD CSpinButton::GetRange() const
+ // Retrieves the minimum and maximum positions (range) for the up-down control.
+ {
+ assert(IsWindow());
+ return (DWORD)SendMessage(UDM_GETRANGE, 0, 0);
+ }
+
+ inline void CSpinButton::PreCreate(CREATESTRUCT &cs)
+ {
+ cs.style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VISIBLE |UDS_SETBUDDYINT;
+ }
+
+ inline void CSpinButton::PreRegisterClass(WNDCLASS &wc)
+ {
+ wc.lpszClassName = UPDOWN_CLASS;
+ }
+
+ inline BOOL CSpinButton::SetAccel(int cAccels, LPUDACCEL paAccels) const
+ // Sets the acceleration for the up-down control.
+ {
+ assert(IsWindow());
+ return (BOOL)SendMessage(UDM_SETACCEL, (WPARAM)cAccels, (LPARAM)paAccels);
+ }
+
+ inline int CSpinButton::SetBase(int nBase) const
+ // Sets the radix base for the up-down control.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(UDM_SETBASE, (WPARAM)nBase, 0);
+ }
+
+ inline CWnd* CSpinButton::SetBuddy(CWnd* pBuddy) const
+ // Sets the buddy window for the up-down control.
+ {
+ assert(IsWindow());
+ return FromHandle((HWND)SendMessage(UDM_SETBUDDY, (WPARAM)pBuddy->GetHwnd(), 0));
+ }
+
+ inline int CSpinButton::SetPos(int nPos) const
+ // Sets the current position for the up-down control with 16-bit precision.
+ {
+ assert(IsWindow());
+ return (int)SendMessage(UDM_SETPOS, 0, (LPARAM)MAKELONG ((short) nPos, 0));
+ }
+
+ inline void CSpinButton::SetRange(int nLower, int nUpper) const
+ // Sets the minimum and maximum positions (range) for the up-down control.
+ {
+ assert(IsWindow());
+ SendMessage(UDM_SETRANGE, 0, (LPARAM)MAKELONG(nUpper, nLower));
+ }
+
+} // namespace Win32xx
+
+#endif // define _WIN32XX_CONTROLS_H_
+
diff --git a/mmc_updater/depends/win32cpp/copyright.txt b/mmc_updater/depends/win32cpp/copyright.txt
new file mode 100644
index 00000000..d2222fa7
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/copyright.txt
@@ -0,0 +1,33 @@
+Win32++ Version 7.2
+Released: 5th AUgust 2011
+
+ David Nash
+ email: dnash@bigpond.net.au
+ url: https://sourceforge.net/projects/win32-framework
+
+
+Copyright (c) 2005-2011 David Nash
+
+Permission is hereby granted, free of charge, to
+any person obtaining a copy of this software and
+associated documentation files (the "Software"),
+to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/mmc_updater/depends/win32cpp/cstring.h b/mmc_updater/depends/win32cpp/cstring.h
new file mode 100644
index 00000000..faf574d5
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/cstring.h
@@ -0,0 +1,905 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+// Acknowledgements:
+// Thanks to Adam Szulc for his initial CString code.
+
+////////////////////////////////////////////////////////
+// cstring.h
+// Declaration of the cstring.h
+
+// This class is intended to provide a simple alternative to the MFC/ATL
+// CString class that ships with Microsoft compilers. The CString class
+// specified here is compatible with other compilers such as Borland 5.5
+// and MinGW.
+
+// Differences between this class and the MFC/ATL CString class
+// ------------------------------------------------------------
+// 1) The constructors for this class accepts only TCHARs. The various text conversion
+// functions can be used to convert from other character types to TCHARs.
+//
+// 2) This class is not reference counted, so these CStrings should be passed as
+// references or const references when used as function arguments. As a result there
+// is no need for functions like LockBuffer and UnLockBuffer.
+//
+// 3) The Format functions only accepts POD (Plain Old Data) arguments. It does not
+// accept arguments which are class or struct objects. In particular it does not
+// accept CString objects, unless these are cast to LPCTSTR.
+// This is demonstrates valid and invalid usage:
+// CString string1(_T("Hello World"));
+// CString string2;
+//
+// // This is invalid, and produces undefined behaviour.
+// string2.Format(_T("String1 is: %s"), string1); // No! you can't do this
+//
+// // This is ok
+// string2.Format(_T("String1 is: %s"), (LPCTSTR)string1); // Yes, this is correct
+//
+// Note: The MFC/ATL CString class uses a non portable hack to make its CString class
+// behave like a POD. Other compilers (such as the MinGW compiler) specifically
+// prohibit the use of non POD types for functions with variable argument lists.
+//
+// 4) This class provides a few additional functions:
+// b_str Returns a BSTR string. This an an alternative for casting to BSTR.
+// c_str Returns a const TCHAR string. This is an alternative for casting to LPCTSTR.
+// GetErrorString Assigns CString to the error string for the specified System Error Code
+// (from ::GetLastErrror() for example).
+// GetString Returns a reference to the underlying std::basic_string<TCHAR>. This
+// reference can be used to modify the string directly.
+
+
+
+#ifndef _WIN32XX_CSTRING_H_
+#define _WIN32XX_CSTRING_H_
+
+
+#include "wincore.h"
+
+
+namespace Win32xx
+{
+
+ class CString
+ {
+ // friend functions allow the left hand side to be something other than CString
+ friend CString operator + (const CString& string1, const CString& string2);
+ friend CString operator + (const CString& string, LPCTSTR pszText);
+ friend CString operator + (const CString& string, TCHAR ch);
+ friend CString operator + (LPCTSTR pszText, const CString& string);
+ friend CString operator + (TCHAR ch, const CString& string);
+
+ public:
+ CString();
+ ~CString();
+ CString(const CString& str);
+ CString(LPCTSTR pszText);
+ CString(TCHAR ch, int nLength = 1);
+ CString(LPCTSTR pszText, int nLength);
+
+ CString& operator = (const CString& str);
+ CString& operator = (const TCHAR ch);
+ CString& operator = (LPCTSTR pszText);
+ BOOL operator == (LPCTSTR pszText);
+ BOOL operator != (LPCTSTR pszText);
+ BOOL operator < (LPCTSTR pszText);
+ BOOL operator > (LPCTSTR pszText);
+ BOOL operator <= (LPCTSTR pszText);
+ BOOL operator >= (LPCTSTR pszText);
+ operator LPCTSTR() const;
+ operator BSTR() const;
+ TCHAR& operator [] (int nIndex);
+ CString& operator += (const CString& str);
+
+ // Attributes
+ BSTR b_str() const { return T2W(m_str.c_str()); } // alternative for casting to BSTR
+ LPCTSTR c_str() const { return m_str.c_str(); } // alternative for casting to LPCTSTR
+ tString& GetString() { return m_str; } // returns a reference to the underlying std::basic_string<TCHAR>
+ int GetLength() const { return (int)m_str.length(); } // returns the length in characters
+
+ // Operations
+ BSTR AllocSysString() const;
+ void AppendFormat(LPCTSTR pszFormat,...);
+ void AppendFormat(UINT nFormatID, ...);
+ int Compare(LPCTSTR pszText) const;
+ int CompareNoCase(LPCTSTR pszText) const;
+ int Delete(int nIndex, int nCount = 1);
+ int Find(TCHAR ch, int nIndex = 0 ) const;
+ int Find(LPCTSTR pszText, int nStart = 0) const;
+ int FindOneOf(LPCTSTR pszText) const;
+ void Format(UINT nID, ...);
+ void Format(LPCTSTR pszFormat,...);
+ void FormatV(LPCTSTR pszFormat, va_list args);
+ void FormatMessage(LPCTSTR pszFormat,...);
+ void FormatMessageV(LPCTSTR pszFormat, va_list args);
+ TCHAR GetAt(int nIndex) const;
+ LPTSTR GetBuffer(int nMinBufLength);
+ void GetErrorString(DWORD dwError);
+ void Empty();
+ int Insert(int nIndex, TCHAR ch);
+ int Insert(int nIndex, const CString& str);
+ BOOL IsEmpty() const;
+ CString Left(int nCount) const;
+ BOOL LoadString(UINT nID);
+ void MakeLower();
+ void MakeReverse();
+ void MakeUpper();
+ CString Mid(int nFirst) const;
+ CString Mid(int nFirst, int nCount) const;
+ void ReleaseBuffer( int nNewLength = -1 );
+ int Remove(LPCTSTR pszText);
+ int Replace(TCHAR chOld, TCHAR chNew);
+ int Replace(const LPCTSTR pszOld, LPCTSTR pszNew);
+ int ReverseFind(LPCTSTR pszText, int nStart = -1) const;
+ CString Right(int nCount) const;
+ void SetAt(int nIndex, TCHAR ch);
+ BSTR SetSysString(BSTR* pBstr) const;
+ CString SpanExcluding(LPCTSTR pszText) const;
+ CString SpanIncluding(LPCTSTR pszText) const;
+ CString Tokenize(LPCTSTR pszTokens, int& iStart) const;
+ void Trim();
+ void TrimLeft();
+ void TrimLeft(TCHAR chTarget);
+ void TrimLeft(LPCTSTR pszTargets);
+ void TrimRight();
+ void TrimRight(TCHAR chTarget);
+ void TrimRight(LPCTSTR pszTargets);
+ void Truncate(int nNewLength);
+
+#ifndef _WIN32_WCE
+ int Collate(LPCTSTR pszText) const;
+ int CollateNoCase(LPCTSTR pszText) const;
+ BOOL GetEnvironmentVariable(LPCTSTR pszVar);
+#endif
+
+ private:
+ tString m_str;
+ std::vector<TCHAR> m_buf;
+ };
+
+ inline CString::CString()
+ {
+ }
+
+ inline CString::~CString()
+ {
+ }
+
+ inline CString::CString(const CString& str)
+ {
+ m_str.assign(str);
+ }
+
+ inline CString::CString(LPCTSTR pszText)
+ {
+ m_str.assign(pszText);
+ }
+
+ inline CString::CString(TCHAR ch, int nLength)
+ {
+ m_str.assign(nLength, ch);
+ }
+
+ inline CString::CString(LPCTSTR pszText, int nLength)
+ {
+ m_str.assign(pszText, nLength);
+ }
+
+ inline CString& CString::operator = (const CString& str)
+ {
+ m_str.assign(str);
+ return *this;
+ }
+
+ inline CString& CString::operator = (const TCHAR ch)
+ {
+ m_str.assign(1, ch);
+ return *this;
+ }
+
+ inline CString& CString::operator = (LPCTSTR pszText)
+ {
+ m_str.assign(pszText);
+ return *this;
+ }
+
+ inline BOOL CString::operator == (LPCTSTR pszText)
+ // Returns TRUE if the strings have the same content
+ {
+ assert(pszText);
+ return (0 == Compare(pszText));
+ }
+
+ inline BOOL CString::operator != (LPCTSTR pszText)
+ // Returns TRUE if the strings have a different content
+ {
+ assert(pszText);
+ return Compare(pszText) != 0;
+ }
+
+ inline BOOL CString::operator < (LPCTSTR pszText)
+ {
+ assert(pszText);
+ return Compare(pszText) < 0;
+ }
+
+ inline BOOL CString::operator > (LPCTSTR pszText)
+ {
+ assert(pszText);
+ return Compare(pszText) > 0;
+ }
+
+ inline BOOL CString::operator <= (LPCTSTR pszText)
+ {
+ assert(pszText);
+ return Compare(pszText) <= 0;
+ }
+
+ inline BOOL CString::operator >= (LPCTSTR pszText)
+ {
+ assert(pszText);
+ return Compare(pszText) >= 0;
+ }
+
+ inline CString::operator LPCTSTR() const
+ {
+ return m_str.c_str();
+ }
+
+ inline TCHAR& CString::operator [] (int nIndex)
+ {
+ assert(nIndex >= 0);
+ assert(nIndex < GetLength());
+ return m_str[nIndex];
+ }
+
+ inline CString& CString::operator += (const CString& str)
+ {
+ m_str.append(str);
+ return *this;
+ }
+
+ inline BSTR CString::AllocSysString() const
+ // Allocates a BSTR from the CString content.
+ {
+ return ::SysAllocStringLen(T2W(m_str.c_str()), (UINT)m_str.size());
+ }
+
+ inline void CString::AppendFormat(LPCTSTR pszFormat,...)
+ // Appends formatted data to an the CString content.
+ {
+ CString str;
+ str.Format(pszFormat);
+ m_str.append(str);
+ }
+
+ inline void CString::AppendFormat(UINT nFormatID, ...)
+ // Appends formatted data to an the CString content.
+ {
+ CString str1;
+ CString str2;
+ if (str1.LoadString(nFormatID))
+ {
+ str2.Format(str1);
+ m_str.append(str2);
+ }
+ }
+
+#ifndef _WIN32_WCE
+ inline int CString::Collate(LPCTSTR pszText) const
+ // Performs a case sensitive comparison of the two strings using locale-specific information.
+ {
+ assert(pszText);
+ return _tcscoll(m_str.c_str(), pszText);
+ }
+
+ inline int CString::CollateNoCase(LPCTSTR pszText) const
+ // Performs a case insensitive comparison of the two strings using locale-specific information.
+ {
+ assert(pszText);
+ return _tcsicoll(m_str.c_str(), pszText);
+ }
+#endif // _WIN32_WCE
+
+ inline int CString::Compare(LPCTSTR pszText) const
+ // Performs a case sensitive comparison of the two strings.
+ {
+ assert(pszText);
+ return m_str.compare(pszText);
+ }
+
+ inline int CString::CompareNoCase(LPCTSTR pszText) const
+ // Performs a case insensitive comparison of the two strings.
+ {
+ assert(pszText);
+ return _tcsicmp(m_str.data(), pszText);
+ }
+
+ inline int CString::Delete(int nIndex, int nCount /* = 1 */)
+ // Deletes a character or characters from the string.
+ {
+ assert(nIndex >= 0);
+ assert(nCount >= 0);
+
+ m_str.erase(nIndex, nCount);
+ return (int)m_str.size();
+ }
+
+ inline void CString::Empty()
+ // Erases the contents of the string.
+ {
+ m_str.erase();
+ }
+
+ inline int CString::Find(TCHAR ch, int nIndex /* = 0 */) const
+ // Finds a character in the string.
+ {
+ assert(nIndex >= 0);
+ return (int)m_str.find(ch, nIndex);
+ }
+
+ inline int CString::Find(LPCTSTR pszText, int nIndex /* = 0 */) const
+ // Finds a substring within the string.
+ {
+ assert(pszText);
+ assert(nIndex >= 0);
+ return (int)m_str.find(pszText, nIndex);
+ }
+
+ inline int CString::FindOneOf(LPCTSTR pszText) const
+ // Finds the first matching character from a set.
+ {
+ assert(pszText);
+ return (int)m_str.find_first_of(pszText);
+ }
+
+ inline void CString::Format(LPCTSTR pszFormat,...)
+ // Formats the string as sprintf does.
+ {
+ va_list args;
+ va_start(args, pszFormat);
+ FormatV(pszFormat, args);
+ va_end(args);
+ }
+
+ inline void CString::Format(UINT nID, ...)
+ // Formats the string as sprintf does.
+ {
+ Empty();
+ CString str;
+ if (str.LoadString(nID))
+ Format(str);
+ }
+
+ inline void CString::FormatV(LPCTSTR pszFormat, va_list args)
+ // Formats the string using a variable list of arguments.
+ {
+ if (pszFormat)
+ {
+ int nResult = -1, nLength = 256;
+
+ // A vector is used to store the TCHAR array
+ std::vector<TCHAR> vBuffer;( nLength+1, _T('\0') );
+
+ while (-1 == nResult)
+ {
+ vBuffer.assign( nLength+1, _T('\0') );
+ nResult = _vsntprintf(&vBuffer[0], nLength, pszFormat, args);
+ nLength *= 2;
+ }
+ m_str.assign(&vBuffer[0]);
+ }
+ }
+
+ inline void CString::FormatMessage(LPCTSTR pszFormat,...)
+ // Formats a message string.
+ {
+ va_list args;
+ va_start(args, pszFormat);
+ FormatMessageV(pszFormat, args);
+ va_end(args);
+ }
+
+ inline void CString::FormatMessageV(LPCTSTR pszFormat, va_list args)
+ // Formats a message string using a variable argument list.
+ {
+ LPTSTR pszTemp = 0;
+ if (pszFormat)
+ {
+ DWORD dwResult = ::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, pszFormat, 0, 0, pszTemp, 0, &args);
+
+ if (0 == dwResult || 0 == pszTemp )
+ throw std::bad_alloc();
+
+ m_str = pszTemp;
+ LocalFree(pszTemp);
+ }
+ }
+
+ inline TCHAR CString::GetAt(int nIndex) const
+ // Returns the character at the specified location within the string.
+ {
+ assert(nIndex >= 0);
+ assert(nIndex < GetLength());
+ return m_str[nIndex];
+ }
+
+ inline LPTSTR CString::GetBuffer(int nMinBufLength)
+ // Creates a buffer of nMinBufLength charaters (+1 extra for NULL termination) and returns
+ // a pointer to this buffer. This buffer can be used by any function which accepts a LPTSTR.
+ // Care must be taken not to exceed the length of the buffer. Use ReleaseBuffer to safely
+ // copy this buffer back to the CString object.
+ //
+ // Note: The buffer uses a vector. Vectors are required to be contiguous in memory under
+ // the current standard, whereas std::strings do not have this requirement.
+ {
+ assert (nMinBufLength >= 0);
+
+ m_buf.assign(nMinBufLength + 1, _T('\0'));
+ tString::iterator it_end;
+
+ if (m_str.length() >= (size_t)nMinBufLength)
+ {
+ it_end = m_str.begin();
+ std::advance(it_end, nMinBufLength);
+ }
+ else
+ it_end = m_str.end();
+
+ std::copy(m_str.begin(), it_end, m_buf.begin());
+
+ return &m_buf[0];
+ }
+
+#ifndef _WIN32_WCE
+ inline BOOL CString::GetEnvironmentVariable(LPCTSTR pszVar)
+ // Sets the string to the value of the specified environment variable.
+ {
+ assert(pszVar);
+ Empty();
+
+ int nLength = ::GetEnvironmentVariable(pszVar, NULL, 0);
+ if (nLength > 0)
+ {
+ std::vector<TCHAR> vBuffer( nLength+1, _T('\0') );
+ ::GetEnvironmentVariable(pszVar, &vBuffer[0], nLength);
+ m_str = &vBuffer[0];
+ }
+
+ return (BOOL)nLength;
+ }
+#endif // _WIN32_WCE
+
+ inline void CString::GetErrorString(DWORD dwError)
+ // Returns the error string for the specified System Error Code (e.g from GetLastErrror).
+ {
+ m_str.erase();
+
+ if (dwError != 0)
+ {
+ TCHAR* pTemp = 0;
+ DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ ::FormatMessage(dwFlags, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pTemp, 1, NULL);
+ m_str.assign(pTemp);
+ ::LocalFree(pTemp);
+ }
+ }
+
+ inline int CString::Insert(int nIndex, TCHAR ch)
+ // Inserts a single character or a substring at the given index within the string.
+ {
+ assert(nIndex >= 0);
+ assert(ch);
+
+ m_str.insert(nIndex, &ch, 1);
+ return (int)m_str.size();
+ }
+
+ inline int CString::Insert(int nIndex, const CString& str)
+ // Inserts a single character or a substring at the given index within the string.
+ {
+ assert(nIndex >= 0);
+
+ m_str.insert(nIndex, str);
+ return (int)m_str.size();
+ }
+
+ inline BOOL CString::IsEmpty() const
+ // Returns TRUE if the string is empty
+ {
+ return m_str.empty();
+ }
+
+ inline CString CString::Left(int nCount) const
+ // Extracts the left part of a string.
+ {
+ assert(nCount >= 0);
+
+ CString str;
+ str.m_str.assign(c_str(), 0, nCount);
+ return str;
+ }
+
+ inline BOOL CString::LoadString(UINT nID)
+ // Loads the string from a Windows resource.
+ {
+ assert (GetApp());
+
+ int nSize = 64;
+ TCHAR* pTCharArray = 0;
+ std::vector<TCHAR> vString;
+ int nTChars = nSize;
+
+ Empty();
+
+ // Increase the size of our array in a loop until we load the entire string
+ // The ANSI and _UNICODE versions of LoadString behave differently. This technique works for both.
+ while ( nSize-1 <= nTChars )
+ {
+ nSize = nSize * 4;
+ vString.assign(nSize+1, _T('\0'));
+ pTCharArray = &vString[0];
+ nTChars = ::LoadString (GetApp()->GetResourceHandle(), nID, pTCharArray, nSize);
+ }
+
+ if (nTChars > 0)
+ m_str.assign(pTCharArray);
+
+ return (nTChars != 0);
+ }
+
+ inline void CString::MakeLower()
+ // Converts all the characters in this string to lowercase characters.
+ {
+ std::transform(m_str.begin(), m_str.end(), m_str.begin(), &::tolower);
+ }
+
+ inline void CString::MakeReverse()
+ // Reverses the string.
+ {
+ std::reverse(m_str.begin(), m_str.end());
+ }
+
+ inline void CString::MakeUpper()
+ // Converts all the characters in this string to uppercase characters.
+ {
+ std::transform(m_str.begin(), m_str.end(), m_str.begin(), &::toupper);
+ }
+
+ inline CString CString::Mid(int nFirst) const
+ // Extracts the middle part of a string.
+ {
+ return Mid(nFirst, GetLength());
+ }
+
+ inline CString CString::Mid(int nFirst, int nCount) const
+ // Extracts the middle part of a string.
+ {
+ assert(nFirst >= 0);
+ assert(nCount >= 0);
+
+ CString str;
+ str.m_str.assign(c_str(), nFirst, nFirst + nCount);
+ return str;
+ }
+
+ inline int CString::ReverseFind(LPCTSTR pszText, int nIndex /* = -1 */) const
+ // Search for a substring within the string, starting from the end.
+ {
+ assert(pszText);
+ return (int)m_str.rfind(pszText, nIndex);
+ }
+
+ inline void CString::SetAt(int nIndex, TCHAR ch)
+ // Sets the character at the specificed position to the specified value.
+ {
+ assert(nIndex >= 0);
+ assert(nIndex < GetLength());
+ m_str[nIndex] = ch;
+ }
+
+ inline void CString::ReleaseBuffer( int nNewLength /*= -1*/ )
+ // This copies the contents of the buffer (acquired by GetBuffer) to this CString,
+ // and releases the contents of the buffer. The default length of -1 copies from the
+ // buffer until a null terminator is reached. If the buffer doesn't contain a null
+ // terminator, you must specify the buffer's length.
+ {
+ assert (nNewLength > 0 || -1 == nNewLength);
+ assert (nNewLength < (int)m_buf.size());
+
+ if (-1 == nNewLength)
+ nNewLength = lstrlen(&m_buf[0]);
+ m_str.assign(nNewLength+1, _T('\0'));
+
+ std::vector<TCHAR>::iterator it_end = m_buf.begin();
+ std::advance(it_end, nNewLength);
+
+ std::copy(m_buf.begin(), it_end, m_str.begin());
+ m_buf.clear();
+ }
+
+ inline int CString::Remove(LPCTSTR pszText)
+ // Removes each occurrence of the specified substring from the string.
+ {
+ assert(pszText);
+
+ int nCount = 0;
+ size_t pos = 0;
+ while ((pos = m_str.find(pszText, pos)) != std::string::npos)
+ {
+ m_str.erase(pos, lstrlen(pszText));
+ ++nCount;
+ }
+ return nCount;
+ }
+
+ inline int CString::Replace(TCHAR chOld, TCHAR chNew)
+ // Replaces each occurance of the old character with the new character.
+ {
+ int nCount = 0;
+ tString::iterator it = m_str.begin();
+ while (it != m_str.end())
+ {
+ if (*it == chOld)
+ {
+ *it = chNew;
+ ++nCount;
+ }
+ ++it;
+ }
+ return nCount;
+ }
+
+ inline int CString::Replace(LPCTSTR pszOld, LPCTSTR pszNew)
+ // Replaces each occurance of the old substring with the new substring.
+ {
+ assert(pszOld);
+ assert(pszNew);
+
+ int nCount = 0;
+ size_t pos = 0;
+ while ((pos = m_str.find(pszOld, pos)) != std::string::npos)
+ {
+ m_str.replace(pos, lstrlen(pszOld), pszNew);
+ pos += lstrlen(pszNew);
+ ++nCount;
+ }
+ return nCount;
+ }
+
+ inline CString CString::Right(int nCount) const
+ // Extracts the right part of a string.
+ {
+ assert(nCount >= 0);
+
+ CString str;
+ str.m_str.assign(c_str(), m_str.size() - nCount, nCount);
+ return str;
+ }
+
+ inline BSTR CString::SetSysString(BSTR* pBstr) const
+ // Sets an existing BSTR object to the string.
+ {
+ assert(pBstr);
+
+ if ( !::SysReAllocStringLen(pBstr, T2W(m_str.c_str()), (UINT)m_str.length()) )
+ throw std::bad_alloc();
+
+ return *pBstr;
+ }
+
+ inline CString CString::SpanExcluding(LPCTSTR pszText) const
+ // Extracts characters from the string, starting with the first character,
+ // that are not in the set of characters identified by pszCharSet.
+ {
+ assert (pszText);
+
+ CString str;
+ size_t pos = 0;
+
+ while ((pos = m_str.find_first_not_of(pszText, pos)) != std::string::npos)
+ {
+ str.m_str.append(1, m_str[pos++]);
+ }
+
+ return str;
+ }
+
+ inline CString CString::SpanIncluding(LPCTSTR pszText) const
+ // Extracts a substring that contains only the characters in a set.
+ {
+ assert (pszText);
+
+ CString str;
+ size_t pos = 0;
+
+ while ((pos = m_str.find_first_of(pszText, pos)) != std::string::npos)
+ {
+ str.m_str.append(1, m_str[pos++]);
+ }
+
+ return str;
+ }
+
+ inline CString CString::Tokenize(LPCTSTR pszTokens, int& iStart) const
+ // Extracts specified tokens in a target string.
+ {
+ assert(pszTokens);
+ assert(iStart >= 0);
+
+ CString str;
+ size_t pos1 = m_str.find_first_not_of(pszTokens, iStart);
+ size_t pos2 = m_str.find_first_of(pszTokens, pos1);
+
+ iStart = (int)pos2 + 1;
+ if (pos2 == m_str.npos)
+ iStart = -1;
+
+ if (pos1 != m_str.npos)
+ str.m_str = m_str.substr(pos1, pos2-pos1);
+
+ return str;
+ }
+
+ inline void CString::Trim()
+ // Trims all leading and trailing whitespace characters from the string.
+ {
+ TrimLeft();
+ TrimRight();
+ }
+
+ inline void CString::TrimLeft()
+ // Trims leading whitespace characters from the string.
+ {
+ // This method is supported by the Borland 5.5 compiler
+ tString::iterator iter;
+ for (iter = m_str.begin(); iter < m_str.end(); ++iter)
+ {
+ if (!isspace(*iter))
+ break;
+ }
+
+ m_str.erase(m_str.begin(), iter);
+ }
+
+ inline void CString::TrimLeft(TCHAR chTarget)
+ // Trims the specified character from the beginning of the string.
+ {
+ m_str.erase(0, m_str.find_first_not_of(chTarget));
+ }
+
+ inline void CString::TrimLeft(LPCTSTR pszTargets)
+ // Trims the specified set of characters from the beginning of the string.
+ {
+ assert(pszTargets);
+ m_str.erase(0, m_str.find_first_not_of(pszTargets));
+ }
+
+ inline void CString::TrimRight()
+ // Trims trailing whitespace characters from the string.
+ {
+ // This method is supported by the Borland 5.5 compiler
+ tString::reverse_iterator riter;
+ for (riter = m_str.rbegin(); riter < m_str.rend(); ++riter)
+ {
+ if (!isspace(*riter))
+ break;
+ }
+
+ m_str.erase(riter.base(), m_str.end());
+ }
+
+ inline void CString::TrimRight(TCHAR chTarget)
+ // Trims the specified character from the end of the string.
+ {
+ size_t pos = m_str.find_last_not_of(chTarget);
+ if (pos != std::string::npos)
+ m_str.erase(++pos);
+ }
+
+ inline void CString::TrimRight(LPCTSTR pszTargets)
+ // Trims the specified set of characters from the end of the string.
+ {
+ assert(pszTargets);
+
+ size_t pos = m_str.find_last_not_of(pszTargets);
+ if (pos != std::string::npos)
+ m_str.erase(++pos);
+ }
+
+ inline void CString::Truncate(int nNewLength)
+ // Reduces the length of the string to the specified amount.
+ {
+ if (nNewLength < GetLength())
+ {
+ assert(nNewLength >= 0);
+ m_str.erase(nNewLength);
+ }
+ }
+
+
+ ///////////////////////////////////
+ // Global Functions
+ //
+
+ // friend functions of CString
+ inline CString operator + (const CString& string1, const CString& string2)
+ {
+ CString str(string1);
+ str.m_str.append(string2.m_str);
+ return str;
+ }
+
+ inline CString operator + (const CString& string, LPCTSTR pszText)
+ {
+ CString str(string);
+ str.m_str.append(pszText);
+ return str;
+ }
+
+ inline CString operator + (const CString& string, TCHAR ch)
+ {
+ CString str(string);
+ str.m_str.append(1, ch);
+ return str;
+ }
+
+ inline CString operator + (LPCTSTR pszText, const CString& string)
+ {
+ CString str(pszText);
+ str.m_str.append(string);
+ return str;
+ }
+
+ inline CString operator + (TCHAR ch, const CString& string)
+ {
+ CString str(ch);
+ str.m_str.append(string);
+ return str;
+ }
+
+ // Global LoadString
+ inline CString LoadString(UINT nID)
+ {
+ CString str;
+ str.LoadString(nID);
+ return str;
+ }
+
+
+} // namespace Win32xx
+
+#endif//_WIN32XX_CSTRING_H_
diff --git a/mmc_updater/depends/win32cpp/default_resource.h b/mmc_updater/depends/win32cpp/default_resource.h
new file mode 100644
index 00000000..b616a183
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/default_resource.h
@@ -0,0 +1,94 @@
+// This file contains the resource ID definitions for Win32++.
+
+
+// The resource ID for MENU, ICON, ToolBar Bitmap, Accelerator,
+// and Window Caption
+#define IDW_MAIN 51
+
+// Resource ID for the About dialog
+#define IDW_ABOUT 52
+
+// Resource IDs for menu items
+#define IDW_VIEW_TOOLBAR 53
+#define IDW_VIEW_STATUSBAR 54
+
+// Resource IDs for the Command Bands
+#define IDW_CMD_BANDS 55
+#define IDW_MENUBAR 56
+#define IDW_TOOLBAR 57
+
+// Resource ID for the Accelerator key
+#define IDW_QUIT 58
+
+// Resource IDs for MDI menu items
+#define IDW_MDI_CASCADE 60
+#define IDW_MDI_TILE 61
+#define IDW_MDI_ARRANGE 62
+#define IDW_MDI_CLOSEALL 63
+#define IDW_FIRSTCHILD 64
+#define IDW_CHILD2 65
+#define IDW_CHILD3 66
+#define IDW_CHILD4 67
+#define IDW_CHILD5 68
+#define IDW_CHILD6 69
+#define IDW_CHILD7 70
+#define IDW_CHILD8 71
+#define IDW_CHILD9 72
+#define IDW_CHILD10 73
+
+#define IDW_FILE_MRU_FILE1 75
+#define IDW_FILE_MRU_FILE2 76
+#define IDW_FILE_MRU_FILE3 77
+#define IDW_FILE_MRU_FILE4 78
+#define IDW_FILE_MRU_FILE5 79
+#define IDW_FILE_MRU_FILE6 80
+#define IDW_FILE_MRU_FILE7 81
+#define IDW_FILE_MRU_FILE8 82
+#define IDW_FILE_MRU_FILE9 83
+#define IDW_FILE_MRU_FILE10 84
+#define IDW_FILE_MRU_FILE11 85
+#define IDW_FILE_MRU_FILE12 86
+#define IDW_FILE_MRU_FILE13 87
+#define IDW_FILE_MRU_FILE14 88
+#define IDW_FILE_MRU_FILE15 89
+#define IDW_FILE_MRU_FILE16 90
+
+// Cursor Resources
+#define IDW_SPLITH 91
+#define IDW_SPLITV 92
+#define IDW_TRACK4WAY 93
+
+// Docking Bitmap Resources
+#define IDW_SDBOTTOM 94
+#define IDW_SDCENTER 95
+#define IDW_SDLEFT 96
+#define IDW_SDMIDDLE 97
+#define IDW_SDRIGHT 98
+#define IDW_SDTOP 99
+
+
+// A generic ID for any static control
+#ifndef IDC_STATIC
+ #define IDC_STATIC -1
+#endif
+
+
+
+// Notes about Resource IDs
+// * In general, resource IDs can have values from 1 to 65535. Programs with
+// resource IDs higher than 65535 aren't supported on Windows 95
+//
+// * CMenuBar uses resource IDs beginning from 0 for the top level menu items.
+// Win32++ leaves resource IDs below 51 unallocated for top level menu items.
+//
+// * Windows uses the icon with the lowest resource ID as the application's
+// icon. The application's icon is IDW_MAIN, which is the first resource ID
+// defined by Win32++.
+//
+// * When more than one static control is used in a dialog, the controls should
+// have a unique ID, unless a resource ID of -1 is used.
+//
+// * Users of Win32++ are advised to begin their resource IDs from 120 to
+// allow for possible expansion of Win32++.
+
+
diff --git a/mmc_updater/depends/win32cpp/default_resource.rc b/mmc_updater/depends/win32cpp/default_resource.rc
new file mode 100644
index 00000000..d53479f5
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/default_resource.rc
@@ -0,0 +1,250 @@
+// An example of a resource file
+//
+
+#include "resource.h"
+#include "windows.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+1 24 DISCARDABLE "res/Win32++.manifest"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+
+IDW_MAIN MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "New &View", IDM_FILE_NEWVIEW
+ MENUITEM SEPARATOR
+ MENUITEM "Recent Files", IDW_FILE_MRU_FILE1, GRAYED
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_FILE_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Undo\tCtrl+Z", IDM_EDIT_UNDO
+ MENUITEM "Redo\tShift+Ctrl+Z", IDM_EDIT_REDO
+ MENUITEM SEPARATOR
+ MENUITEM "Cut\tCtrl+X", IDM_EDIT_CUT
+ MENUITEM "Copy\tCtrl+C", IDM_EDIT_COPY
+ MENUITEM "Paste\tCtrl+V", IDM_EDIT_PASTE
+ MENUITEM "Delete\tDel", IDM_EDIT_DELETE
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "&Tool Bar", IDW_VIEW_TOOLBAR, CHECKED
+ MENUITEM "&Status Bar", IDW_VIEW_STATUSBAR, CHECKED
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About", IDM_HELP_ABOUT
+ END
+END
+
+MDIMENUVIEW MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "New &View", IDM_FILE_NEWVIEW
+ MENUITEM "&Close", IDM_FILE_CLOSE
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_FILE_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Undo", IDM_EDIT_UNDO
+ MENUITEM "Redo", IDM_EDIT_REDO
+ MENUITEM SEPARATOR
+ MENUITEM "Cu&t", IDM_EDIT_CUT
+ MENUITEM "&Copy", IDM_EDIT_COPY
+ MENUITEM "&Paste", IDM_EDIT_PASTE
+ MENUITEM "De&lete", IDM_EDIT_DELETE
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "Tool Bar", IDW_VIEW_TOOLBAR, CHECKED
+ MENUITEM "Status Bar", IDW_VIEW_STATUSBAR, CHECKED
+ END
+ POPUP "&Color"
+ BEGIN
+ MENUITEM "&Black", IDM_COLOR_BLACK
+ MENUITEM "&Red", IDM_COLOR_RED
+ MENUITEM "&Green", IDM_COLOR_GREEN
+ MENUITEM "B&lue", IDM_COLOR_BLUE
+ MENUITEM "&White", IDM_COLOR_WHITE
+ END
+ POPUP "&Window"
+ BEGIN
+ MENUITEM "&Cascade\tShift+F5", IDW_WINDOW_CASCADE
+ MENUITEM "&Tile\tShift+F4", IDW_WINDOW_TILE
+ MENUITEM "Arrange &Icons", IDW_WINDOW_ARRANGE
+ MENUITEM "Close &All", IDW_WINDOW_CLOSEALL
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "About", IDM_HELP_ABOUT
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDW_MAIN ICON "res/mdi.ico"
+IDI_VIEW ICON "res/view.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDW_MAIN BITMAP "res/toolbar.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDW_ABOUT DIALOGEX 0, 0, 186, 90
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "About"
+FONT 8, "MS Shell Dlg", 400, 0
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,68,49,50,14
+ CTEXT "MDI Frame",IDC_STATIC,60,22,64,11
+ ICON IDW_MAIN,0,4,4,20,20
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDW_MAIN ACCELERATORS
+BEGIN
+ "N", IDM_FILE_NEW, VIRTKEY, CONTROL, NOINVERT
+ "O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
+ "P", IDM_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT
+ "S", IDM_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT
+ "C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
+ "X", IDM_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
+ "V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
+ "Z", IDM_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT
+ "Y", IDM_EDIT_REDO, VIRTKEY, SHIFT, CONTROL, NOINVERT
+ VK_DELETE, IDM_EDIT_DELETE, VIRTKEY, NOINVERT
+END
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDW_MAIN "MDI Frame"
+END
+
+STRINGTABLE
+BEGIN
+ IDM_FILE_NEW "Create a New Document"
+ IDM_FILE_OPEN "Open Existing Document"
+ IDM_FILE_SAVE "Save the Document"
+ IDM_FILE_SAVEAS "Save the Document with a new name"
+ IDM_FILE_PRINT "Print the Document"
+ IDM_FILE_EXIT "End the Program"
+END
+
+STRINGTABLE
+BEGIN
+ IDM_EDIT_UNDO "Undo the last action"
+ IDM_EDIT_REDO "Redo the previously undone action"
+ IDM_EDIT_CUT "Cut the Selected Contents to the Clipboard"
+ IDM_EDIT_COPY "Copy the Selected Contents to the Clipboard"
+ IDM_EDIT_PASTE "Paste the Clipboard Contents to the Document"
+ IDM_EDIT_DELETE "Erase the selected Contents"
+ IDW_VIEW_TOOLBAR "Show or hide the tool bar"
+ IDW_VIEW_STATUSBAR "Show or hide the status bar"
+END
+
+STRINGTABLE
+BEGIN
+ IDM_HELP_ABOUT "Display Information about this program"
+END
+
+STRINGTABLE
+BEGIN
+ IDW_FIRSTCHILD "Activate this window"
+ IDW_CHILD2 "Activate this window"
+ IDW_CHILD3 "Activate this window"
+ IDW_CHILD4 "Activate this window"
+ IDW_CHILD5 "Activate this window"
+ IDW_CHILD6 "Activate this window"
+ IDW_CHILD7 "Activate this window"
+ IDW_CHILD8 "Activate this window"
+ IDW_CHILD9 "Activate this window"
+ IDW_CHILD10 "Select a window"
+END
+
+STRINGTABLE
+BEGIN
+ IDM_FILE_NEWVIEW "Create View MDI Child"
+ IDM_FILE_CLOSE "Close MDI Window"
+ IDM_COLOR_BLACK "Use Black Printing"
+ IDM_COLOR_RED "Use Red Printing"
+ IDM_COLOR_GREEN "Use Green Printing"
+ IDM_COLOR_BLUE "Use Blue Printing"
+ IDM_COLOR_WHITE "Use White Printing"
+ IDW_WINDOW_CASCADE "Cascade MDI Windows"
+ IDW_WINDOW_TILE "Tile MDI Windows"
+ IDW_WINDOW_ARRANGE "Arrange Icons"
+ IDW_WINDOW_CLOSEALL "Close All MDI Windows"
+END
+
+STRINGTABLE
+BEGIN
+ SC_CLOSE "Close the Window"
+ SC_MAXIMIZE "Maximize the Window"
+ SC_MINIMIZE "Minimize the WIndow"
+ SC_MOVE "Move the Window"
+ SC_NEXTWINDOW "Select Next Window"
+ SC_PREVWINDOW "Select Previous Window"
+ SC_RESTORE "Restore the Window"
+ SC_SIZE "Resize the Window"
+END
+
+STRINGTABLE
+BEGIN
+ IDW_FILE_MRU_FILE1 "Open this document"
+ IDW_FILE_MRU_FILE2 "Open this document"
+ IDW_FILE_MRU_FILE3 "Open this document"
+ IDW_FILE_MRU_FILE4 "Open this document"
+ IDW_FILE_MRU_FILE5 "Open this document"
+ IDW_FILE_MRU_FILE6 "Open this document"
+ IDW_FILE_MRU_FILE7 "Open this document"
+ IDW_FILE_MRU_FILE8 "Open this document"
+ IDW_FILE_MRU_FILE9 "Open this document"
+ IDW_FILE_MRU_FILE10 "Open this document"
+ IDW_FILE_MRU_FILE11 "Open this document"
+ IDW_FILE_MRU_FILE12 "Open this document"
+ IDW_FILE_MRU_FILE13 "Open this document"
+ IDW_FILE_MRU_FILE14 "Open this document"
+ IDW_FILE_MRU_FILE15 "Open this document"
+ IDW_FILE_MRU_FILE16 "Open this document"
+END
+
+
diff --git a/mmc_updater/depends/win32cpp/dialog.h b/mmc_updater/depends/win32cpp/dialog.h
new file mode 100644
index 00000000..e5123304
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/dialog.h
@@ -0,0 +1,876 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// dialog.h
+// Declaration of the CDialog class
+
+// CDialog adds support for dialogs to Win32++. Dialogs are specialised
+// windows which are a parent window for common controls. Common controls
+// are special window types such as buttons, edit controls, tree views,
+// list views, static text etc.
+
+// The layout of a dialog is typically defined in a resource script file
+// (often Resource.rc). While this script file can be constructed manually,
+// it is often created using a resource editor. If your compiler doesn't
+// include a resource editor, you might find ResEdit useful. It is a free
+// resource editor available for download at:
+// http://www.resedit.net/
+
+// CDialog supports modal and modeless dialogs. It also supports the creation
+// of dialogs defined in a resource script file, as well as those defined in
+// a dialog template.
+
+// Use the Dialog generic program as the starting point for your own dialog
+// applications.
+// The DlgSubclass sample demonstrates how to use subclassing to customise
+// the behaviour of common controls in a dialog.
+
+
+#ifndef _WIN32XX_DIALOG_H_
+#define _WIN32XX_DIALOG_H_
+
+#include "wincore.h"
+
+#ifndef SWP_NOCOPYBITS
+ #define SWP_NOCOPYBITS 0x0100
+#endif
+
+namespace Win32xx
+{
+
+ class CDialog : public CWnd
+ {
+ public:
+ CDialog(UINT nResID, CWnd* pParent = NULL);
+ CDialog(LPCTSTR lpszResName, CWnd* pParent = NULL);
+ CDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent = NULL);
+ virtual ~CDialog();
+
+ // You probably won't need to override these functions
+ virtual void AttachItem(int nID, CWnd& Wnd);
+ virtual HWND Create(CWnd* pParent = NULL);
+ virtual INT_PTR DoModal();
+ virtual HWND DoModeless();
+ virtual void SetDlgParent(CWnd* pParent);
+ BOOL IsModal() const { return m_IsModal; }
+ BOOL IsIndirect() const { return (NULL != m_lpTemplate); }
+
+ protected:
+ // These are the functions you might wish to override
+ virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual INT_PTR DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual void EndDialog(INT_PTR nResult);
+ virtual void OnCancel();
+ virtual BOOL OnInitDialog();
+ virtual void OnOK();
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+
+ // Can't override these functions
+ static INT_PTR CALLBACK StaticDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ #ifndef _WIN32_WCE
+ static LRESULT CALLBACK StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam);
+ #endif
+
+ private:
+ CDialog(const CDialog&); // Disable copy construction
+ CDialog& operator = (const CDialog&); // Disable assignment operator
+
+ BOOL m_IsModal; // a flag for modal dialogs
+ LPCTSTR m_lpszResName; // the resource name for the dialog
+ LPCDLGTEMPLATE m_lpTemplate; // the dialog template for indirect dialogs
+ HWND m_hParent; // handle to the dialogs's parent window
+ };
+
+
+#ifndef _WIN32_WCE
+
+ //////////////////////////////////////
+ // Declaration of the CResizer class
+ //
+ // The CResizer class can be used to rearrange a dialog's child
+ // windows when the dialog is resized.
+
+ // To use CResizer, follow the following steps:
+ // 1) Use Initialize to specify the dialog's CWnd, and min and max size.
+ // 3) Use AddChild for each child window
+ // 4) Call HandleMessage from within DialogProc.
+ //
+
+ // Resize Dialog Styles
+#define RD_STRETCH_WIDTH 0x0001 // The item has a variable width
+#define RD_STRETCH_HEIGHT 0x0002 // The item has a variable height
+
+ // Resize Dialog alignments
+ enum Alignment { topleft, topright, bottomleft, bottomright };
+
+ class CResizer
+ {
+ public:
+ CResizer() : m_pParent(0), m_xScrollPos(0), m_yScrollPos(0) {}
+ virtual ~CResizer() {}
+
+ virtual void AddChild(CWnd* pWnd, Alignment corner, DWORD dwStyle);
+ virtual void AddChild(HWND hWnd, Alignment corner, DWORD dwStyle);
+ virtual void HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual void Initialize(CWnd* pParent, RECT rcMin, RECT rcMax = CRect(0,0,0,0));
+ virtual void OnHScroll(WPARAM wParam, LPARAM lParam);
+ virtual void OnVScroll(WPARAM wParam, LPARAM lParam);
+ virtual void RecalcLayout();
+ CRect GetMinRect() const { return m_rcMin; }
+ CRect GetMaxRect() const { return m_rcMax; }
+
+ struct ResizeData
+ {
+ CRect rcInit;
+ CRect rcOld;
+ Alignment corner;
+ BOOL bFixedWidth;
+ BOOL bFixedHeight;
+ HWND hWnd;
+ };
+
+ private:
+ CWnd* m_pParent;
+ std::vector<ResizeData> m_vResizeData;
+
+ CRect m_rcInit;
+ CRect m_rcMin;
+ CRect m_rcMax;
+
+ int m_xScrollPos;
+ int m_yScrollPos;
+ };
+
+#endif
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+ ////////////////////////////////////
+ // Definitions for the CDialog class
+ //
+ inline CDialog::CDialog(LPCTSTR lpszResName, CWnd* pParent/* = NULL*/)
+ : m_IsModal(TRUE), m_lpszResName(lpszResName), m_lpTemplate(NULL)
+ {
+ m_hParent = pParent? pParent->GetHwnd() : NULL;
+ ::InitCommonControls();
+ }
+
+ inline CDialog::CDialog(UINT nResID, CWnd* pParent/* = NULL*/)
+ : m_IsModal(TRUE), m_lpszResName(MAKEINTRESOURCE (nResID)), m_lpTemplate(NULL)
+ {
+ m_hParent = pParent? pParent->GetHwnd() : NULL;
+ ::InitCommonControls();
+ }
+
+ //For indirect dialogs - created from a dialog box template in memory.
+ inline CDialog::CDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent/* = NULL*/)
+ : m_IsModal(TRUE), m_lpszResName(NULL), m_lpTemplate(lpTemplate)
+ {
+ m_hParent = pParent? pParent->GetHwnd() : NULL;
+ ::InitCommonControls();
+ }
+
+ inline CDialog::~CDialog()
+ {
+ if (m_hWnd != NULL)
+ {
+ if (IsModal())
+ ::EndDialog(m_hWnd, 0);
+ else
+ Destroy();
+ }
+ }
+
+ inline void CDialog::AttachItem(int nID, CWnd& Wnd)
+ // Attach a dialog item to a CWnd
+ {
+ Wnd.AttachDlgItem(nID, this);
+ }
+
+ inline HWND CDialog::Create(CWnd* pParent /* = NULL */)
+ {
+ // Allow a dialog to be used as a child window
+
+ assert(GetApp());
+ SetDlgParent(pParent);
+ return DoModeless();
+ }
+
+ inline INT_PTR CDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Override this function in your class derrived from CDialog if you wish to handle messages
+ // A typical function might look like this:
+
+ // switch (uMsg)
+ // {
+ // case MESSAGE1: // Some Windows API message
+ // OnMessage1(); // A user defined function
+ // break; // Also do default processing
+ // case MESSAGE2:
+ // OnMessage2();
+ // return x; // Don't do default processing, but instead return
+ // // a value recommended by the Windows API documentation
+ // }
+
+ // Always pass unhandled messages on to DialogProcDefault
+ return DialogProcDefault(uMsg, wParam, lParam);
+ }
+
+ inline INT_PTR CDialog::DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // All DialogProc functions should pass unhandled messages to this function
+ {
+ LRESULT lr = 0;
+
+ switch (uMsg)
+ {
+ case UWM_CLEANUPTEMPS:
+ {
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ pTLSData->vTmpWnds.clear();
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ // Center the dialog
+ CenterWindow();
+ }
+ return OnInitDialog();
+ case WM_COMMAND:
+ switch (LOWORD (wParam))
+ {
+ case IDOK:
+ OnOK();
+ return TRUE;
+ case IDCANCEL:
+ OnCancel();
+ return TRUE;
+ default:
+ {
+ // Refelect this message if it's from a control
+ CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam);
+ if (pWnd != NULL)
+ lr = pWnd->OnCommand(wParam, lParam);
+
+ // Handle user commands
+ if (!lr)
+ lr = OnCommand(wParam, lParam);
+
+ if (lr) return 0L;
+ }
+ break; // Some commands require default processing
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ // Do Notification reflection if it came from a CWnd object
+ HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom;
+ CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom);
+
+ if (pWndFrom != NULL)
+ lr = pWndFrom->OnNotifyReflect(wParam, lParam);
+ else
+ {
+ // Some controls (eg ListView) have child windows.
+ // Reflect those notifications too.
+ CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom));
+ if (pWndFromParent != NULL)
+ lr = pWndFromParent->OnNotifyReflect(wParam, lParam);
+ }
+
+ // Handle user notifications
+ if (!lr) lr = OnNotify(wParam, lParam);
+
+ // Set the return code for notifications
+ if (IsWindow())
+ SetWindowLongPtr(DWLP_MSGRESULT, (LONG_PTR)lr);
+
+ return (BOOL)lr;
+ }
+
+ case WM_PAINT:
+ {
+ if (::GetUpdateRect(m_hWnd, NULL, FALSE))
+ {
+ CPaintDC dc(this);
+ OnDraw(&dc);
+ }
+ else
+ // RedrawWindow can require repainting without an update rect
+ {
+ CClientDC dc(this);
+ OnDraw(&dc);
+ }
+
+ break;
+ }
+
+ case WM_ERASEBKGND:
+ {
+ CDC dc((HDC)wParam);
+ BOOL bResult = OnEraseBkgnd(&dc);
+ dc.Detach();
+ if (bResult) return TRUE;
+ }
+ break;
+
+ // A set of messages to be reflected back to the control that generated them
+ case WM_CTLCOLORBTN:
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLORLISTBOX:
+ case WM_CTLCOLORSCROLLBAR:
+ case WM_CTLCOLORSTATIC:
+ case WM_DRAWITEM:
+ case WM_MEASUREITEM:
+ case WM_DELETEITEM:
+ case WM_COMPAREITEM:
+ case WM_CHARTOITEM:
+ case WM_VKEYTOITEM:
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ case WM_PARENTNOTIFY:
+ return MessageReflect(m_hWnd, uMsg, wParam, lParam);
+
+ } // switch(uMsg)
+ return FALSE;
+
+ } // INT_PTR CALLBACK CDialog::DialogProc(...)
+
+ inline INT_PTR CDialog::DoModal()
+ {
+ // Create a modal dialog
+ // A modal dialog box must be closed by the user before the application continues
+
+ assert( GetApp() ); // Test if Win32++ has been started
+ assert(!::IsWindow(m_hWnd)); // Only one window per CWnd instance allowed
+
+ INT_PTR nResult = 0;
+
+ try
+ {
+ m_IsModal=TRUE;
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+
+ #ifndef _WIN32_WCE
+ BOOL IsHookedHere = FALSE;
+ if (NULL == pTLSData->hHook )
+ {
+ pTLSData->hHook = ::SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC)StaticMsgHook, NULL, ::GetCurrentThreadId());
+ IsHookedHere = TRUE;
+ }
+ #endif
+
+ HINSTANCE hInstance = GetApp()->GetInstanceHandle();
+ pTLSData->pCWnd = this;
+
+ // Create a modal dialog
+ if (IsIndirect())
+ nResult = ::DialogBoxIndirect(hInstance, m_lpTemplate, m_hParent, (DLGPROC)CDialog::StaticDialogProc);
+ else
+ {
+ if (::FindResource(GetApp()->GetResourceHandle(), m_lpszResName, RT_DIALOG))
+ hInstance = GetApp()->GetResourceHandle();
+ nResult = ::DialogBox(hInstance, m_lpszResName, m_hParent, (DLGPROC)CDialog::StaticDialogProc);
+ }
+
+ // Tidy up
+ m_hWnd = NULL;
+ pTLSData->pCWnd = NULL;
+ GetApp()->CleanupTemps();
+
+ #ifndef _WIN32_WCE
+ if (IsHookedHere)
+ {
+ ::UnhookWindowsHookEx(pTLSData->hHook);
+ pTLSData->hHook = NULL;
+ }
+ #endif
+
+ if (nResult == -1)
+ throw CWinException(_T("Failed to create modal dialog box"));
+
+ }
+
+ catch (const CWinException &e)
+ {
+ TRACE(_T("\n*** Failed to create dialog ***\n"));
+ e.what(); // Display the last error message.
+
+ // eat the exception (don't rethrow)
+ }
+
+ return nResult;
+ }
+
+ inline HWND CDialog::DoModeless()
+ {
+ assert( GetApp() ); // Test if Win32++ has been started
+ assert(!::IsWindow(m_hWnd)); // Only one window per CWnd instance allowed
+
+ try
+ {
+ m_IsModal=FALSE;
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+
+ // Store the CWnd pointer in Thread Local Storage
+ pTLSData->pCWnd = this;
+
+ HINSTANCE hInstance = GetApp()->GetInstanceHandle();
+
+ // Create a modeless dialog
+ if (IsIndirect())
+ m_hWnd = ::CreateDialogIndirect(hInstance, m_lpTemplate, m_hParent, (DLGPROC)CDialog::StaticDialogProc);
+ else
+ {
+ if (::FindResource(GetApp()->GetResourceHandle(), m_lpszResName, RT_DIALOG))
+ hInstance = GetApp()->GetResourceHandle();
+
+ m_hWnd = ::CreateDialog(hInstance, m_lpszResName, m_hParent, (DLGPROC)CDialog::StaticDialogProc);
+ }
+
+ // Tidy up
+ pTLSData->pCWnd = NULL;
+
+ // Now handle dialog creation failure
+ if (!m_hWnd)
+ throw CWinException(_T("Failed to create dialog"));
+ }
+
+ catch (const CWinException &e)
+ {
+ TRACE(_T("\n*** Failed to create dialog ***\n"));
+ e.what(); // Display the last error message.
+
+ // eat the exception (don't rethrow)
+ }
+
+ return m_hWnd;
+ }
+
+ inline void CDialog::EndDialog(INT_PTR nResult)
+ {
+ assert(::IsWindow(m_hWnd));
+
+ if (IsModal())
+ ::EndDialog(m_hWnd, nResult);
+ else
+ Destroy();
+
+ m_hWnd = NULL;
+ }
+
+ inline void CDialog::OnCancel()
+ {
+ // Override to customize OnCancel behaviour
+ EndDialog(IDCANCEL);
+ }
+
+ inline BOOL CDialog::OnInitDialog()
+ {
+ // Called when the dialog is initialized
+ // Override it in your derived class to automatically perform tasks
+ // The return value is used by WM_INITDIALOG
+
+ return TRUE;
+ }
+
+ inline void CDialog::OnOK()
+ {
+ // Override to customize OnOK behaviour
+ EndDialog(IDOK);
+ }
+
+ inline BOOL CDialog::PreTranslateMessage(MSG* pMsg)
+ {
+ // allow the dialog to translate keyboard input
+ if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST))
+ {
+ // Process dialog keystrokes for modeless dialogs
+ if (!IsModal())
+ {
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ if (NULL == pTLSData->hHook)
+ {
+ if (IsDialogMessage(pMsg))
+ return TRUE;
+ }
+ else
+ {
+ // A modal message loop is running so we can't do IsDialogMessage.
+ // Avoid having modal dialogs create other windows, because those
+ // windows will then use the modal dialog's special message loop.
+ }
+ }
+ }
+
+ return FALSE;
+ }
+
+ inline void CDialog::SetDlgParent(CWnd* pParent)
+ // Allows the parent of the dialog to be set before the dialog is created
+ {
+ m_hParent = pParent? pParent->GetHwnd() : NULL;
+ }
+
+ inline INT_PTR CALLBACK CDialog::StaticDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Find the CWnd pointer mapped to this HWND
+ CDialog* w = (CDialog*)GetApp()->GetCWndFromMap(hWnd);
+ if (0 == w)
+ {
+ // The HWND wasn't in the map, so add it now
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ assert(pTLSData);
+
+ // Retrieve pointer to CWnd object from Thread Local Storage TLS
+ w = (CDialog*)pTLSData->pCWnd;
+ assert(w);
+ pTLSData->pCWnd = NULL;
+
+ // Store the Window pointer into the HWND map
+ w->m_hWnd = hWnd;
+ w->AddToMap();
+ }
+
+ return w->DialogProc(uMsg, wParam, lParam);
+
+ } // INT_PTR CALLBACK CDialog::StaticDialogProc(...)
+
+#ifndef _WIN32_WCE
+ inline LRESULT CALLBACK CDialog::StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam)
+ {
+ // Used by Modal Dialogs to PreTranslate Messages
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+
+ if (nCode == MSGF_DIALOGBOX)
+ {
+ MSG* lpMsg = (MSG*) lParam;
+
+ // only pre-translate keyboard events
+ if ((lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST))
+ {
+ for (HWND hWnd = lpMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
+ {
+ CDialog* pDialog = (CDialog*)GetApp()->GetCWndFromMap(hWnd);
+ if (pDialog && (lstrcmp(pDialog->GetClassName(), _T("#32770")) == 0)) // only for dialogs
+ {
+ pDialog->PreTranslateMessage(lpMsg);
+ break;
+ }
+ }
+ }
+ }
+
+ return ::CallNextHookEx(pTLSData->hHook, nCode, wParam, lParam);
+ }
+#endif
+
+
+
+#ifndef _WIN32_WCE
+
+ /////////////////////////////////////
+ // Definitions for the CResizer class
+ //
+
+ void inline CResizer::AddChild(CWnd* pWnd, Alignment corner, DWORD dwStyle)
+ // Adds a child window (usually a dialog control) to the set of windows managed by
+ // the Resizer.
+ //
+ // The alignment corner should be set to the closest corner of the dialog. Allowed
+ // values are topleft, topright, bottomleft, and bottomright.
+ // Set bFixedWidth to TRUE if the width should be fixed instead of variable.
+ // Set bFixedHeight to TRUE if the height should be fixed instead of variable.
+ {
+ ResizeData rd;
+ rd.corner = corner;
+ rd.bFixedWidth = !(dwStyle & RD_STRETCH_WIDTH);
+ rd.bFixedHeight = !(dwStyle & RD_STRETCH_HEIGHT);
+ CRect rcInit = pWnd->GetWindowRect();
+ m_pParent->ScreenToClient(rcInit);
+ rd.rcInit = rcInit;
+ rd.hWnd = pWnd->GetHwnd();
+
+ m_vResizeData.insert(m_vResizeData.begin(), rd);
+ }
+
+ void inline CResizer::AddChild(HWND hWnd, Alignment corner, DWORD dwStyle)
+ // Adds a child window (usually a dialog control) to the set of windows managed by
+ // the Resizer.
+ {
+ AddChild(FromHandle(hWnd), corner, dwStyle);
+ }
+
+ inline void CResizer::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_SIZE:
+ RecalcLayout();
+ break;
+
+ case WM_HSCROLL:
+ if (0 == lParam)
+ OnHScroll(wParam, lParam);
+ break;
+
+ case WM_VSCROLL:
+ if (0 == lParam)
+ OnVScroll(wParam, lParam);
+ break;
+ }
+ }
+
+ void inline CResizer::Initialize(CWnd* pParent, RECT rcMin, RECT rcMax)
+ // Sets up the Resizer by specifying the parent window (usually a dialog),
+ // and the minimum and maximum allowed rectangle sizes.
+ {
+ assert (NULL != pParent);
+
+ m_pParent = pParent;
+ m_rcInit = pParent->GetClientRect();
+ m_rcMin = rcMin;
+ m_rcMax = rcMax;
+
+ // Add scroll bar support to the parent window
+ DWORD dwStyle = (DWORD)m_pParent->GetClassLongPtr(GCL_STYLE);
+ dwStyle |= WS_HSCROLL | WS_VSCROLL;
+ m_pParent->SetClassLongPtr(GCL_STYLE, dwStyle);
+ }
+
+ void inline CResizer::OnHScroll(WPARAM wParam, LPARAM /*lParam*/)
+ {
+ int xNewPos;
+
+ switch (LOWORD(wParam))
+ {
+ case SB_PAGEUP: // User clicked the scroll bar shaft left of the scroll box.
+ xNewPos = m_xScrollPos - 50;
+ break;
+
+ case SB_PAGEDOWN: // User clicked the scroll bar shaft right of the scroll box.
+ xNewPos = m_xScrollPos + 50;
+ break;
+
+ case SB_LINEUP: // User clicked the left arrow.
+ xNewPos = m_xScrollPos - 5;
+ break;
+
+ case SB_LINEDOWN: // User clicked the right arrow.
+ xNewPos = m_xScrollPos + 5;
+ break;
+
+ case SB_THUMBPOSITION: // User dragged the scroll box.
+ xNewPos = HIWORD(wParam);
+ break;
+
+ case SB_THUMBTRACK: // User dragging the scroll box.
+ xNewPos = HIWORD(wParam);
+ break;
+
+ default:
+ xNewPos = m_xScrollPos;
+ }
+
+ // Scroll the window.
+ xNewPos = MAX(0, xNewPos);
+ xNewPos = MIN( xNewPos, GetMinRect().Width() - m_pParent->GetClientRect().Width() );
+ int xDelta = xNewPos - m_xScrollPos;
+ m_xScrollPos = xNewPos;
+ m_pParent->ScrollWindow(-xDelta, 0, NULL, NULL);
+
+ // Reset the scroll bar.
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = m_xScrollPos;
+ m_pParent->SetScrollInfo(SB_HORZ, si, TRUE);
+ }
+
+ void inline CResizer::OnVScroll(WPARAM wParam, LPARAM /*lParam*/)
+ {
+ int yNewPos;
+
+ switch (LOWORD(wParam))
+ {
+ case SB_PAGEUP: // User clicked the scroll bar shaft above the scroll box.
+ yNewPos = m_yScrollPos - 50;
+ break;
+
+ case SB_PAGEDOWN: // User clicked the scroll bar shaft below the scroll box.
+ yNewPos = m_yScrollPos + 50;
+ break;
+
+ case SB_LINEUP: // User clicked the top arrow.
+ yNewPos = m_yScrollPos - 5;
+ break;
+
+ case SB_LINEDOWN: // User clicked the bottom arrow.
+ yNewPos = m_yScrollPos + 5;
+ break;
+
+ case SB_THUMBPOSITION: // User dragged the scroll box.
+ yNewPos = HIWORD(wParam);
+ break;
+
+ case SB_THUMBTRACK: // User dragging the scroll box.
+ yNewPos = HIWORD(wParam);
+ break;
+
+ default:
+ yNewPos = m_yScrollPos;
+ }
+
+ // Scroll the window.
+ yNewPos = MAX(0, yNewPos);
+ yNewPos = MIN( yNewPos, GetMinRect().Height() - m_pParent->GetClientRect().Height() );
+ int yDelta = yNewPos - m_yScrollPos;
+ m_yScrollPos = yNewPos;
+ m_pParent->ScrollWindow(0, -yDelta, NULL, NULL);
+
+ // Reset the scroll bar.
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = m_yScrollPos;
+ m_pParent->SetScrollInfo(SB_VERT, si, TRUE);
+ }
+
+ void inline CResizer::RecalcLayout()
+ // Repositions the child windows. Call this function when handling
+ // the WM_SIZE message in the parent window.
+ {
+ assert (m_rcInit.Width() > 0 && m_rcInit.Height() > 0);
+ assert (NULL != m_pParent);
+
+ CRect rcCurrent = m_pParent->GetClientRect();
+
+ // Adjust the scrolling if required
+ m_xScrollPos = MIN(m_xScrollPos, MAX(0, m_rcMin.Width() - rcCurrent.Width() ) );
+ m_yScrollPos = MIN(m_yScrollPos, MAX(0, m_rcMin.Height() - rcCurrent.Height()) );
+ SCROLLINFO si = {0};
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
+ si.nMax = m_rcMin.Width();
+ si.nPage = rcCurrent.Width();
+ si.nPos = m_xScrollPos;
+ m_pParent->SetScrollInfo(SB_HORZ, si, TRUE);
+ si.nMax = m_rcMin.Height();
+ si.nPage = rcCurrent.Height();
+ si.nPos = m_yScrollPos;
+ m_pParent->SetScrollInfo(SB_VERT, si, TRUE);
+
+ rcCurrent.right = MAX( rcCurrent.Width(), m_rcMin.Width() );
+ rcCurrent.bottom = MAX( rcCurrent.Height(), m_rcMin.Height() );
+ if (!m_rcMax.IsRectEmpty())
+ {
+ rcCurrent.right = MIN( rcCurrent.Width(), m_rcMax.Width() );
+ rcCurrent.bottom = MIN( rcCurrent.Height(), m_rcMax.Height() );
+ }
+
+ // Declare an iterator to step through the vector
+ std::vector<ResizeData>::iterator iter;
+
+ for (iter = m_vResizeData.begin(); iter < m_vResizeData.end(); ++iter)
+ {
+ int left = 0;
+ int top = 0;
+ int width = 0;
+ int height = 0;
+
+ // Calculate the new size and position of the child window
+ switch( (*iter).corner )
+ {
+ case topleft:
+ width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width();
+ height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height();
+ left = (*iter).rcInit.left;
+ top = (*iter).rcInit.top;
+ break;
+ case topright:
+ width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width();
+ height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height();
+ left = (*iter).rcInit.right - width - m_rcInit.Width() + rcCurrent.Width();
+ top = (*iter).rcInit.top;
+ break;
+ case bottomleft:
+ width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width();
+ height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height();
+ left = (*iter).rcInit.left;
+ top = (*iter).rcInit.bottom - height - m_rcInit.Height() + rcCurrent.Height();
+ break;
+ case bottomright:
+ width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width();
+ height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height();
+ left = (*iter).rcInit.right - width - m_rcInit.Width() + rcCurrent.Width();
+ top = (*iter).rcInit.bottom - height - m_rcInit.Height() + rcCurrent.Height();
+ break;
+ }
+
+ // Position the child window.
+ CRect rc(left - m_xScrollPos, top - m_yScrollPos, left + width - m_xScrollPos, top + height - m_yScrollPos);
+ if ( rc != (*iter).rcOld)
+ {
+ CWnd* pWnd = FromHandle((*iter).hWnd);
+ CWnd *pWndPrev = pWnd->GetWindow(GW_HWNDPREV); // Trick to maintain the original tab order.
+ HWND hWnd = pWndPrev ? pWndPrev->GetHwnd():NULL;
+ pWnd->SetWindowPos(hWnd, rc, SWP_NOCOPYBITS);
+ (*iter).rcOld = rc;
+ }
+ }
+ }
+
+#endif // #ifndef _WIN32_WCE
+
+} // namespace Win32xx
+
+
+
+#endif // _WIN32XX_DIALOG_H_
+
diff --git a/mmc_updater/depends/win32cpp/docking.h b/mmc_updater/depends/win32cpp/docking.h
new file mode 100644
index 00000000..9e7c4486
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/docking.h
@@ -0,0 +1,4214 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////
+// docking.h
+// Declaration of the CDocker class
+
+#ifndef _WIN32XX_DOCKING_H_
+#define _WIN32XX_DOCKING_H_
+
+
+#include "wincore.h"
+#include "gdi.h"
+#include "toolbar.h"
+#include "tab.h"
+#include "frame.h"
+#include "default_resource.h"
+
+
+// Docking Styles
+#define DS_DOCKED_LEFT 0x0001 // Dock the child left
+#define DS_DOCKED_RIGHT 0x0002 // Dock the child right
+#define DS_DOCKED_TOP 0x0004 // Dock the child top
+#define DS_DOCKED_BOTTOM 0x0008 // Dock the child bottom
+#define DS_NO_DOCKCHILD_LEFT 0x0010 // Prevent a child docking left
+#define DS_NO_DOCKCHILD_RIGHT 0x0020 // Prevent a child docking right
+#define DS_NO_DOCKCHILD_TOP 0x0040 // Prevent a child docking at the top
+#define DS_NO_DOCKCHILD_BOTTOM 0x0080 // Prevent a child docking at the bottom
+#define DS_NO_RESIZE 0x0100 // Prevent resizing
+#define DS_NO_CAPTION 0x0200 // Prevent display of caption when docked
+#define DS_NO_CLOSE 0x0400 // Prevent closing of a docker while docked
+#define DS_NO_UNDOCK 0x0800 // Prevent undocking and dock closing
+#define DS_CLIENTEDGE 0x1000 // Has a 3D border when docked
+#define DS_FIXED_RESIZE 0x2000 // Perfomed a fixed resize instead of a proportional resize on dock children
+#define DS_DOCKED_CONTAINER 0x4000 // Dock a container within a container
+#define DS_DOCKED_LEFTMOST 0x10000 // Leftmost outer docking
+#define DS_DOCKED_RIGHTMOST 0x20000 // Rightmost outer docking
+#define DS_DOCKED_TOPMOST 0x40000 // Topmost outer docking
+#define DS_DOCKED_BOTTOMMOST 0x80000 // Bottommost outer docking
+
+// Required for Dev-C++
+#ifndef TME_NONCLIENT
+ #define TME_NONCLIENT 0x00000010
+#endif
+#ifndef TME_LEAVE
+ #define TME_LEAVE 0x000000002
+#endif
+#ifndef WM_NCMOUSELEAVE
+ #define WM_NCMOUSELEAVE 0x000002A2
+#endif
+
+namespace Win32xx
+{
+ // Class declarations
+ class CDockContainer;
+ class CDocker;
+
+ typedef Shared_Ptr<CDocker> DockPtr;
+
+ struct ContainerInfo
+ {
+ TCHAR szTitle[MAX_MENU_STRING];
+ int iImage;
+ CDockContainer* pContainer;
+ };
+
+ ///////////////////////////////////////
+ // Declaration of the CDockContainer class
+ // A CDockContainer is a CTab window. A CTab has a view window, and optionally a toolbar control.
+ // A top level CDockContainer can contain other CDockContainers. The view for each container
+ // (including the top level container) along with possibly its toolbar, is displayed
+ // within the container parent's view page.
+ class CDockContainer : public CTab
+ {
+ public:
+
+ // Nested class. This is the Wnd for the window displayed over the client area
+ // of the tab control. The toolbar and view window are child windows of the
+ // viewpage window. Only the ViewPage of the parent CDockContainer is displayed. It's
+ // contents are updated with the view window of the relevant container whenever
+ // a different tab is selected.
+ class CViewPage : public CWnd
+ {
+
+ public:
+ CViewPage() : m_pView(NULL), m_pTab(NULL) {}
+ virtual ~CViewPage() {}
+ virtual CToolBar& GetToolBar() const {return (CToolBar&)m_ToolBar;}
+ virtual CWnd* GetView() const {return m_pView;}
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+ virtual void OnCreate();
+ virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual void RecalcLayout();
+ virtual void SetView(CWnd& wndView);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ CWnd* GetTabCtrl() const { return m_pTab;}
+
+ private:
+ CToolBar m_ToolBar;
+ tString m_tsTooltip;
+ CWnd* m_pView;
+ CWnd* m_pTab;
+ };
+
+ public:
+ CDockContainer();
+ virtual ~CDockContainer();
+ virtual void AddContainer(CDockContainer* pContainer);
+ virtual void AddToolBarButton(UINT nID, BOOL bEnabled = TRUE);
+ virtual CDockContainer* GetContainerFromIndex(UINT nPage);
+ virtual CDockContainer* GetContainerFromView(CWnd* pView) const;
+ virtual int GetContainerIndex(CDockContainer* pContainer);
+ virtual SIZE GetMaxTabTextSize();
+ virtual CViewPage& GetViewPage() const { return (CViewPage&)m_ViewPage; }
+ virtual void RecalcLayout();
+ virtual void RemoveContainer(CDockContainer* pWnd);
+ virtual void SelectPage(int nPage);
+ virtual void SetTabSize();
+ virtual void SetupToolBar();
+
+ // Attributes
+ CDockContainer* GetActiveContainer() const {return GetContainerFromView(GetActiveView());}
+ CWnd* GetActiveView() const;
+ std::vector<ContainerInfo>& GetAllContainers() const {return m_pContainerParent->m_vContainerInfo;}
+ CDockContainer* GetContainerParent() const { return m_pContainerParent; }
+ CString& GetDockCaption() const { return (CString&)m_csCaption; }
+ HICON GetTabIcon() const { return m_hTabIcon; }
+ LPCTSTR GetTabText() const { return m_tsTabText.c_str(); }
+ virtual CToolBar& GetToolBar() const { return GetViewPage().GetToolBar(); }
+ CWnd* GetView() const { return GetViewPage().GetView(); }
+ void SetActiveContainer(CDockContainer* pContainer);
+ void SetDockCaption(LPCTSTR szCaption) { m_csCaption = szCaption; }
+ void SetTabIcon(HICON hTabIcon) { m_hTabIcon = hTabIcon; }
+ void SetTabIcon(UINT nID_Icon);
+ void SetTabIcon(int i, HICON hIcon) { CTab::SetTabIcon(i, hIcon); }
+ void SetTabText(LPCTSTR szText) { m_tsTabText = szText; }
+ void SetTabText(UINT nTab, LPCTSTR szText);
+ void SetView(CWnd& Wnd);
+
+ protected:
+ virtual void OnCreate();
+ virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam);
+ virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam);
+ virtual void OnMouseLeave(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam);
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ std::vector<ContainerInfo> m_vContainerInfo;
+ tString m_tsTabText;
+ CString m_csCaption;
+ CViewPage m_ViewPage;
+ int m_iCurrentPage;
+ CDockContainer* m_pContainerParent;
+ HICON m_hTabIcon;
+ int m_nTabPressed;
+
+ };
+
+ typedef struct DRAGPOS
+ {
+ NMHDR hdr;
+ POINT ptPos;
+ UINT DockZone;
+ } *LPDRAGPOS;
+
+
+ /////////////////////////////////////////
+ // Declaration of the CDocker class
+ // A CDocker window allows other CDocker windows to be "docked" inside it.
+ // A CDocker can dock on the top, left, right or bottom side of a parent CDocker.
+ // There is no theoretical limit to the number of CDockers within CDockers.
+ class CDocker : public CWnd
+ {
+ public:
+ // A nested class for the splitter bar that seperates the docked panes.
+ class CDockBar : public CWnd
+ {
+ public:
+ CDockBar();
+ virtual ~CDockBar();
+ virtual void OnDraw(CDC* pDC);
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void PreRegisterClass(WNDCLASS& wc);
+ virtual void SendNotify(UINT nMessageID);
+ virtual void SetColor(COLORREF color);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ CDocker* GetDock() {return m_pDock;}
+ int GetWidth() {return m_DockBarWidth;}
+ void SetDock(CDocker* pDock) {m_pDock = pDock;}
+ void SetWidth(int nWidth) {m_DockBarWidth = nWidth;}
+
+ private:
+ CDockBar(const CDockBar&); // Disable copy construction
+ CDockBar& operator = (const CDockBar&); // Disable assignment operator
+
+ CDocker* m_pDock;
+ DRAGPOS m_DragPos;
+ CBrush m_brBackground;
+ int m_DockBarWidth;
+ };
+
+ // A nested class for the window inside a CDocker which includes all of this docked client.
+ // It's the remaining part of the CDocker that doesn't belong to the CDocker's children.
+ // The Docker's view window is a child window of CDockClient.
+ class CDockClient : public CWnd
+ {
+ public:
+ CDockClient();
+ virtual ~CDockClient() {}
+ virtual void Draw3DBorder(RECT& Rect);
+ virtual void DrawCaption(WPARAM wParam);
+ virtual void DrawCloseButton(CDC& DrawDC, BOOL bFocus);
+ virtual CRect GetCloseRect();
+ virtual void SendNotify(UINT nMessageID);
+
+ CString& GetCaption() const { return (CString&)m_csCaption; }
+ CWnd* GetView() const { return m_pView; }
+ void SetDock(CDocker* pDock) { m_pDock = pDock;}
+ void SetCaption(LPCTSTR szCaption) { m_csCaption = szCaption; }
+ void SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2);
+ void SetClosePressed() { m_IsClosePressed = TRUE; }
+ void SetView(CWnd& Wnd) { m_pView = &Wnd; }
+
+ protected:
+ virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam);
+ virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam);
+ virtual void OnMouseActivate(WPARAM wParam, LPARAM lParam);
+ virtual void OnMouseMove(WPARAM wParam, LPARAM lParam);
+ virtual void OnNCCalcSize(WPARAM& wParam, LPARAM& lParam);
+ virtual LRESULT OnNCHitTest(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNCLButtonDown(WPARAM wParam, LPARAM lParam);
+ virtual void OnNCMouseLeave(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNCMouseMove(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNCPaint(WPARAM wParam, LPARAM lParam);
+ virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam);
+ virtual void PreRegisterClass(WNDCLASS& wc);
+ virtual void PreCreate(CREATESTRUCT& cs);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CDockClient(const CDockClient&); // Disable copy construction
+ CDockClient& operator = (const CDockClient&); // Disable assignment operator
+
+ CString m_csCaption;
+ CPoint m_Oldpt;
+ CDocker* m_pDock;
+ CWnd* m_pView;
+ BOOL m_IsClosePressed;
+ BOOL m_bOldFocus;
+ BOOL m_bCaptionPressed;
+ BOOL m_IsTracking;
+ COLORREF m_Foregnd1;
+ COLORREF m_Backgnd1;
+ COLORREF m_Foregnd2;
+ COLORREF m_Backgnd2;
+ };
+
+ // This nested class is used to indicate where a window could dock by
+ // displaying a blue tinted window.
+ class CDockHint : public CWnd
+ {
+ public:
+ CDockHint();
+ virtual ~CDockHint();
+ virtual RECT CalcHintRectContainer(CDocker* pDockTarget);
+ virtual RECT CalcHintRectInner(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide);
+ virtual RECT CalcHintRectOuter(CDocker* pDockDrag, UINT uDockSide);
+ virtual void DisplayHint(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide);
+ virtual void OnDraw(CDC* pDC);
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void ShowHintWindow(CDocker* pDockTarget, CRect rcHint);
+
+ private:
+ CDockHint(const CDockHint&); // Disable copy construction
+ CDockHint& operator = (const CDockHint&); // Disable assignment operator
+
+ CBitmap m_bmBlueTint;
+ UINT m_uDockSideOld;
+ };
+
+ class CTarget : public CWnd
+ {
+ public:
+ CTarget() {}
+ virtual ~CTarget();
+ virtual void OnDraw(CDC* pDC);
+ virtual void PreCreate(CREATESTRUCT &cs);
+
+ protected:
+ CBitmap m_bmImage;
+
+ private:
+ CTarget(const CTarget&); // Disable copy construction
+ CTarget& operator = (const CTarget&); // Disable assignment operator
+ };
+
+ class CTargetCentre : public CTarget
+ {
+ public:
+ CTargetCentre();
+ virtual ~CTargetCentre();
+ virtual void OnDraw(CDC* pDC);
+ virtual void OnCreate();
+ virtual BOOL CheckTarget(LPDRAGPOS pDragPos);
+ BOOL IsOverContainer() { return m_bIsOverContainer; }
+
+ private:
+ CTargetCentre(const CTargetCentre&); // Disable copy construction
+ CTargetCentre& operator = (const CTargetCentre&); // Disable assignment operator
+
+ BOOL m_bIsOverContainer;
+ CDocker* m_pOldDockTarget;
+ };
+
+ class CTargetLeft : public CTarget
+ {
+ public:
+ CTargetLeft() {m_bmImage.LoadImage(IDW_SDLEFT,0,0,0);}
+ virtual BOOL CheckTarget(LPDRAGPOS pDragPos);
+
+ private:
+ CTargetLeft(const CTargetLeft&); // Disable copy construction
+ CTargetLeft& operator = (const CTargetLeft&); // Disable assignment operator
+ };
+
+ class CTargetTop : public CTarget
+ {
+ public:
+ CTargetTop() {m_bmImage.LoadImage(IDW_SDTOP,0,0,0);}
+ virtual BOOL CheckTarget(LPDRAGPOS pDragPos);
+ private:
+ CTargetTop(const CTargetTop&); // Disable copy construction
+ CTargetTop& operator = (const CTargetTop&); // Disable assignment operator
+ };
+
+ class CTargetRight : public CTarget
+ {
+ public:
+ CTargetRight() {m_bmImage.LoadImage(IDW_SDRIGHT,0,0,0);}
+ virtual BOOL CheckTarget(LPDRAGPOS pDragPos);
+
+ private:
+ CTargetRight(const CTargetRight&); // Disable copy construction
+ CTargetRight& operator = (const CTargetRight&); // Disable assignment operator
+ };
+
+ class CTargetBottom : public CTarget
+ {
+ public:
+ CTargetBottom() {m_bmImage.LoadImage(IDW_SDBOTTOM,0,0,0);}
+ virtual BOOL CheckTarget(LPDRAGPOS pDragPos);
+ };
+
+ friend class CTargetCentre;
+ friend class CTargetLeft;
+ friend class CTargetTop;
+ friend class CTargetRight;
+ friend class CTargetBottom;
+ friend class CDockClient;
+ friend class CDockContainer;
+
+ public:
+ // Operations
+ CDocker();
+ virtual ~CDocker();
+ virtual CDocker* AddDockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, int nDockID = 0);
+ virtual CDocker* AddUndockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, RECT rc, int nDockID = 0);
+ virtual void Close();
+ virtual void CloseAllDockers();
+ virtual void Dock(CDocker* pDocker, UINT uDockSide);
+ virtual void DockInContainer(CDocker* pDock, DWORD dwDockStyle);
+ virtual CDockContainer* GetContainer() const;
+ virtual CDocker* GetActiveDocker() const;
+ virtual CDocker* GetDockAncestor() const;
+ virtual CDocker* GetDockFromID(int n_DockID) const;
+ virtual CDocker* GetDockFromPoint(POINT pt) const;
+ virtual CDocker* GetDockFromView(CWnd* pView) const;
+ virtual CDocker* GetTopmostDocker() const;
+ virtual int GetDockSize() const;
+ virtual CTabbedMDI* GetTabbedMDI() const;
+ virtual int GetTextHeight();
+ virtual void Hide();
+ virtual BOOL LoadRegistrySettings(tString tsRegistryKeyName);
+ virtual void RecalcDockLayout();
+ virtual BOOL SaveRegistrySettings(tString tsRegistryKeyName);
+ virtual void Undock(CPoint pt, BOOL bShowUndocked = TRUE);
+ virtual void UndockContainer(CDockContainer* pContainer, CPoint pt, BOOL bShowUndocked);
+ virtual BOOL VerifyDockers();
+
+ // Attributes
+ virtual CDockBar& GetDockBar() const {return (CDockBar&)m_DockBar;}
+ virtual CDockClient& GetDockClient() const {return (CDockClient&)m_DockClient;}
+ virtual CDockHint& GetDockHint() const {return m_pDockAncestor->m_DockHint;}
+
+
+ std::vector <DockPtr> & GetAllDockers() const {return GetDockAncestor()->m_vAllDockers;}
+ int GetBarWidth() const {return GetDockBar().GetWidth();}
+ CString& GetCaption() const {return GetDockClient().GetCaption();}
+ std::vector <CDocker*> & GetDockChildren() const {return (std::vector <CDocker*> &)m_vDockChildren;}
+ int GetDockID() const {return m_nDockID;}
+ CDocker* GetDockParent() const {return m_pDockParent;}
+ DWORD GetDockStyle() const {return m_DockStyle;}
+ CWnd* GetView() const {return GetDockClient().GetView();}
+ BOOL IsChildOfDocker(CWnd* pWnd) const;
+ BOOL IsDocked() const;
+ BOOL IsDragAutoResize();
+ BOOL IsRelated(CWnd* pWnd) const;
+ BOOL IsUndocked() const;
+ void SetBarColor(COLORREF color) {GetDockBar().SetColor(color);}
+ void SetBarWidth(int nWidth) {GetDockBar().SetWidth(nWidth);}
+ void SetCaption(LPCTSTR szCaption);
+ void SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2);
+ void SetCaptionHeight(int nHeight);
+ void SetDockStyle(DWORD dwDockStyle);
+ void SetDockSize(int DockSize);
+ void SetDragAutoResize(BOOL bAutoResize);
+ void SetView(CWnd& wndView);
+
+ protected:
+ virtual CDocker* NewDockerFromID(int idDock);
+ virtual void OnActivate(WPARAM wParam, LPARAM lParam);
+ virtual void OnCaptionTimer(WPARAM wParam, LPARAM lParam);
+ virtual void OnCreate();
+ virtual void OnDestroy(WPARAM wParam, LPARAM lParam);
+ virtual void OnDockDestroyed(WPARAM wParam, LPARAM lParam);
+ virtual void OnExitSizeMove(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam);
+ virtual void OnSetFocus(WPARAM wParam, LPARAM lParam);
+ virtual void OnSysColorChange(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnSysCommand(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnWindowPosChanging(WPARAM wParam, LPARAM lParam);
+ virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam);
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CDocker(const CDocker&); // Disable copy construction
+ CDocker& operator = (const CDocker&); // Disable assignment operator
+ void CheckAllTargets(LPDRAGPOS pDragPos);
+ void CloseAllTargets();
+ void DockOuter(CDocker* pDocker, DWORD dwDockStyle);
+ void DrawAllCaptions();
+ void DrawHashBar(HWND hBar, POINT Pos);
+ void ConvertToChild(HWND hWndParent);
+ void ConvertToPopup(RECT rc);
+ void MoveDockChildren(CDocker* pDockTarget);
+ void PromoteFirstChild();
+ void RecalcDockChildLayout(CRect rc);
+ void ResizeDockers(LPDRAGPOS pdp);
+ CDocker* SeparateFromDock();
+ void SendNotify(UINT nMessageID);
+ void SetUndockPosition(CPoint pt);
+ std::vector<CDocker*> SortDockers();
+
+ CDockBar m_DockBar;
+ CDockHint m_DockHint;
+ CDockClient m_DockClient;
+ CTargetCentre m_TargetCentre;
+ CTargetLeft m_TargetLeft;
+ CTargetTop m_TargetTop;
+ CTargetRight m_TargetRight;
+ CPoint m_OldPoint;
+ CTargetBottom m_TargetBottom;
+ CDocker* m_pDockParent;
+ CDocker* m_pDockAncestor;
+ CDocker* m_pDockActive;
+
+ std::vector <CDocker*> m_vDockChildren;
+ std::vector <DockPtr> m_vAllDockers; // Only used in DockAncestor
+
+ CRect m_rcBar;
+ CRect m_rcChild;
+
+ BOOL m_BlockMove;
+ BOOL m_Undocking;
+ BOOL m_bIsClosing;
+ BOOL m_bIsDragging;
+ BOOL m_bDragAutoResize;
+ int m_DockStartSize;
+ int m_nDockID;
+ int m_nTimerCount;
+ int m_NCHeight;
+ DWORD m_dwDockZone;
+ double m_DockSizeRatio;
+ DWORD m_DockStyle;
+ HWND m_hOldFocus;
+
+ }; // class CDocker
+
+ struct DockInfo
+ {
+ DWORD DockStyle;
+ int DockSize;
+ int DockID;
+ int DockParentID;
+ RECT Rect;
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+
+ /////////////////////////////////////////////////////////////
+ // Definitions for the CDockBar class nested within CDocker
+ //
+ inline CDocker::CDockBar::CDockBar() : m_pDock(NULL), m_DockBarWidth(4)
+ {
+ m_brBackground.CreateSolidBrush(RGB(192,192,192));
+ }
+
+ inline CDocker::CDockBar::~CDockBar()
+ {
+ }
+
+ inline void CDocker::CDockBar::OnDraw(CDC* pDC)
+ {
+ CRect rcClient = GetClientRect();
+ pDC->SelectObject(&m_brBackground);
+ pDC->PatBlt(0, 0, rcClient.Width(), rcClient.Height(), PATCOPY);
+ }
+
+ inline void CDocker::CDockBar::PreCreate(CREATESTRUCT &cs)
+ {
+ // Create a child window, initially hidden
+ cs.style = WS_CHILD;
+ }
+
+ inline void CDocker::CDockBar::PreRegisterClass(WNDCLASS& wc)
+ {
+ wc.lpszClassName = _T("Win32++ Bar");
+ wc.hbrBackground = m_brBackground;
+ }
+
+ inline void CDocker::CDockBar::SendNotify(UINT nMessageID)
+ {
+ // Send a splitter bar notification to the parent
+ m_DragPos.hdr.code = nMessageID;
+ m_DragPos.hdr.hwndFrom = m_hWnd;
+ m_DragPos.ptPos = GetCursorPos();
+ m_DragPos.ptPos.x += 1;
+ GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&m_DragPos);
+ }
+
+ inline void CDocker::CDockBar::SetColor(COLORREF color)
+ {
+ // Useful colors:
+ // GetSysColor(COLOR_BTNFACE) // Default Grey
+ // RGB(196, 215, 250) // Default Blue
+
+ m_brBackground.CreateSolidBrush(color);
+ }
+
+ inline LRESULT CDocker::CDockBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ {
+ switch (uMsg)
+ {
+ case WM_SETCURSOR:
+ {
+ if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE))
+ {
+ HCURSOR hCursor;
+ DWORD dwSide = GetDock()->GetDockStyle() & 0xF;
+ if ((dwSide == DS_DOCKED_LEFT) || (dwSide == DS_DOCKED_RIGHT))
+ hCursor = LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_SPLITH));
+ else
+ hCursor = LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_SPLITV));
+
+ if (hCursor) SetCursor(hCursor);
+ else TRACE(_T("**WARNING** Missing cursor resource for slider bar\n"));
+
+ return TRUE;
+ }
+ else
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ {
+ if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE))
+ {
+ SendNotify(UWM_BAR_START);
+ SetCapture();
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE) && (GetCapture() == this))
+ {
+ SendNotify(UWM_BAR_END);
+ ReleaseCapture();
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE) && (GetCapture() == this))
+ {
+ SendNotify(UWM_BAR_MOVE);
+ }
+ break;
+ }
+ }
+
+ // pass unhandled messages on for default processing
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CDockClient class nested within CDocker
+ //
+ inline CDocker::CDockClient::CDockClient() : m_pView(0), m_IsClosePressed(FALSE),
+ m_bOldFocus(FALSE), m_bCaptionPressed(FALSE), m_IsTracking(FALSE)
+ {
+ m_Foregnd1 = RGB(32,32,32);
+ m_Backgnd1 = RGB(190,207,227);
+ m_Foregnd2 = GetSysColor(COLOR_BTNTEXT);
+ m_Backgnd2 = GetSysColor(COLOR_BTNFACE);
+ }
+
+ inline void CDocker::CDockClient::Draw3DBorder(RECT& Rect)
+ {
+ // Imitates the drawing of the WS_EX_CLIENTEDGE extended style
+ // This draws a 2 pixel border around the specified Rect
+ CWindowDC dc(this);
+ CRect rcw = Rect;
+ dc.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
+ dc.MoveTo(0, rcw.Height());
+ dc.LineTo(0, 0);
+ dc.LineTo(rcw.Width(), 0);
+ dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DDKSHADOW));
+ dc.MoveTo(1, rcw.Height()-2);
+ dc.LineTo(1, 1);
+ dc.LineTo(rcw.Width()-2, 1);
+ dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DHILIGHT));
+ dc.MoveTo(rcw.Width()-1, 0);
+ dc.LineTo(rcw.Width()-1, rcw.Height()-1);
+ dc.LineTo(0, rcw.Height()-1);
+ dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DLIGHT));
+ dc.MoveTo(rcw.Width()-2, 1);
+ dc.LineTo(rcw.Width()-2, rcw.Height()-2);
+ dc.LineTo(1, rcw.Height()-2);
+ }
+
+ inline CRect CDocker::CDockClient::GetCloseRect()
+ {
+ // Calculate the close rect position in screen co-ordinates
+ CRect rcClose;
+
+ int gap = 4;
+ CRect rc = GetWindowRect();
+ int cx = GetSystemMetrics(SM_CXSMICON);
+ int cy = GetSystemMetrics(SM_CYSMICON);
+
+ rcClose.top = 2 + rc.top + m_pDock->m_NCHeight/2 - cy/2;
+ rcClose.bottom = 2 + rc.top + m_pDock->m_NCHeight/2 + cy/2;
+ rcClose.right = rc.right - gap;
+ rcClose.left = rcClose.right - cx;
+
+#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500)
+ if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
+ {
+ rcClose.left = rc.left + gap;
+ rcClose.right = rcClose.left + cx;
+ }
+#endif
+
+
+ return rcClose;
+ }
+
+ inline void CDocker::CDockClient::DrawCaption(WPARAM wParam)
+ {
+ if (IsWindow() && m_pDock->IsDocked() && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ BOOL bFocus = m_pDock->IsChildOfDocker(GetFocus());
+ m_bOldFocus = FALSE;
+
+ // Acquire the DC for our NonClient painting
+ CDC* pDC;
+ if ((wParam != 1) && (bFocus == m_bOldFocus))
+ pDC = GetDCEx((HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN|DCX_PARENTCLIP);
+ else
+ pDC = GetWindowDC();
+
+ // Create and set up our memory DC
+ CRect rc = GetWindowRect();
+ CMemDC dcMem(pDC);
+ int rcAdjust = (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)? 2 : 0;
+ int Width = MAX(rc.Width() -rcAdjust, 0);
+ int Height = m_pDock->m_NCHeight + rcAdjust;
+ dcMem.CreateCompatibleBitmap(pDC, Width, Height);
+ m_bOldFocus = bFocus;
+
+ // Set the font for the title
+ NONCLIENTMETRICS info = {0};
+ info.cbSize = GetSizeofNonClientMetrics();
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
+ dcMem.CreateFontIndirect(&info.lfStatusFont);
+
+ // Set the Colours
+ if (bFocus)
+ {
+ dcMem.SetTextColor(m_Foregnd1);
+ dcMem.CreateSolidBrush(m_Backgnd1);
+ dcMem.SetBkColor(m_Backgnd1);
+ }
+ else
+ {
+ dcMem.SetTextColor(m_Foregnd2);
+ dcMem.CreateSolidBrush(m_Backgnd2);
+ dcMem.SetBkColor(m_Backgnd2);
+ }
+
+ // Draw the rectangle
+ dcMem.CreatePen(PS_SOLID, 1, RGB(160, 150, 140));
+ dcMem.Rectangle(rcAdjust, rcAdjust, rc.Width() -rcAdjust, m_pDock->m_NCHeight +rcAdjust);
+
+ // Display the caption
+ int cx = (m_pDock->GetDockStyle() & DS_NO_CLOSE)? 0 : GetSystemMetrics(SM_CXSMICON);
+ CRect rcText(4 +rcAdjust, rcAdjust, rc.Width() -4 - cx -rcAdjust, m_pDock->m_NCHeight +rcAdjust);
+ dcMem.DrawText(m_csCaption, m_csCaption.GetLength(), rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
+
+ // Draw the close button
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE))
+ DrawCloseButton(dcMem, bFocus);
+
+ // Draw the 3D border
+ if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)
+ Draw3DBorder(rc);
+
+ // Copy the Memory DC to the window's DC
+ pDC->BitBlt(rcAdjust, rcAdjust, Width, Height, &dcMem, rcAdjust, rcAdjust, SRCCOPY);
+
+ // Required for Win98/WinME
+ pDC->Destroy();
+ }
+ }
+
+ inline void CDocker::CDockClient::DrawCloseButton(CDC& DrawDC, BOOL bFocus)
+ {
+ // The close button isn't displayed on Win95
+ if (GetWinVersion() == 1400) return;
+
+ if (m_pDock->IsDocked() && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ // Determine the close button's drawing position relative to the window
+ CRect rcClose = GetCloseRect();
+ UINT uState = GetCloseRect().PtInRect(GetCursorPos())? m_IsClosePressed && IsLeftButtonDown()? 2 : 1 : 0;
+ ScreenToClient(rcClose);
+
+ if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)
+ {
+ rcClose.OffsetRect(2, m_pDock->m_NCHeight+2);
+ if (GetWindowRect().Height() < (m_pDock->m_NCHeight+4))
+ rcClose.OffsetRect(-2, -2);
+ }
+ else
+ rcClose.OffsetRect(0, m_pDock->m_NCHeight-2);
+
+ // Draw the outer highlight for the close button
+ if (!IsRectEmpty(&rcClose))
+ {
+ switch (uState)
+ {
+ case 0:
+ {
+ // Normal button
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220));
+ DrawDC.MoveTo(rcClose.left, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.bottom);
+ break;
+ }
+
+ case 1:
+ {
+ // Popped up button
+ // Draw outline, white at top, black on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.MoveTo(rcClose.left, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.LineTo(rcClose.left, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.bottom);
+ }
+
+ break;
+ case 2:
+ {
+ // Pressed button
+ // Draw outline, black on top, white on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.MoveTo(rcClose.left, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.LineTo(rcClose.left, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.bottom);
+ }
+ break;
+ }
+
+ // Manually Draw Close Button
+ if (bFocus)
+ DrawDC.CreatePen(PS_SOLID, 1, m_Foregnd1);
+ else
+ DrawDC.CreatePen(PS_SOLID, 1, m_Foregnd2);
+
+ DrawDC.MoveTo(rcClose.left + 3, rcClose.top +3);
+ DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.left + 4, rcClose.top +3);
+ DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -3);
+
+ DrawDC.MoveTo(rcClose.left + 3, rcClose.top +4);
+ DrawDC.LineTo(rcClose.right - 3, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.right -3, rcClose.top +3);
+ DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.right -3, rcClose.top +4);
+ DrawDC.LineTo(rcClose.left + 3, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.right -4, rcClose.top +3);
+ DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -3);
+ }
+ }
+ }
+
+ inline void CDocker::CDockClient::OnNCCalcSize(WPARAM& wParam, LPARAM& lParam)
+ {
+ // Sets the non-client area (and hence sets the client area)
+ // This function modifies lParam
+
+ UNREFERENCED_PARAMETER(wParam);
+
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ if (m_pDock->IsDocked())
+ {
+ LPRECT rc = (LPRECT)lParam;
+ rc->top += m_pDock->m_NCHeight;
+ }
+ }
+ }
+
+ inline LRESULT CDocker::CDockClient::OnNCHitTest(WPARAM wParam, LPARAM lParam)
+ {
+ // Identify which part of the non-client area the cursor is over
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ if (m_pDock->IsDocked())
+ {
+ CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ // Indicate if the point is in the close button (except for Win95)
+ if ((GetWinVersion() > 1400) && (GetCloseRect().PtInRect(pt)))
+ return HTCLOSE;
+
+ ScreenToClient(pt);
+
+ // Indicate if the point is in the caption
+ if (pt.y < 0)
+ return HTCAPTION;
+ }
+ }
+ return CWnd::WndProcDefault(WM_NCHITTEST, wParam, lParam);
+ }
+
+ inline LRESULT CDocker::CDockClient::OnNCLButtonDown(WPARAM wParam, LPARAM lParam)
+ {
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ if ((HTCLOSE == wParam) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE))
+ {
+ m_IsClosePressed = TRUE;
+ SetCapture();
+ }
+
+ m_bCaptionPressed = TRUE;
+ m_Oldpt.x = GET_X_LPARAM(lParam);
+ m_Oldpt.y = GET_Y_LPARAM(lParam);
+ if (m_pDock->IsDocked())
+ {
+ CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ ScreenToClient(pt);
+ m_pView->SetFocus();
+
+ // Update the close button
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE))
+ {
+ CWindowDC dc(this);
+ DrawCloseButton(dc, m_bOldFocus);
+ }
+
+ return 0L;
+ }
+ }
+ return CWnd::WndProcDefault(WM_NCLBUTTONDOWN, wParam, lParam);
+ }
+
+ inline void CDocker::CDockClient::OnLButtonUp(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & (DS_NO_CAPTION|DS_NO_CLOSE)))
+ {
+ m_bCaptionPressed = FALSE;
+ if (m_IsClosePressed && GetCloseRect().PtInRect(GetCursorPos()))
+ {
+ // Destroy the docker
+ if (dynamic_cast<CDockContainer*>(m_pDock->GetView()))
+ {
+ CDockContainer* pContainer = ((CDockContainer*)m_pDock->GetView())->GetActiveContainer();
+ CDocker* pDock = m_pDock->GetDockFromView(pContainer);
+ pDock->GetDockClient().SetClosePressed();
+ m_pDock->UndockContainer(pContainer, GetCursorPos(), FALSE);
+ pDock->Destroy();
+ }
+ else
+ {
+ m_pDock->Hide();
+ m_pDock->Destroy();
+ }
+ }
+ }
+ }
+
+ inline void CDocker::CDockClient::OnLButtonDown(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ m_IsClosePressed = FALSE;
+ ReleaseCapture();
+ CWindowDC dc(this);
+ DrawCloseButton(dc, m_bOldFocus);
+ }
+
+ inline void CDocker::CDockClient::OnMouseActivate(WPARAM wParam, LPARAM lParam)
+ // Focus changed, so redraw the captions
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ m_pDock->GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0);
+ }
+ }
+
+ inline void CDocker::CDockClient::OnMouseMove(WPARAM wParam, LPARAM lParam)
+ {
+ OnNCMouseMove(wParam, lParam);
+ }
+
+ inline void CDocker::CDockClient::OnNCMouseLeave(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ m_IsTracking = FALSE;
+ CWindowDC dc(this);
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & (DS_NO_CAPTION|DS_NO_CLOSE)) && m_pDock->IsDocked())
+ DrawCloseButton(dc, m_bOldFocus);
+
+ m_IsTracking = FALSE;
+ }
+
+ inline LRESULT CDocker::CDockClient::OnNCMouseMove(WPARAM wParam, LPARAM lParam)
+ {
+ if (!m_IsTracking)
+ {
+ TRACKMOUSEEVENT TrackMouseEventStruct = {0};
+ TrackMouseEventStruct.cbSize = sizeof(TrackMouseEventStruct);
+ TrackMouseEventStruct.dwFlags = TME_LEAVE|TME_NONCLIENT;
+ TrackMouseEventStruct.hwndTrack = m_hWnd;
+ _TrackMouseEvent(&TrackMouseEventStruct);
+ m_IsTracking = TRUE;
+ }
+
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ if (m_pDock->IsDocked())
+ {
+ // Discard phantom mouse move messages
+ if ( (m_Oldpt.x == GET_X_LPARAM(lParam) ) && (m_Oldpt.y == GET_Y_LPARAM(lParam)))
+ return 0L;
+
+ if (IsLeftButtonDown() && (wParam == HTCAPTION) && (m_bCaptionPressed))
+ {
+ CDocker* pDock = (CDocker*)GetParent();
+ if (pDock)
+ pDock->Undock(GetCursorPos());
+ }
+
+ // Update the close button
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE))
+ {
+ CWindowDC dc(this);
+ DrawCloseButton(dc, m_bOldFocus);
+ }
+ }
+
+ m_bCaptionPressed = FALSE;
+ }
+ return CWnd::WndProcDefault(WM_MOUSEMOVE, wParam, lParam);
+ }
+
+ inline LRESULT CDocker::CDockClient::OnNCPaint(WPARAM wParam, LPARAM lParam)
+ {
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION))
+ {
+ if (m_pDock->IsDocked())
+ {
+ DefWindowProc(WM_NCPAINT, wParam, lParam);
+ DrawCaption(wParam);
+ return 0;
+ }
+ }
+ return CWnd::WndProcDefault(WM_NCPAINT, wParam, lParam);
+ }
+
+ inline void CDocker::CDockClient::OnWindowPosChanged(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ // Reposition the View window to cover the DockClient's client area
+ CRect rc = GetClientRect();
+ m_pView->SetWindowPos(NULL, rc, SWP_SHOWWINDOW);
+ }
+
+ inline void CDocker::CDockClient::PreRegisterClass(WNDCLASS& wc)
+ {
+ wc.lpszClassName = _T("Win32++ DockClient");
+ wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+ }
+
+ inline void CDocker::CDockClient::PreCreate(CREATESTRUCT& cs)
+ {
+ DWORD dwStyle = m_pDock->GetDockStyle();
+ if (dwStyle & DS_CLIENTEDGE)
+ cs.dwExStyle = WS_EX_CLIENTEDGE;
+
+#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500)
+ if (m_pDock->GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
+ cs.dwExStyle |= WS_EX_LAYOUTRTL;
+#endif
+
+ }
+
+ inline void CDocker::CDockClient::SendNotify(UINT nMessageID)
+ {
+ // Fill the DragPos structure with data
+ DRAGPOS DragPos;
+ DragPos.hdr.code = nMessageID;
+ DragPos.hdr.hwndFrom = m_hWnd;
+ DragPos.ptPos = GetCursorPos();
+
+ // Send a DragPos notification to the docker
+ GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&DragPos);
+ }
+
+ inline void CDocker::CDockClient::SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF Foregnd2, COLORREF Backgnd2)
+ {
+ // Set the colors used when drawing the caption
+ // m_Foregnd1 Foreground colour (focused). m_Backgnd1 Background colour (focused)
+ // m_Foregnd2 Foreground colour (not focused). m_Backgnd2 Foreground colour (not focused)
+ m_Foregnd1 = Foregnd1;
+ m_Backgnd1 = Backgnd1;
+ m_Foregnd2 = Foregnd2;
+ m_Backgnd2 = Backgnd2;
+ }
+
+ inline LRESULT CDocker::CDockClient::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_LBUTTONUP:
+ {
+ ReleaseCapture();
+ if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE))
+ {
+ CWindowDC dc(this);
+ DrawCloseButton(dc, m_bOldFocus);
+ OnLButtonUp(wParam, lParam);
+ }
+ }
+ break;
+
+ case WM_MOUSEACTIVATE:
+ OnMouseActivate(wParam, lParam);
+ break;
+
+ case WM_MOUSEMOVE:
+ OnMouseMove(wParam, lParam);
+ break;
+
+ case WM_NCCALCSIZE:
+ OnNCCalcSize(wParam, lParam);
+ break;
+
+ case WM_NCHITTEST:
+ return OnNCHitTest(wParam, lParam);
+
+ case WM_NCLBUTTONDOWN:
+ return OnNCLButtonDown(wParam, lParam);
+
+ case WM_NCMOUSEMOVE:
+ return OnNCMouseMove(wParam, lParam);
+
+ case WM_NCPAINT:
+ return OnNCPaint(wParam, lParam);
+
+ case WM_NCMOUSELEAVE:
+ OnNCMouseLeave(wParam, lParam);
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ OnWindowPosChanged(wParam, lParam);
+ break;
+ }
+
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+ // Definitions for the CDockHint class nested within CDocker
+ //
+ inline CDocker::CDockHint::CDockHint() : m_uDockSideOld(0)
+ {
+ }
+
+ inline CDocker::CDockHint::~CDockHint()
+ {
+ }
+
+ inline RECT CDocker::CDockHint::CalcHintRectContainer(CDocker* pDockTarget)
+ {
+ // Calculate the hint window's position for container docking
+ CRect rcHint = pDockTarget->GetDockClient().GetWindowRect();
+ if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)
+ rcHint.InflateRect(-2, -2);
+ pDockTarget->ScreenToClient(rcHint);
+
+ return rcHint;
+ }
+
+ inline RECT CDocker::CDockHint::CalcHintRectInner(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide)
+ {
+ // Calculate the hint window's position for inner docking
+ CRect rcHint = pDockTarget->GetDockClient().GetWindowRect();
+ if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)
+ rcHint.InflateRect(-2, -2);
+ pDockTarget->ScreenToClient(rcHint);
+
+ int Width;
+ CRect rcDockDrag = pDockDrag->GetWindowRect();
+ CRect rcDockTarget = pDockTarget->GetDockClient().GetWindowRect();
+ if ((uDockSide == DS_DOCKED_LEFT) || (uDockSide == DS_DOCKED_RIGHT))
+ {
+ Width = rcDockDrag.Width();
+ if (Width >= (rcDockTarget.Width() - pDockDrag->GetBarWidth()))
+ Width = MAX(rcDockTarget.Width()/2 - pDockDrag->GetBarWidth(), pDockDrag->GetBarWidth());
+ }
+ else
+ {
+ Width = rcDockDrag.Height();
+ if (Width >= (rcDockTarget.Height() - pDockDrag->GetBarWidth()))
+ Width = MAX(rcDockTarget.Height()/2 - pDockDrag->GetBarWidth(), pDockDrag->GetBarWidth());
+ }
+ switch (uDockSide)
+ {
+ case DS_DOCKED_LEFT:
+ rcHint.right = rcHint.left + Width;
+ break;
+ case DS_DOCKED_RIGHT:
+ rcHint.left = rcHint.right - Width;
+ break;
+ case DS_DOCKED_TOP:
+ rcHint.bottom = rcHint.top + Width;
+ break;
+ case DS_DOCKED_BOTTOM:
+ rcHint.top = rcHint.bottom - Width;
+ break;
+ }
+
+ return rcHint;
+ }
+
+ inline RECT CDocker::CDockHint::CalcHintRectOuter(CDocker* pDockDrag, UINT uDockSide)
+ {
+ // Calculate the hint window's position for outer docking
+ CDocker* pDockTarget = pDockDrag->GetDockAncestor();
+ CRect rcHint = pDockTarget->GetClientRect();
+ if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)
+ rcHint.InflateRect(-2, -2);
+
+ int Width;
+ CRect rcDockDrag = pDockDrag->GetWindowRect();
+ CRect rcDockTarget = pDockTarget->GetDockClient().GetWindowRect();
+
+ // Limit the docked size to half the parent's size if it won't fit inside parent
+ if ((uDockSide == DS_DOCKED_LEFTMOST) || (uDockSide == DS_DOCKED_RIGHTMOST))
+ {
+ Width = rcDockDrag.Width();
+ int BarWidth = pDockDrag->GetBarWidth();
+ if (Width >= pDockTarget->GetDockClient().GetClientRect().Width() - pDockDrag->GetBarWidth())
+ Width = MAX(pDockTarget->GetDockClient().GetClientRect().Width()/2 - BarWidth, BarWidth);
+ }
+ else
+ {
+ Width = rcDockDrag.Height();
+ int BarWidth = pDockDrag->GetBarWidth();
+ if (Width >= pDockTarget->GetDockClient().GetClientRect().Height() - pDockDrag->GetBarWidth())
+ Width = MAX(pDockTarget->GetDockClient().GetClientRect().Height()/2 - BarWidth, BarWidth);
+ }
+ switch (uDockSide)
+ {
+ case DS_DOCKED_LEFTMOST:
+ rcHint.right = rcHint.left + Width;
+ break;
+ case DS_DOCKED_RIGHTMOST:
+ rcHint.left = rcHint.right - Width;
+ break;
+ case DS_DOCKED_TOPMOST:
+ rcHint.bottom = rcHint.top + Width;
+ break;
+ case DS_DOCKED_BOTTOMMOST:
+ rcHint.top = rcHint.bottom - Width;
+ break;
+ }
+
+ return rcHint;
+ }
+
+ inline void CDocker::CDockHint::DisplayHint(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide)
+ {
+ // Ensure a new hint window is created if dock side changes
+ if (uDockSide != m_uDockSideOld)
+ {
+ Destroy();
+ pDockTarget->RedrawWindow( NULL, NULL, RDW_NOERASE | RDW_UPDATENOW );
+ pDockDrag->RedrawWindow();
+ }
+ m_uDockSideOld = uDockSide;
+
+ if (!IsWindow())
+ {
+ CRect rcHint;
+
+ if (uDockSide & 0xF)
+ rcHint = CalcHintRectInner(pDockTarget, pDockDrag, uDockSide);
+ else if (uDockSide & 0xF0000)
+ rcHint = CalcHintRectOuter(pDockDrag, uDockSide);
+ else if (uDockSide & DS_DOCKED_CONTAINER)
+ rcHint = CalcHintRectContainer(pDockTarget);
+ else
+ return;
+
+ ShowHintWindow(pDockTarget, rcHint);
+ }
+ }
+
+ inline void CDocker::CDockHint::OnDraw(CDC* pDC)
+ {
+ // Display the blue tinted bitmap
+ CRect rc = GetClientRect();
+ CMemDC MemDC(pDC);
+ MemDC.SelectObject(&m_bmBlueTint);
+ pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &MemDC, 0, 0, SRCCOPY);
+ }
+
+ inline void CDocker::CDockHint::PreCreate(CREATESTRUCT &cs)
+ {
+ cs.style = WS_POPUP;
+
+ // WS_EX_TOOLWINDOW prevents the window being displayed on the taskbar
+ cs.dwExStyle = WS_EX_TOOLWINDOW;
+
+ cs.lpszClass = _T("Win32++ DockHint");
+ }
+
+ inline void CDocker::CDockHint::ShowHintWindow(CDocker* pDockTarget, CRect rcHint)
+ {
+ // Save the Dock window's blue tinted bitmap
+ CClientDC dcDesktop(NULL);
+ CMemDC dcMem(&dcDesktop);
+ CRect rcBitmap = rcHint;
+ CRect rcTarget = rcHint;
+ pDockTarget->ClientToScreen(rcTarget);
+
+ m_bmBlueTint.CreateCompatibleBitmap(&dcDesktop, rcBitmap.Width(), rcBitmap.Height());
+ CBitmap* pOldBitmap = dcMem.SelectObject(&m_bmBlueTint);
+ dcMem.BitBlt(0, 0, rcBitmap.Width(), rcBitmap.Height(), &dcDesktop, rcTarget.left, rcTarget.top, SRCCOPY);
+ dcMem.SelectObject(pOldBitmap);
+ TintBitmap(&m_bmBlueTint, -64, -24, +128);
+
+ // Create the Hint window
+ if (!IsWindow())
+ {
+ Create(pDockTarget);
+ }
+
+ pDockTarget->ClientToScreen(rcHint);
+ SetWindowPos(NULL, rcHint, SWP_SHOWWINDOW|SWP_NOZORDER|SWP_NOACTIVATE);
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CTargetCentre class nested within CDocker
+ //
+ inline CDocker::CTargetCentre::CTargetCentre() : m_bIsOverContainer(FALSE), m_pOldDockTarget(0)
+ {
+ }
+
+ inline CDocker::CTargetCentre::~CTargetCentre()
+ {
+ }
+
+ inline void CDocker::CTargetCentre::OnDraw(CDC* pDC)
+ {
+ CBitmap bmCentre(IDW_SDCENTER);
+ CBitmap bmLeft(IDW_SDLEFT);
+ CBitmap bmRight(IDW_SDRIGHT);
+ CBitmap bmTop(IDW_SDTOP);
+ CBitmap bmBottom(IDW_SDBOTTOM);
+
+ if (bmCentre.GetHandle()) pDC->DrawBitmap(0, 0, 88, 88, bmCentre, RGB(255,0,255));
+ else TRACE(_T("Missing docking resource: Target Centre\n"));
+
+ if (bmLeft.GetHandle()) pDC->DrawBitmap(0, 29, 31, 29, bmLeft, RGB(255,0,255));
+ else TRACE(_T("Missing docking resource: Target Left\n"));
+
+ if (bmTop.GetHandle()) pDC->DrawBitmap(29, 0, 29, 31, bmTop, RGB(255,0,255));
+ else TRACE(_T("Missing docking resource: Target Top\n"));
+
+ if (bmRight.GetHandle()) pDC->DrawBitmap(55, 29, 31, 29, bmRight, RGB(255,0,255));
+ else TRACE(_T("Missing docking resource: Target Right\n"));
+
+ if (bmBottom.GetHandle()) pDC->DrawBitmap(29, 55, 29, 31, bmBottom, RGB(255,0,255));
+ else TRACE(_T("Missing docking resource: Target Bottom\n"));
+
+ if (IsOverContainer())
+ {
+ CBitmap bmMiddle(IDW_SDMIDDLE);
+ pDC->DrawBitmap(31, 31, 25, 26, bmMiddle, RGB(255,0,255));
+ }
+ }
+
+ inline void CDocker::CTargetCentre::OnCreate()
+ {
+ // Use a region to create an irregularly shapped window
+ POINT ptArray[16] = { {0,29}, {22, 29}, {29, 22}, {29, 0},
+ {58, 0}, {58, 22}, {64, 29}, {87, 29},
+ {87, 58}, {64, 58}, {58, 64}, {58, 87},
+ {29, 87}, {29, 64}, {23, 58}, {0, 58} };
+
+ CRgn rgnPoly;
+ rgnPoly.CreatePolygonRgn(ptArray, 16, WINDING);
+ SetWindowRgn(&rgnPoly, FALSE);
+ }
+
+ inline BOOL CDocker::CTargetCentre::CheckTarget(LPDRAGPOS pDragPos)
+ {
+ CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom);
+ if (NULL == pDockDrag) return FALSE;
+
+ CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pDragPos->ptPos);
+ if (NULL == pDockTarget) return FALSE;
+
+ if (!IsWindow()) Create();
+ m_bIsOverContainer = (dynamic_cast<CDockContainer*>(pDockTarget->GetView()) != NULL);
+
+ // Redraw the target if the dock target changes
+ if (m_pOldDockTarget != pDockTarget) Invalidate();
+ m_pOldDockTarget = pDockTarget;
+
+ int cxImage = 88;
+ int cyImage = 88;
+
+ CRect rcTarget = pDockTarget->GetDockClient().GetWindowRect();
+ int xMid = rcTarget.left + (rcTarget.Width() - cxImage)/2;
+ int yMid = rcTarget.top + (rcTarget.Height() - cyImage)/2;
+ SetWindowPos(HWND_TOPMOST, xMid, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW);
+
+ // Create the docking zone rectangles
+ CPoint pt = pDragPos->ptPos;
+ ScreenToClient(pt);
+ CRect rcLeft(0, 29, 31, 58);
+ CRect rcTop(29, 0, 58, 31);
+ CRect rcRight(55, 29, 87, 58);
+ CRect rcBottom(29, 55, 58, 87);
+ CRect rcMiddle(31, 31, 56, 57);
+
+ // Test if our cursor is in one of the docking zones
+ if ((rcLeft.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_LEFT))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_LEFT);
+ pDockDrag->m_dwDockZone = DS_DOCKED_LEFT;
+ return TRUE;
+ }
+ else if ((rcTop.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_TOP))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_TOP);
+ pDockDrag->m_dwDockZone = DS_DOCKED_TOP;
+ return TRUE;
+ }
+ else if ((rcRight.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_RIGHT))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_RIGHT);
+ pDockDrag->m_dwDockZone = DS_DOCKED_RIGHT;
+ return TRUE;
+ }
+ else if ((rcBottom.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_BOTTOM))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_BOTTOM);
+ pDockDrag->m_dwDockZone = DS_DOCKED_BOTTOM;
+ return TRUE;
+ }
+ else if ((rcMiddle.PtInRect(pt)) && (IsOverContainer()))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_CONTAINER);
+ pDockDrag->m_dwDockZone = DS_DOCKED_CONTAINER;
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CTarget class nested within CDocker
+ // CTarget is the base class for a number of CTargetXXX classes
+ inline CDocker::CTarget::~CTarget()
+ {
+ }
+
+ inline void CDocker::CTarget::OnDraw(CDC* pDC)
+ {
+ BITMAP bm = m_bmImage.GetBitmapData();
+ int cxImage = bm.bmWidth;
+ int cyImage = bm.bmHeight;
+
+ if (m_bmImage)
+ pDC->DrawBitmap(0, 0, cxImage, cyImage, m_bmImage, RGB(255,0,255));
+ else
+ TRACE(_T("Missing docking resource\n"));
+ }
+
+ inline void CDocker::CTarget::PreCreate(CREATESTRUCT &cs)
+ {
+ cs.style = WS_POPUP;
+ cs.dwExStyle = WS_EX_TOPMOST|WS_EX_TOOLWINDOW;
+ cs.lpszClass = _T("Win32++ DockTargeting");
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CTargetLeft class nested within CDocker
+ //
+ inline BOOL CDocker::CTargetLeft::CheckTarget(LPDRAGPOS pDragPos)
+ {
+ CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom);
+ if (NULL == pDockDrag) return FALSE;
+
+ CPoint pt = pDragPos->ptPos;
+ CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker();
+ if (pDockTarget != pDockDrag->GetDockAncestor())
+ {
+ Destroy();
+ return FALSE;
+ }
+
+ BITMAP bm = m_bmImage.GetBitmapData();
+ int cxImage = bm.bmWidth;
+ int cyImage = bm.bmHeight;
+
+ if (!IsWindow())
+ {
+ Create();
+ CRect rc = pDockTarget->GetWindowRect();
+ int yMid = rc.top + (rc.Height() - cyImage)/2;
+ SetWindowPos(HWND_TOPMOST, rc.left + 10, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW);
+ }
+
+ CRect rcLeft(0, 0, cxImage, cyImage);
+ ScreenToClient(pt);
+
+ // Test if our cursor is in one of the docking zones
+ if ((rcLeft.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_LEFT))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_LEFTMOST);
+ pDockDrag->m_dwDockZone = DS_DOCKED_LEFTMOST;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CTargetTop class nested within CDocker
+ //
+ inline BOOL CDocker::CTargetTop::CheckTarget(LPDRAGPOS pDragPos)
+ {
+ CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom);
+ if (NULL == pDockDrag) return FALSE;
+
+ CPoint pt = pDragPos->ptPos;
+ CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker();
+ if (pDockTarget != pDockDrag->GetDockAncestor())
+ {
+ Destroy();
+ return FALSE;
+ }
+
+ BITMAP bm = m_bmImage.GetBitmapData();
+ int cxImage = bm.bmWidth;
+ int cyImage = bm.bmHeight;
+
+ if (!IsWindow())
+ {
+ Create();
+ CRect rc = pDockTarget->GetWindowRect();
+ int xMid = rc.left + (rc.Width() - cxImage)/2;
+ SetWindowPos(HWND_TOPMOST, xMid, rc.top + 10, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW);
+ }
+
+ CRect rcTop(0, 0, cxImage, cyImage);
+ ScreenToClient(pt);
+
+ // Test if our cursor is in one of the docking zones
+ if ((rcTop.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_TOP))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_TOPMOST);
+ pDockDrag->m_dwDockZone = DS_DOCKED_TOPMOST;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CTargetRight class nested within CDocker
+ //
+ inline BOOL CDocker::CTargetRight::CheckTarget(LPDRAGPOS pDragPos)
+ {
+ CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom);
+ if (NULL == pDockDrag) return FALSE;
+
+ CPoint pt = pDragPos->ptPos;
+ CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker();
+ if (pDockTarget != pDockDrag->GetDockAncestor())
+ {
+ Destroy();
+ return FALSE;
+ }
+
+ BITMAP bm = m_bmImage.GetBitmapData();
+ int cxImage = bm.bmWidth;
+ int cyImage = bm.bmHeight;
+
+ if (!IsWindow())
+ {
+ Create();
+ CRect rc = pDockTarget->GetWindowRect();
+ int yMid = rc.top + (rc.Height() - cyImage)/2;
+ SetWindowPos(HWND_TOPMOST, rc.right - 10 - cxImage, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW);
+ }
+
+ CRect rcRight(0, 0, cxImage, cyImage);
+ ScreenToClient(pt);
+
+ // Test if our cursor is in one of the docking zones
+ if ((rcRight.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_RIGHT))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_RIGHTMOST);
+ pDockDrag->m_dwDockZone = DS_DOCKED_RIGHTMOST;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // Definitions for the CTargetBottom class nested within CDocker
+ //
+ inline BOOL CDocker::CTargetBottom::CheckTarget(LPDRAGPOS pDragPos)
+ {
+ CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom);
+ if (NULL == pDockDrag) return FALSE;
+
+ CPoint pt = pDragPos->ptPos;
+ CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker();
+ if (pDockTarget != pDockDrag->GetDockAncestor())
+ {
+ Destroy();
+ return FALSE;
+ }
+
+ BITMAP bm = m_bmImage.GetBitmapData();
+ int cxImage = bm.bmWidth;
+ int cyImage = bm.bmHeight;
+
+ if (!IsWindow())
+ {
+ Create();
+ CRect rc = pDockTarget->GetWindowRect();
+ int xMid = rc.left + (rc.Width() - cxImage)/2;
+ SetWindowPos(HWND_TOPMOST, xMid, rc.bottom - 10 - cyImage, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW);
+ }
+ CRect rcBottom(0, 0, cxImage, cyImage);
+ ScreenToClient(pt);
+
+ // Test if our cursor is in one of the docking zones
+ if ((rcBottom.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_BOTTOM))
+ {
+ pDockDrag->m_BlockMove = TRUE;
+ pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_BOTTOMMOST);
+ pDockDrag->m_dwDockZone = DS_DOCKED_BOTTOMMOST;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+ /////////////////////////////////////////
+ // Definitions for the CDocker class
+ //
+ inline CDocker::CDocker() : m_pDockParent(NULL), m_pDockActive(NULL), m_BlockMove(FALSE), m_Undocking(FALSE),
+ m_bIsClosing(FALSE), m_bIsDragging(FALSE), m_bDragAutoResize(TRUE), m_DockStartSize(0), m_nDockID(0),
+ m_nTimerCount(0), m_NCHeight(0), m_dwDockZone(0), m_DockSizeRatio(1.0), m_DockStyle(0), m_hOldFocus(0)
+ {
+ // Assume this docker is the DockAncestor for now.
+ m_pDockAncestor = this;
+ }
+
+ inline CDocker::~CDocker()
+ {
+ GetDockBar().Destroy();
+
+ std::vector <DockPtr>::iterator iter;
+ if (GetDockAncestor() == this)
+ {
+ // Destroy all dock descendants of this dock ancestor
+ for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter)
+ {
+ (*iter)->Destroy();
+ }
+ }
+ }
+
+ inline CDocker* CDocker::AddDockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, int nDockID /* = 0*/)
+ // This function creates the docker, and adds it to the docker heirachy as docked
+ {
+ // Create the docker window as a child of the frame window.
+ // This pernamently sets the frame window as the docker window's owner,
+ // even when its parent is subsequently changed.
+
+ assert(pDocker);
+
+ // Store the Docker's pointer in the DockAncestor's vector for later deletion
+ GetDockAncestor()->m_vAllDockers.push_back(DockPtr(pDocker));
+
+ pDocker->SetDockStyle(dwDockStyle);
+ pDocker->m_nDockID = nDockID;
+ pDocker->m_pDockAncestor = GetDockAncestor();
+ pDocker->m_pDockParent = this;
+ pDocker->SetDockSize(DockSize);
+ CWnd* pFrame = GetDockAncestor()->GetAncestor();
+ pDocker->Create(pFrame);
+ pDocker->SetParent(this);
+
+ // Dock the docker window
+ if (dwDockStyle & DS_DOCKED_CONTAINER)
+ DockInContainer(pDocker, dwDockStyle);
+ else
+ Dock(pDocker, dwDockStyle);
+
+ // Issue TRACE warnings for any missing resources
+ HMODULE hMod= GetApp()->GetResourceHandle();
+
+ if (!(dwDockStyle & DS_NO_RESIZE))
+ {
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SPLITH), RT_GROUP_CURSOR))
+ TRACE(_T("**WARNING** Horizontal cursor resource missing\n"));
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SPLITV), RT_GROUP_CURSOR))
+ TRACE(_T("**WARNING** Vertical cursor resource missing\n"));
+ }
+
+ if (!(dwDockStyle & DS_NO_UNDOCK))
+ {
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDCENTER), RT_BITMAP))
+ TRACE(_T("**WARNING** Docking center bitmap resource missing\n"));
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDLEFT), RT_BITMAP))
+ TRACE(_T("**WARNING** Docking left bitmap resource missing\n"));
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDRIGHT), RT_BITMAP))
+ TRACE(_T("**WARNING** Docking right bitmap resource missing\n"));
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDTOP), RT_BITMAP))
+ TRACE(_T("**WARNING** Docking top bitmap resource missing\n"));
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDBOTTOM), RT_BITMAP))
+ TRACE(_T("**WARNING** Docking center bottom resource missing\n"));
+ }
+
+ if (dwDockStyle & DS_DOCKED_CONTAINER)
+ {
+ if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDMIDDLE), RT_BITMAP))
+ TRACE(_T("**WARNING** Docking container bitmap resource missing\n"));
+ }
+
+ return pDocker;
+ }
+
+ inline CDocker* CDocker::AddUndockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, RECT rc, int nDockID /* = 0*/)
+ // This function creates the docker, and adds it to the docker heirachy as undocked
+ {
+ assert(pDocker);
+
+ // Store the Docker's pointer in the DockAncestor's vector for later deletion
+ GetDockAncestor()->m_vAllDockers.push_back(DockPtr(pDocker));
+
+ pDocker->SetDockSize(DockSize);
+ pDocker->SetDockStyle(dwDockStyle & 0XFFFFFF0);
+ pDocker->m_nDockID = nDockID;
+ pDocker->m_pDockAncestor = GetDockAncestor();
+
+ // Initially create the as a child window of the frame
+ // This makes the frame window the owner of our docker
+ CWnd* pFrame = GetDockAncestor()->GetAncestor();
+ pDocker->Create(pFrame);
+ pDocker->SetParent(this);
+
+ // Change the Docker to a POPUP window
+ DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE;
+ pDocker->SetWindowLongPtr(GWL_STYLE, dwStyle);
+ pDocker->SetRedraw(FALSE);
+ pDocker->SetParent(0);
+ pDocker->SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED);
+ pDocker->SetRedraw(TRUE);
+ pDocker->RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ALLCHILDREN);
+ pDocker->SetWindowText(pDocker->GetCaption().c_str());
+
+ return pDocker;
+ }
+
+ inline void CDocker::CheckAllTargets(LPDRAGPOS pDragPos)
+ // Calls CheckTarget for each possible target zone
+ {
+ if (!GetDockAncestor()->m_TargetCentre.CheckTarget(pDragPos))
+ {
+ if (!GetDockAncestor()->m_TargetLeft.CheckTarget(pDragPos))
+ {
+ if(!GetDockAncestor()->m_TargetTop.CheckTarget(pDragPos))
+ {
+ if(!GetDockAncestor()->m_TargetRight.CheckTarget(pDragPos))
+ {
+ if(!GetDockAncestor()->m_TargetBottom.CheckTarget(pDragPos))
+ {
+ // Not in a docking zone, so clean up
+ NMHDR nmhdr = pDragPos->hdr;
+ CDocker* pDockDrag = (CDocker*)FromHandle(nmhdr.hwndFrom);
+ if (pDockDrag)
+ {
+ if (pDockDrag->m_BlockMove)
+ pDockDrag->RedrawWindow(0, 0, RDW_FRAME|RDW_INVALIDATE);
+
+ GetDockHint().Destroy();
+ pDockDrag->m_dwDockZone = 0;
+ pDockDrag->m_BlockMove = FALSE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ inline BOOL CDocker::VerifyDockers()
+ // A diagnostic routine which verifies the integrity of the docking layout
+ {
+ BOOL bResult = TRUE;
+
+ // Check dock ancestor
+ std::vector<DockPtr>::iterator iter;
+
+ for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter)
+ {
+ if (GetDockAncestor() != (*iter)->m_pDockAncestor)
+ {
+ TRACE(_T("Invalid Dock Ancestor\n"));
+ bResult = FALSE;
+ }
+ }
+
+ // Check presence of dock parent
+ for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter)
+ {
+ if ((*iter)->IsUndocked() && (*iter)->m_pDockParent != 0)
+ {
+ TRACE(_T("Error: Undocked dockers should not have a dock parent\n"));
+ bResult = FALSE;
+ }
+
+ if ((*iter)->IsDocked() && (*iter)->m_pDockParent == 0)
+ {
+ TRACE(_T("Error: Docked dockers should have a dock parent\n"));
+ bResult = FALSE;
+ }
+ }
+
+ // Check dock parent/child relationship
+ for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter)
+ {
+ std::vector<CDocker*>::iterator iterChild;
+ for (iterChild = (*iter)->GetDockChildren().begin(); iterChild != (*iter)->GetDockChildren().end(); ++iterChild)
+ {
+ if ((*iterChild)->m_pDockParent != (*iter).get())
+ {
+ TRACE(_T("Error: Docking parent/Child information mismatch\n"));
+ bResult = FALSE;
+ }
+ if ((*iterChild)->GetParent() != (*iter).get())
+ {
+ TRACE(_T("Error: Incorrect windows child parent relationship\n"));
+ bResult = FALSE;
+ }
+ }
+ }
+
+ // Check dock parent chain
+ for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter)
+ {
+ CDocker* pDockTopLevel = (*iter)->GetTopmostDocker();
+ if (pDockTopLevel->IsDocked())
+ TRACE(_T("Error: Top level parent should be undocked\n"));
+ }
+
+ return bResult;
+ }
+
+ inline void CDocker::Close()
+ {
+ // Destroy the docker
+ Hide();
+ Destroy();
+ }
+
+ inline void CDocker::CloseAllDockers()
+ {
+ assert(this == GetDockAncestor()); // Must call CloseAllDockers from the DockAncestor
+
+ std::vector <DockPtr>::iterator v;
+
+ SetRedraw(FALSE);
+ std::vector<DockPtr> AllDockers = GetAllDockers();
+ for (v = AllDockers.begin(); v != AllDockers.end(); ++v)
+ {
+ // The CDocker is destroyed when the window is destroyed
+ (*v)->m_bIsClosing = TRUE;
+ (*v)->Destroy(); // Destroy the window
+ }
+
+ GetDockChildren().clear();
+ SetRedraw(TRUE);
+ RecalcDockLayout();
+ }
+
+ inline void CDocker::CloseAllTargets()
+ {
+ GetDockAncestor()->m_TargetCentre.Destroy();
+ GetDockAncestor()->m_TargetLeft.Destroy();
+ GetDockAncestor()->m_TargetTop.Destroy();
+ GetDockAncestor()->m_TargetRight.Destroy();
+ GetDockAncestor()->m_TargetBottom.Destroy();
+ }
+
+ inline void CDocker::Dock(CDocker* pDocker, UINT DockStyle)
+ // Docks the specified docker inside this docker
+ {
+ assert(pDocker);
+
+ pDocker->m_pDockParent = this;
+ pDocker->m_BlockMove = FALSE;
+ pDocker->SetDockStyle(DockStyle);
+ m_vDockChildren.push_back(pDocker);
+ pDocker->ConvertToChild(m_hWnd);
+
+ // Limit the docked size to half the parent's size if it won't fit inside parent
+ if (((DockStyle & 0xF) == DS_DOCKED_LEFT) || ((DockStyle &0xF) == DS_DOCKED_RIGHT))
+ {
+ int Width = GetDockClient().GetWindowRect().Width();
+ int BarWidth = pDocker->GetBarWidth();
+ if (pDocker->m_DockStartSize >= (Width - BarWidth))
+ pDocker->SetDockSize(MAX(Width/2 - BarWidth, BarWidth));
+
+ pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetWindowRect().Width();
+ }
+ else
+ {
+ int Height = GetDockClient().GetWindowRect().Height();
+ int BarWidth = pDocker->GetBarWidth();
+ if (pDocker->m_DockStartSize >= (Height - BarWidth))
+ pDocker->SetDockSize(MAX(Height/2 - BarWidth, BarWidth));
+
+ pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetWindowRect().Height();
+ }
+
+ // Redraw the docked windows
+ GetAncestor()->SetForegroundWindow();
+ GetTopmostDocker()->m_hOldFocus = pDocker->GetView()->GetHwnd();
+ pDocker->GetView()->SetFocus();
+
+ GetTopmostDocker()->SetRedraw(FALSE);
+ RecalcDockLayout();
+ GetTopmostDocker()->SetRedraw(TRUE);
+ GetTopmostDocker()->RedrawWindow();
+ }
+
+ inline void CDocker::DockInContainer(CDocker* pDock, DWORD dwDockStyle)
+ // Add a container to an existing container
+ {
+ if ((dwDockStyle & DS_DOCKED_CONTAINER) && (dynamic_cast<CDockContainer*>(pDock->GetView())))
+ {
+ // Transfer any dock children to this docker
+ pDock->MoveDockChildren(this);
+
+ // Transfer container children to the target container
+ CDockContainer* pContainer = (CDockContainer*)GetView();
+ CDockContainer* pContainerSource = (CDockContainer*)pDock->GetView();
+
+ if (pContainerSource->GetAllContainers().size() > 1)
+ {
+ // The container we're about to add has children, so transfer those first
+ std::vector<ContainerInfo>::reverse_iterator riter;
+ std::vector<ContainerInfo> AllContainers = pContainerSource->GetAllContainers();
+ for ( riter = AllContainers.rbegin() ; riter < AllContainers.rend() -1; ++riter )
+ {
+ // Remove child container from pContainerSource
+ CDockContainer* pContainerChild = (*riter).pContainer;
+ pContainerChild->ShowWindow(SW_HIDE);
+ pContainerSource->RemoveContainer(pContainerChild);
+
+ // Add child container to this container
+ pContainer->AddContainer(pContainerChild);
+
+ CDocker* pDockChild = GetDockFromView(pContainerChild);
+ pDockChild->SetParent(this);
+ pDockChild->m_pDockParent = this;
+ }
+ }
+
+ pContainer->AddContainer((CDockContainer*)pDock->GetView());
+ pDock->m_pDockParent = this;
+ pDock->m_BlockMove = FALSE;
+ pDock->ShowWindow(SW_HIDE);
+ pDock->SetWindowLongPtr(GWL_STYLE, WS_CHILD);
+ pDock->SetDockStyle(dwDockStyle);
+ pDock->SetParent(this);
+ }
+ }
+
+ inline void CDocker::DockOuter(CDocker* pDocker, DWORD dwDockStyle)
+ // Docks the specified docker inside the dock ancestor
+ {
+ assert(pDocker);
+
+ pDocker->m_pDockParent = GetDockAncestor();
+
+ DWORD OuterDocking = dwDockStyle & 0xF0000;
+ DWORD DockSide = OuterDocking / 0x10000;
+ dwDockStyle &= 0xFFF0FFFF;
+ dwDockStyle |= DockSide;
+
+ // Set the dock styles
+ DWORD dwStyle = WS_CHILD | WS_VISIBLE;
+ pDocker->m_BlockMove = FALSE;
+ pDocker->SetWindowLongPtr(GWL_STYLE, dwStyle);
+ pDocker->ShowWindow(SW_HIDE);
+ pDocker->SetDockStyle(dwDockStyle);
+
+ // Set the docking relationships
+ std::vector<CDocker*>::iterator iter = GetDockAncestor()->m_vDockChildren.begin();
+ GetDockAncestor()->m_vDockChildren.insert(iter, pDocker);
+ pDocker->SetParent(GetDockAncestor());
+ pDocker->GetDockBar().SetParent(GetDockAncestor());
+
+ // Limit the docked size to half the parent's size if it won't fit inside parent
+ if (((dwDockStyle & 0xF) == DS_DOCKED_LEFT) || ((dwDockStyle &0xF) == DS_DOCKED_RIGHT))
+ {
+ int Width = GetDockAncestor()->GetDockClient().GetWindowRect().Width();
+ int BarWidth = pDocker->GetBarWidth();
+ if (pDocker->m_DockStartSize >= (Width - BarWidth))
+ pDocker->SetDockSize(MAX(Width/2 - BarWidth, BarWidth));
+
+ pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetDockAncestor()->GetWindowRect().Width();
+ }
+ else
+ {
+ int Height = GetDockAncestor()->GetDockClient().GetWindowRect().Height();
+ int BarWidth = pDocker->GetBarWidth();
+ if (pDocker->m_DockStartSize >= (Height - BarWidth))
+ pDocker->SetDockSize(MAX(Height/2 - BarWidth, BarWidth));
+
+ pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetDockAncestor()->GetWindowRect().Height();
+ }
+
+ // Redraw the docked windows
+ GetAncestor()->SetFocus();
+ pDocker->GetView()->SetFocus();
+ RecalcDockLayout();
+ }
+
+ inline void CDocker::DrawAllCaptions()
+ {
+ std::vector<DockPtr>::iterator iter;
+ for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); iter++)
+ {
+ if ((*iter)->IsDocked())
+ (*iter)->GetDockClient().DrawCaption((WPARAM)1);
+ }
+ }
+
+ inline void CDocker::DrawHashBar(HWND hBar, POINT Pos)
+ // Draws a hashed bar while the splitter bar is being dragged
+ {
+ CDocker* pDock = ((CDockBar*)FromHandle(hBar))->GetDock();
+ if (NULL == pDock) return;
+
+ BOOL bVertical = ((pDock->GetDockStyle() & 0xF) == DS_DOCKED_LEFT) || ((pDock->GetDockStyle() & 0xF) == DS_DOCKED_RIGHT);
+
+ CClientDC dcBar(this);
+
+ WORD HashPattern[] = {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA};
+ CBitmap bmHash;
+ CBrush brDithered;
+ bmHash.CreateBitmap(8, 8, 1, 1, HashPattern);
+ brDithered.CreatePatternBrush(&bmHash);
+ dcBar.SelectObject(&brDithered);
+
+ CRect rc = FromHandle(hBar)->GetWindowRect();
+ ScreenToClient(rc);
+ int cx = rc.Width();
+ int cy = rc.Height();
+ int BarWidth = pDock->GetDockBar().GetWidth();
+
+ if (bVertical)
+ dcBar.PatBlt(Pos.x - BarWidth/2, rc.top, BarWidth, cy, PATINVERT);
+ else
+ dcBar.PatBlt(rc.left, Pos.y - BarWidth/2, cx, BarWidth, PATINVERT);
+ }
+
+ inline CDockContainer* CDocker::GetContainer() const
+ {
+ CDockContainer* pContainer = NULL;
+ if (dynamic_cast<CDockContainer*>(GetView()))
+ pContainer = (CDockContainer*)GetView();
+
+ return pContainer;
+ }
+
+ inline CDocker* CDocker::GetActiveDocker() const
+ // Returns the docker whose child window has focus
+ {
+ CWnd* pWnd = GetFocus();
+ CDocker* pDock= NULL;
+ while (pWnd && (pDock == NULL))
+ {
+ if (IsRelated(pWnd))
+ pDock = (CDocker*)pWnd;
+
+ pWnd = pWnd->GetParent();
+ }
+
+ return pDock;
+ }
+
+ inline CDocker* CDocker::GetDockAncestor() const
+ // The GetDockAncestor function retrieves the pointer to the
+ // ancestor (root docker parent) of the Docker.
+ {
+ return m_pDockAncestor;
+ }
+
+ inline CDocker* CDocker::GetDockFromPoint(POINT pt) const
+ // Retrieves the Docker whose view window contains the specified point
+ {
+ // Step 1: Find the top level Docker the point is over
+ CDocker* pDockTop = NULL;
+ CWnd* pAncestor = GetDockAncestor()->GetAncestor();
+
+ // Iterate through all top level windows
+ CWnd* pWnd = GetWindow(GW_HWNDFIRST);
+ while(pWnd)
+ {
+ if (IsRelated(pWnd) || pWnd == pAncestor)
+ {
+ CDocker* pDockTest;
+ if (pWnd == pAncestor)
+ pDockTest = GetDockAncestor();
+ else
+ pDockTest = (CDocker*)pWnd;
+
+ CRect rc = pDockTest->GetClientRect();
+ pDockTest->ClientToScreen(rc);
+ if ((this != pDockTest) && rc.PtInRect(pt))
+ {
+ pDockTop = pDockTest;
+ break;
+ }
+ }
+
+ pWnd = pWnd->GetWindow(GW_HWNDNEXT);
+ }
+
+ // Step 2: Find the docker child whose view window has the point
+ CDocker* pDockTarget = NULL;
+ if (pDockTop)
+ {
+ CDocker* pDockParent = pDockTop;
+ CDocker* pDockTest = pDockParent;
+
+ while (IsRelated(pDockTest))
+ {
+ pDockParent = pDockTest;
+ CPoint ptLocal = pt;
+ pDockParent->ScreenToClient(ptLocal);
+ pDockTest = (CDocker*)pDockParent->ChildWindowFromPoint(ptLocal);
+ assert (pDockTest != pDockParent);
+ }
+
+ CRect rc = pDockParent->GetDockClient().GetWindowRect();
+ if (rc.PtInRect(pt)) pDockTarget = pDockParent;
+ }
+
+ return pDockTarget;
+ }
+
+ inline CDocker* CDocker::GetDockFromID(int n_DockID) const
+ {
+ std::vector <DockPtr>::iterator v;
+
+ if (GetDockAncestor())
+ {
+ for (v = GetDockAncestor()->m_vAllDockers.begin(); v != GetDockAncestor()->m_vAllDockers.end(); v++)
+ {
+ if (n_DockID == (*v)->GetDockID())
+ return (*v).get();
+ }
+ }
+
+ return 0;
+ }
+
+ inline CDocker* CDocker::GetDockFromView(CWnd* pView) const
+ {
+ CDocker* pDock = 0;
+ std::vector<DockPtr>::iterator iter;
+ std::vector<DockPtr> AllDockers = GetAllDockers();
+ for (iter = AllDockers.begin(); iter != AllDockers.end(); ++iter)
+ {
+ if ((*iter)->GetView() == pView)
+ pDock = (*iter).get();
+ }
+
+ return pDock;
+ }
+
+ inline int CDocker::GetDockSize() const
+ {
+ // Returns the size of the docker to be used if it is redocked
+ // Note: This function returns 0 if the docker has the DS_DOCKED_CONTAINER style
+
+ CRect rcParent;
+ if (GetDockParent())
+ rcParent = GetDockParent()->GetWindowRect();
+ else
+ rcParent = GetDockAncestor()->GetWindowRect();
+
+ double DockSize = 0;
+ if ((GetDockStyle() & DS_DOCKED_LEFT) || (GetDockStyle() & DS_DOCKED_RIGHT))
+ DockSize = (double)(rcParent.Width()*m_DockSizeRatio);
+ else if ((GetDockStyle() & DS_DOCKED_TOP) || (GetDockStyle() & DS_DOCKED_BOTTOM))
+ DockSize = (double)(rcParent.Height()*m_DockSizeRatio);
+ else if ((GetDockStyle() & DS_DOCKED_CONTAINER))
+ DockSize = 0;
+
+ return (int)DockSize;
+ }
+
+ inline CDocker* CDocker::GetTopmostDocker() const
+ // Returns the docker's parent at the top of the Z order.
+ // Could be the dock ancestor or an undocked docker.
+ {
+ CDocker* pDockTopLevel = (CDocker* const)this;
+
+ while(pDockTopLevel->GetDockParent())
+ {
+ assert (pDockTopLevel != pDockTopLevel->GetDockParent());
+ pDockTopLevel = pDockTopLevel->GetDockParent();
+ }
+
+ return pDockTopLevel;
+ }
+
+ inline CTabbedMDI* CDocker::GetTabbedMDI() const
+ {
+ CTabbedMDI* pTabbedMDI = NULL;
+ if (dynamic_cast<CTabbedMDI*>(GetView()))
+ pTabbedMDI = (CTabbedMDI*)GetView();
+
+ return pTabbedMDI;
+ }
+
+ inline int CDocker::GetTextHeight()
+ {
+ NONCLIENTMETRICS nm = {0};
+ nm.cbSize = GetSizeofNonClientMetrics();
+ SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &nm, 0);
+ LOGFONT lf = nm.lfStatusFont;
+
+ CClientDC dc(this);
+ dc.CreateFontIndirect(&lf);
+ CSize szText = dc.GetTextExtentPoint32(_T("Text"), lstrlen(_T("Text")));
+ return szText.cy;
+ }
+
+ inline void CDocker::Hide()
+ {
+ // Undocks a docker (if needed) and hides it.
+ // Do unhide the docker, dock it.
+
+ if (IsDocked())
+ {
+ if (dynamic_cast<CDockContainer*>(GetView()))
+ {
+ CDockContainer* pContainer = GetContainer();
+ CDocker* pDock = GetDockFromView(pContainer->GetContainerParent());
+ pDock->UndockContainer(pContainer, GetCursorPos(), FALSE);
+ }
+ else
+ {
+ CDocker* pDockUndockedFrom = SeparateFromDock();
+ pDockUndockedFrom->RecalcDockLayout();
+ }
+ }
+
+ ShowWindow(SW_HIDE);
+ }
+
+ inline BOOL CDocker::IsChildOfDocker(CWnd* pWnd) const
+ // returns true if the specified window is a child of this docker
+ {
+ while ((pWnd != NULL) && (pWnd != GetDockAncestor()))
+ {
+ if (pWnd == (CWnd*)this) return TRUE;
+ if (IsRelated(pWnd)) break;
+ pWnd = pWnd->GetParent();
+ }
+
+ return FALSE;
+ }
+
+ inline BOOL CDocker::IsDocked() const
+ {
+ return (((m_DockStyle&0xF) || (m_DockStyle & DS_DOCKED_CONTAINER)) && !m_Undocking); // Boolean expression
+ }
+
+ inline BOOL CDocker::IsDragAutoResize()
+ {
+ return m_bDragAutoResize;
+ }
+
+ inline BOOL CDocker::IsRelated(CWnd* pWnd) const
+ // Returns TRUE if the hWnd is a docker within this dock family
+ {
+ if (GetDockAncestor() == pWnd) return TRUE;
+
+ std::vector<DockPtr>::iterator iter;
+ for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter)
+ {
+ if ((*iter).get() == pWnd) return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ inline BOOL CDocker::IsUndocked() const
+ {
+ return (!((m_DockStyle&0xF)|| (m_DockStyle & DS_DOCKED_CONTAINER)) && !m_Undocking); // Boolean expression
+ }
+
+ inline BOOL CDocker::LoadRegistrySettings(tString tsRegistryKeyName)
+ // Recreates the docker layout based on information stored in the registry.
+ // Assumes the DockAncestor window is already created.
+ {
+ BOOL bResult = FALSE;
+
+ if (0 != tsRegistryKeyName.size())
+ {
+ std::vector<DockInfo> vDockList;
+ std::vector<int> vActiveContainers;
+ tString tsKey = _T("Software\\") + tsRegistryKeyName + _T("\\Dock Windows");
+ HKEY hKey = 0;
+ RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey);
+ if (hKey)
+ {
+ DWORD dwType = REG_BINARY;
+ DWORD BufferSize = sizeof(DockInfo);
+ DockInfo di;
+ int i = 0;
+ TCHAR szNumber[20];
+ tString tsSubKey = _T("DockChild");
+ tsSubKey += _itot(i, szNumber, 10);
+
+ // Fill the DockList vector from the registry
+ while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&di, &BufferSize))
+ {
+ vDockList.push_back(di);
+ i++;
+ tsSubKey = _T("DockChild");
+ tsSubKey += _itot(i, szNumber, 10);
+ }
+
+ dwType = REG_DWORD;
+ BufferSize = sizeof(int);
+ int nID;
+ i = 0;
+ tsSubKey = _T("ActiveContainer");
+ tsSubKey += _itot(i, szNumber, 10);
+ // Fill the DockList vector from the registry
+ while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&nID, &BufferSize))
+ {
+ vActiveContainers.push_back(nID);
+ i++;
+ tsSubKey = _T("ActiveContainer");
+ tsSubKey += _itot(i, szNumber, 10);
+ }
+
+ RegCloseKey(hKey);
+ if (vDockList.size() > 0) bResult = TRUE;
+ }
+
+ // Add dockers without parents first
+ std::vector<DockInfo>::iterator iter;
+ for (iter = vDockList.begin(); iter < vDockList.end() ; ++iter)
+ {
+ DockInfo di = (*iter);
+ if (di.DockParentID == 0)
+ {
+ CDocker* pDocker = NewDockerFromID(di.DockID);
+ if (pDocker)
+ {
+ if (di.DockStyle & 0xF)
+ AddDockedChild(pDocker, di.DockStyle, di.DockSize, di.DockID);
+ else
+ AddUndockedChild(pDocker, di.DockStyle, di.DockSize, di.Rect, di.DockID);
+ }
+ else
+ {
+ TRACE(_T("Failed to add dockers without parents from registry"));
+ bResult = FALSE;
+ }
+ }
+ }
+
+ // Remove dockers without parents from vDockList
+ for (UINT n = (UINT)vDockList.size(); n > 0; --n)
+ {
+ iter = vDockList.begin() + n-1;
+ if ((*iter).DockParentID == 0)
+ vDockList.erase(iter);
+ }
+
+ // Add remaining dockers
+ while (vDockList.size() > 0)
+ {
+ bool bFound = false;
+ std::vector<DockInfo>::iterator iter;
+ for (iter = vDockList.begin(); iter < vDockList.end(); ++iter)
+ {
+ DockInfo di = *iter;
+ CDocker* pDockParent = GetDockFromID(di.DockParentID);
+
+ if (pDockParent != 0)
+ {
+ CDocker* pDock = NewDockerFromID(di.DockID);
+ if(pDock)
+ {
+ pDockParent->AddDockedChild(pDock, di.DockStyle, di.DockSize, di.DockID);
+ bFound = true;
+ }
+ else
+ {
+ TRACE(_T("Failed to add dockers with parents from registry"));
+ bResult = FALSE;
+ }
+
+ vDockList.erase(iter);
+ break;
+ }
+ }
+
+ if (!bFound)
+ {
+ TRACE(_T("Orphaned dockers stored in registry "));
+ bResult = FALSE;
+ break;
+ }
+ }
+
+ std::vector<int>::iterator iterID;
+ for (iterID = vActiveContainers.begin(); iterID < vActiveContainers.end(); ++iterID)
+ {
+ CDocker* pDocker = GetDockFromID(*iterID);
+ if (pDocker)
+ {
+ CDockContainer* pContainer = pDocker->GetContainer();
+ if (pContainer)
+ {
+ int nPage = pContainer->GetContainerIndex(pContainer);
+ if (nPage >= 0)
+ pContainer->SelectPage(nPage);
+ }
+ }
+ }
+ }
+
+ if (!bResult) CloseAllDockers();
+ return bResult;
+ }
+
+ inline void CDocker::MoveDockChildren(CDocker* pDockTarget)
+ // Used internally by Dock and Undock
+ {
+ assert(pDockTarget);
+
+ // Transfer any dock children from the current docker to the target docker
+ std::vector<CDocker*>::iterator iter;
+ for (iter = GetDockChildren().begin(); iter < GetDockChildren().end(); ++iter)
+ {
+ pDockTarget->GetDockChildren().push_back(*iter);
+ (*iter)->m_pDockParent = pDockTarget;
+ (*iter)->SetParent(pDockTarget);
+ (*iter)->GetDockBar().SetParent(pDockTarget);
+ }
+ GetDockChildren().clear();
+ }
+
+ inline CDocker* CDocker::NewDockerFromID(int nID)
+ // Used in LoadRegistrySettings. Creates a new Docker from the specified ID
+ {
+ UNREFERENCED_PARAMETER(nID);
+
+ // Override this function to create the Docker objects as shown below
+
+ CDocker* pDock = NULL;
+ /* switch(nID)
+ {
+ case ID_CLASSES:
+ pDock = new CDockClasses;
+ break;
+ case ID_FILES:
+ pDock = new CDockFiles;
+ break;
+ default:
+ TRACE(_T("Unknown Dock ID\n"));
+ break;
+ } */
+
+ return pDock;
+ }
+
+ inline void CDocker::OnActivate(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(lParam);
+
+ // Only top level undocked dockers get this message
+ if (LOWORD(wParam) == WA_INACTIVE)
+ {
+ GetTopmostDocker()->m_hOldFocus = ::GetFocus();
+
+ // Send a notification of focus lost
+ int idCtrl = ::GetDlgCtrlID(m_hOldFocus);
+ NMHDR nhdr={0};
+ nhdr.hwndFrom = m_hOldFocus;
+ nhdr.idFrom = idCtrl;
+ nhdr.code = UWM_FRAMELOSTFOCUS;
+ SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr);
+ }
+ }
+
+ inline void CDocker::OnCaptionTimer(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(lParam);
+
+ if (this == GetDockAncestor())
+ {
+ if (wParam == 1)
+ {
+ DrawAllCaptions();
+ m_nTimerCount++;
+ if (m_nTimerCount == 10)
+ {
+ KillTimer(wParam);
+ m_nTimerCount = 0;
+ }
+ }
+ }
+ }
+
+ inline void CDocker::OnCreate()
+ {
+
+#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500)
+ if (GetParent()->GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
+ {
+ DWORD dwExStyle = GetWindowLongPtr(GWL_EXSTYLE);
+ SetWindowLongPtr(GWL_EXSTYLE, dwExStyle | WS_EX_LAYOUTRTL);
+ }
+#endif
+
+ // Create the various child windows
+ GetDockClient().SetDock(this);
+ GetDockClient().Create(this);
+
+ assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window
+ GetView()->Create(&GetDockClient());
+
+ // Create the slider bar belonging to this docker
+ GetDockBar().SetDock(this);
+ if (GetDockAncestor() != this)
+ GetDockBar().Create(GetParent());
+
+ // Now remove the WS_POPUP style. It was required to allow this window
+ // to be owned by the frame window.
+ SetWindowLongPtr(GWL_STYLE, WS_CHILD);
+ SetParent(GetParent()); // Reinstate the window's parent
+
+ // Set the default colour for the splitter bar
+ COLORREF rgbColour = GetSysColor(COLOR_BTNFACE);
+ CWnd* pFrame = GetDockAncestor()->GetAncestor();
+ ReBarTheme* pTheme = (ReBarTheme*)pFrame->SendMessage(UWM_GETREBARTHEME, 0, 0);
+
+ if (pTheme && pTheme->UseThemes && pTheme->clrBkgnd2 != 0)
+ rgbColour =pTheme->clrBkgnd2;
+
+ SetBarColor(rgbColour);
+
+ // Set the caption height based on text height
+ m_NCHeight = MAX(20, GetTextHeight() + 5);
+ }
+
+ inline void CDocker::OnDestroy(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ // Destroy any dock children first
+ std::vector<CDocker*>::iterator iter;
+ for (iter = GetDockChildren().begin(); iter < GetDockChildren().end(); ++iter)
+ {
+ (*iter)->Destroy();
+ }
+
+ if (dynamic_cast<CDockContainer*>(GetView()) && IsUndocked())
+ {
+ CDockContainer* pContainer = (CDockContainer*)GetView();
+ if (pContainer->GetAllContainers().size() > 1)
+ {
+ // This container has children, so destroy them now
+ std::vector<ContainerInfo> AllContainers = pContainer->GetAllContainers();
+ std::vector<ContainerInfo>::iterator iter;
+ for (iter = AllContainers.begin(); iter < AllContainers.end(); ++iter)
+ {
+ if ((*iter).pContainer != pContainer)
+ {
+ // Reset container parent before destroying the dock window
+ CDocker* pDock = GetDockFromView((*iter).pContainer);
+ if (pContainer->IsWindow())
+ pContainer->SetParent(&pDock->GetDockClient());
+
+ pDock->Destroy();
+ }
+ }
+ }
+ }
+
+ GetDockBar().Destroy();
+
+ // Post a destroy docker message
+ if ( GetDockAncestor()->IsWindow() )
+ GetDockAncestor()->PostMessage(UWM_DOCK_DESTROYED, (WPARAM)this, 0L);
+ }
+
+ inline void CDocker::OnDockDestroyed(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(lParam);
+
+ CDocker* pDock = (CDocker*)wParam;
+
+ assert( this == GetDockAncestor() );
+ std::vector<DockPtr>::iterator iter;
+ for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter)
+ {
+ if ((*iter).get() == pDock)
+ {
+ GetAllDockers().erase(iter);
+ break;
+ }
+ }
+ }
+
+ inline void CDocker::OnExitSizeMove(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ m_BlockMove = FALSE;
+ m_bIsDragging = FALSE;
+ SendNotify(UWM_DOCK_END);
+ }
+
+ inline LRESULT CDocker::OnNotify(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ LPDRAGPOS pdp = (LPDRAGPOS)lParam;
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case UWM_DOCK_START:
+ {
+ if (IsDocked())
+ {
+ Undock(GetCursorPos());
+ SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(pdp->ptPos.x, pdp->ptPos.y));
+ }
+ }
+ break;
+
+ case UWM_DOCK_MOVE:
+ {
+ CheckAllTargets((LPDRAGPOS)lParam);
+ }
+ break;
+
+ case UWM_DOCK_END:
+ {
+ CDocker* pDock = (CDocker*)FromHandle(pdp->hdr.hwndFrom);
+ if (NULL == pDock) break;
+
+ UINT DockZone = pdp->DockZone;
+ CRect rc = pDock->GetWindowRect();
+
+ switch(DockZone)
+ {
+ case DS_DOCKED_LEFT:
+ case DS_DOCKED_RIGHT:
+ pDock->SetDockSize(rc.Width());
+ Dock(pDock, pDock->GetDockStyle() | DockZone);
+ break;
+ case DS_DOCKED_TOP:
+ case DS_DOCKED_BOTTOM:
+ pDock->SetDockSize(rc.Height());
+ Dock(pDock, pDock->GetDockStyle() | DockZone);
+ break;
+ case DS_DOCKED_CONTAINER:
+ {
+ DockInContainer(pDock, pDock->GetDockStyle() | DockZone);
+ CDockContainer* pContainer = (CDockContainer*)GetView();
+ int nPage = pContainer->GetContainerIndex((CDockContainer*)pDock->GetView());
+ pContainer->SelectPage(nPage);
+ }
+ break;
+ case DS_DOCKED_LEFTMOST:
+ case DS_DOCKED_RIGHTMOST:
+ pDock->SetDockSize(rc.Width());
+ DockOuter(pDock, pDock->GetDockStyle() | DockZone);
+ break;
+ case DS_DOCKED_TOPMOST:
+ case DS_DOCKED_BOTTOMMOST:
+ pDock->SetDockSize(rc.Height());
+ DockOuter(pDock, pDock->GetDockStyle() | DockZone);
+ break;
+ }
+
+ GetDockHint().Destroy();
+ CloseAllTargets();
+ }
+ break;
+
+ case UWM_BAR_START:
+ {
+ CPoint pt = pdp->ptPos;
+ ScreenToClient(pt);
+ if (!IsDragAutoResize())
+ DrawHashBar(pdp->hdr.hwndFrom, pt);
+ m_OldPoint = pt;
+ }
+ break;
+
+ case UWM_BAR_MOVE:
+ {
+ CPoint pt = pdp->ptPos;
+ ScreenToClient(pt);
+
+ if (pt != m_OldPoint)
+ {
+ if (IsDragAutoResize())
+ ResizeDockers(pdp);
+ else
+ {
+ DrawHashBar(pdp->hdr.hwndFrom, m_OldPoint);
+ DrawHashBar(pdp->hdr.hwndFrom, pt);
+ }
+
+ m_OldPoint = pt;
+ }
+ }
+ break;
+
+ case UWM_BAR_END:
+ {
+ POINT pt = pdp->ptPos;
+ ScreenToClient(pt);
+
+ if (!IsDragAutoResize())
+ DrawHashBar(pdp->hdr.hwndFrom, pt);
+
+ ResizeDockers(pdp);
+ }
+ break;
+ case NM_SETFOCUS:
+ if (GetDockAncestor()->IsWindow())
+ GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0);
+ break;
+ case UWM_FRAMEGOTFOCUS:
+ if (GetDockAncestor()->IsWindow())
+ GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0);
+ if (GetView()->IsWindow())
+ GetView()->SendMessage(WM_NOTIFY, wParam, lParam);
+ break;
+ case UWM_FRAMELOSTFOCUS:
+ if (GetDockAncestor()->IsWindow())
+ GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0);
+ if (GetView()->IsWindow())
+ GetView()->SendMessage(WM_NOTIFY, wParam, lParam);
+ break;
+ }
+ return 0L;
+ }
+
+ inline void CDocker::ResizeDockers(LPDRAGPOS pdp)
+ // Called when the docker's splitter bar is dragged
+ {
+ assert(pdp);
+
+ POINT pt = pdp->ptPos;
+ ScreenToClient(pt);
+
+ CDocker* pDock = ((CDockBar*)FromHandle(pdp->hdr.hwndFrom))->GetDock();
+ if (NULL == pDock) return;
+
+ RECT rcDock = pDock->GetWindowRect();
+ ScreenToClient(rcDock);
+
+ double dBarWidth = pDock->GetDockBar().GetWidth();
+ int iBarWidth = pDock->GetDockBar().GetWidth();
+ int DockSize;
+
+ switch (pDock->GetDockStyle() & 0xF)
+ {
+ case DS_DOCKED_LEFT:
+ DockSize = MAX(pt.x, iBarWidth/2) - rcDock.left - (int)(.5* dBarWidth);
+ DockSize = MAX(-iBarWidth, DockSize);
+ pDock->SetDockSize(DockSize);
+ pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Width());
+ break;
+ case DS_DOCKED_RIGHT:
+ DockSize = rcDock.right - MAX(pt.x, iBarWidth/2) - (int)(.5* dBarWidth);
+ DockSize = MAX(-iBarWidth, DockSize);
+ pDock->SetDockSize(DockSize);
+ pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Width());
+ break;
+ case DS_DOCKED_TOP:
+ DockSize = MAX(pt.y, iBarWidth/2) - rcDock.top - (int)(.5* dBarWidth);
+ DockSize = MAX(-iBarWidth, DockSize);
+ pDock->SetDockSize(DockSize);
+ pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Height());
+ break;
+ case DS_DOCKED_BOTTOM:
+ DockSize = rcDock.bottom - MAX(pt.y, iBarWidth/2) - (int)(.5* dBarWidth);
+ DockSize = MAX(-iBarWidth, DockSize);
+ pDock->SetDockSize(DockSize);
+ pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Height());
+ break;
+ }
+
+ RecalcDockLayout();
+ }
+
+ inline void CDocker::OnSetFocus(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ if (IsUndocked() && m_hOldFocus)
+ ::SetFocus(m_hOldFocus);
+ else
+ // Pass focus on the the view window
+ GetView()->SetFocus();
+
+ if ((this == GetTopmostDocker()) && (this != GetDockAncestor()))
+ {
+ // Send a notification to top level window
+ int idCtrl = ::GetDlgCtrlID(m_hOldFocus);
+ NMHDR nhdr={0};
+ nhdr.hwndFrom = m_hOldFocus;
+ nhdr.idFrom = idCtrl;
+ nhdr.code = NM_SETFOCUS;
+ SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr);
+ }
+ }
+
+ inline void CDocker::OnSysColorChange(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ if (this == GetDockAncestor())
+ {
+ COLORREF rgbColour = GetSysColor(COLOR_BTNFACE);
+ CWnd* pFrame = GetDockAncestor()->GetAncestor();
+ ReBarTheme* pTheme = (ReBarTheme*)pFrame->SendMessage(UWM_GETREBARTHEME, 0, 0);
+
+ if (pTheme && pTheme->UseThemes && pTheme->clrBand2 != 0)
+ rgbColour = pTheme->clrBkgnd2;
+ else
+ rgbColour = GetSysColor(COLOR_BTNFACE);
+
+ // Set the splitter bar colour for each docker decendant
+ std::vector<DockPtr>::iterator iter;
+ for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter)
+ (*iter)->SetBarColor(rgbColour);
+
+ // Set the splitter bar colour for the docker ancestor
+ SetBarColor(rgbColour);
+ }
+ }
+
+ inline LRESULT CDocker::OnSysCommand(WPARAM wParam, LPARAM lParam)
+ {
+ switch(wParam&0xFFF0)
+ {
+ case SC_MOVE:
+ // An undocked docker is being moved
+ {
+ BOOL bResult = FALSE;
+ m_bIsDragging = TRUE;
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ if (SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bResult, 0))
+ {
+ // Turn on DragFullWindows for this move
+ SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, 0, 0);
+
+ // Process this message
+ DefWindowProc(WM_SYSCOMMAND, wParam, lParam);
+
+ // Return DragFullWindows to its previous state
+ SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, bResult, 0, 0);
+ return 0L;
+ }
+ }
+ break;
+ case SC_CLOSE:
+ // The close button is pressed on an undocked docker
+ m_bIsClosing = TRUE;
+ break;
+ }
+ return CWnd::WndProcDefault(WM_SYSCOMMAND, wParam, lParam);
+ }
+
+ inline LRESULT CDocker::OnWindowPosChanging(WPARAM wParam, LPARAM lParam)
+ {
+ // Suspend dock drag moving while over dock zone
+ if (m_BlockMove)
+ {
+ LPWINDOWPOS pWndPos = (LPWINDOWPOS)lParam;
+ pWndPos->flags |= SWP_NOMOVE|SWP_FRAMECHANGED;
+ return 0;
+ }
+
+ return CWnd::WndProcDefault(WM_WINDOWPOSCHANGING, wParam, lParam);
+ }
+
+ inline void CDocker::OnWindowPosChanged(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ if (m_bIsDragging)
+ {
+ // Send a Move notification to the parent
+ if ( IsLeftButtonDown() )
+ {
+ LPWINDOWPOS wPos = (LPWINDOWPOS)lParam;
+ if ((!(wPos->flags & SWP_NOMOVE)) || m_BlockMove)
+ SendNotify(UWM_DOCK_MOVE);
+ }
+ else
+ {
+ CloseAllTargets();
+ m_BlockMove = FALSE;
+ }
+ }
+ else if (this == GetTopmostDocker())
+ {
+ // Reposition the dock children
+ if (IsUndocked() && IsWindowVisible() && !m_bIsClosing) RecalcDockLayout();
+ }
+ }
+
+ inline void CDocker::PreCreate(CREATESTRUCT &cs)
+ {
+ // Specify the WS_POPUP style to have this window owned
+ if (this != GetDockAncestor())
+ cs.style = WS_POPUP;
+
+ cs.dwExStyle = WS_EX_TOOLWINDOW;
+ }
+
+ inline void CDocker::PreRegisterClass(WNDCLASS &wc)
+ {
+ wc.lpszClassName = _T("Win32++ Docker");
+ wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+ }
+
+ inline void CDocker::RecalcDockChildLayout(CRect rc)
+ {
+ // This function positions the Docker's dock children, the Dockers client area
+ // and draws the dockbar bars.
+
+ // Notes:
+ // 1) This function is called recursively.
+ // 2) The client area and child dockers are positioned simultaneously with
+ // DeferWindowPos to avoid drawing errors in complex docker arrangements.
+ // 3) The docker's client area contains the docker's caption (if any) and the docker's view window.
+
+ // Note: All top level dockers are undocked, including the dock ancestor.
+ if (IsDocked())
+ {
+ rc.OffsetRect(-rc.left, -rc.top);
+ }
+
+ HDWP hdwp = BeginDeferWindowPos((int)m_vDockChildren.size() +2);
+
+ // Step 1: Calculate the position of each Docker child, DockBar, and Client window.
+ // The Client area = the docker rect minus the area of dock children and the dock bar (splitter bar).
+ for (UINT u = 0; u < m_vDockChildren.size(); ++u)
+ {
+ CRect rcChild = rc;
+ double DockSize = m_vDockChildren[u]->m_DockStartSize;;
+
+ // Calculate the size of the Docker children
+ switch (m_vDockChildren[u]->GetDockStyle() & 0xF)
+ {
+ case DS_DOCKED_LEFT:
+ if (!(GetDockStyle() & DS_FIXED_RESIZE))
+ DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Width()), rcChild.Width());
+ rcChild.right = rcChild.left + (int)DockSize;
+ break;
+ case DS_DOCKED_RIGHT:
+ if (!(GetDockStyle() & DS_FIXED_RESIZE))
+ DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Width()), rcChild.Width());
+ rcChild.left = rcChild.right - (int)DockSize;
+ break;
+ case DS_DOCKED_TOP:
+ if (!(GetDockStyle() & DS_FIXED_RESIZE))
+ DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Height()), rcChild.Height());
+ rcChild.bottom = rcChild.top + (int)DockSize;
+ break;
+ case DS_DOCKED_BOTTOM:
+ if (!(GetDockStyle() & DS_FIXED_RESIZE))
+ DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Height()), rcChild.Height());
+ rcChild.top = rcChild.bottom - (int)DockSize;
+ break;
+ }
+
+ if (m_vDockChildren[u]->IsDocked())
+ {
+ // Position this docker's children
+ hdwp = m_vDockChildren[u]->DeferWindowPos(hdwp, NULL, rcChild, SWP_SHOWWINDOW|SWP_FRAMECHANGED);
+ m_vDockChildren[u]->m_rcChild = rcChild;
+
+ rc.SubtractRect(rc, rcChild);
+
+ // Calculate the dimensions of the splitter bar
+ CRect rcBar = rc;
+ DWORD DockSide = m_vDockChildren[u]->GetDockStyle() & 0xF;
+
+ if (DS_DOCKED_LEFT == DockSide) rcBar.right = rcBar.left + m_vDockChildren[u]->GetBarWidth();
+ if (DS_DOCKED_RIGHT == DockSide) rcBar.left = rcBar.right - m_vDockChildren[u]->GetBarWidth();
+ if (DS_DOCKED_TOP == DockSide) rcBar.bottom = rcBar.top + m_vDockChildren[u]->GetBarWidth();
+ if (DS_DOCKED_BOTTOM == DockSide) rcBar.top = rcBar.bottom - m_vDockChildren[u]->GetBarWidth();
+
+ // Save the splitter bar position. We will reposition it later.
+ m_vDockChildren[u]->m_rcBar = rcBar;
+ rc.SubtractRect(rc, rcBar);
+ }
+ }
+
+ // Step 2: Position the Dock client and dock bar
+ hdwp = GetDockClient().DeferWindowPos(hdwp, NULL, rc, SWP_SHOWWINDOW |SWP_FRAMECHANGED);
+ EndDeferWindowPos(hdwp);
+
+ // Position the dockbar. Only docked dockers have a dock bar.
+ if (IsDocked())
+ {
+ // The SWP_NOCOPYBITS forces a redraw of the dock bar.
+ GetDockBar().SetWindowPos(NULL, m_rcBar, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOCOPYBITS );
+ }
+
+ // Step 3: Now recurse through the docker's children. They might have children of their own.
+ for (UINT v = 0; v < m_vDockChildren.size(); ++v)
+ {
+ m_vDockChildren[v]->RecalcDockChildLayout(m_vDockChildren[v]->m_rcChild);
+ }
+ }
+
+ inline void CDocker::RecalcDockLayout()
+ // Repositions the dock children of a top level docker
+ {
+ if (GetDockAncestor()->IsWindow())
+ {
+ CRect rc = GetTopmostDocker()->GetClientRect();
+ GetTopmostDocker()->RecalcDockChildLayout(rc);
+ GetTopmostDocker()->UpdateWindow();
+ }
+ }
+
+ inline std::vector<CDocker*> CDocker::SortDockers()
+ // Returns a vector of sorted dockers, used by SaveRegistrySettings.
+ {
+ std::vector<CDocker*> vSorted;
+ std::vector<CDocker*>::iterator itSort;
+ std::vector<DockPtr>::iterator itAll;
+
+ // Add undocked top level dockers
+ for (itAll = GetAllDockers().begin(); itAll < GetAllDockers().end(); ++itAll)
+ {
+ if (!(*itAll)->GetDockParent())
+ vSorted.push_back((*itAll).get());
+ }
+
+ // Add dock ancestor's children
+ vSorted.insert(vSorted.end(), GetDockAncestor()->GetDockChildren().begin(), GetDockAncestor()->GetDockChildren().end());
+
+ // Add other dock children
+ int index = 0;
+ itSort = vSorted.begin();
+ while (itSort < vSorted.end())
+ {
+ vSorted.insert(vSorted.end(), (*itSort)->GetDockChildren().begin(), (*itSort)->GetDockChildren().end());
+ itSort = vSorted.begin() + (++index);
+ }
+
+ // Add dockers docked in containers
+ std::vector<CDocker*> vDockContainers;
+ for (itSort = vSorted.begin(); itSort< vSorted.end(); ++itSort)
+ {
+ if ((*itSort)->GetContainer())
+ vDockContainers.push_back(*itSort);
+ }
+
+ for (itSort = vDockContainers.begin(); itSort < vDockContainers.end(); ++itSort)
+ {
+ CDockContainer* pContainer = (*itSort)->GetContainer();
+
+ for (UINT i = 1; i < pContainer->GetAllContainers().size(); ++i)
+ {
+ CDockContainer* pChild = pContainer->GetContainerFromIndex(i);
+ CDocker* pDock = GetDockFromView(pChild);
+ vSorted.push_back(pDock);
+ }
+ }
+
+ return vSorted;
+ }
+
+ inline BOOL CDocker::SaveRegistrySettings(tString tsRegistryKeyName)
+ // Stores the docking configuration in the registry
+ // NOTE: This function assumes that each docker has a unique DockID
+ {
+ assert(VerifyDockers());
+
+ std::vector<CDocker*> vSorted = SortDockers();
+ std::vector<CDocker*>::iterator iter;
+ std::vector<DockInfo> vDockInfo;
+
+ if (0 != tsRegistryKeyName.size())
+ {
+ // Fill the DockInfo vector with the docking information
+ for (iter = vSorted.begin(); iter < vSorted.end(); ++iter)
+ {
+ DockInfo di = {0};
+ di.DockID = (*iter)->GetDockID();
+ di.DockStyle = (*iter)->GetDockStyle();
+ di.DockSize = (*iter)->GetDockSize();
+ di.Rect = (*iter)->GetWindowRect();
+ if ((*iter)->GetDockParent())
+ di.DockParentID = (*iter)->GetDockParent()->GetDockID();
+
+ vDockInfo.push_back(di);
+ }
+
+ tString tsKeyName = _T("Software\\") + tsRegistryKeyName;
+ HKEY hKey = NULL;
+ HKEY hKeyDock = NULL;
+
+ try
+ {
+ if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL))
+ throw (CWinException(_T("RegCreateKeyEx Failed")));
+
+ RegDeleteKey(hKey, _T("Dock Windows"));
+ if (ERROR_SUCCESS != RegCreateKeyEx(hKey, _T("Dock Windows"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyDock, NULL))
+ throw (CWinException(_T("RegCreateKeyEx Failed")));
+
+ // Add the Dock windows information to the registry
+ for (UINT u = 0; u < vDockInfo.size(); ++u)
+ {
+ DockInfo di = vDockInfo[u];
+ TCHAR szNumber[16];
+ tString tsSubKey = _T("DockChild");
+ tsSubKey += _itot((int)u, szNumber, 10);
+ if(ERROR_SUCCESS != RegSetValueEx(hKeyDock, tsSubKey.c_str(), 0, REG_BINARY, (LPBYTE)&di, sizeof(DockInfo)))
+ throw (CWinException(_T("RegSetValueEx failed")));
+ }
+
+ // Add Active Container to the registry
+ int i = 0;
+ for (iter = vSorted.begin(); iter < vSorted.end(); ++iter)
+ {
+ CDockContainer* pContainer = (*iter)->GetContainer();
+
+ if (pContainer && (pContainer == pContainer->GetActiveContainer()))
+ {
+ TCHAR szNumber[20];
+ tString tsSubKey = _T("ActiveContainer");
+ tsSubKey += _itot(i++, szNumber, 10);
+ int nID = GetDockFromView(pContainer)->GetDockID();
+ if(ERROR_SUCCESS != RegSetValueEx(hKeyDock, tsSubKey.c_str(), 0, REG_DWORD, (LPBYTE)&nID, sizeof(int)))
+ throw (CWinException(_T("RegSetValueEx failed")));
+ }
+ }
+
+ RegCloseKey(hKeyDock);
+ RegCloseKey(hKey);
+ }
+
+ catch (const CWinException& e)
+ {
+ // Roll back the registry changes by deleting the subkeys
+ if (hKey)
+ {
+ if (hKeyDock)
+ {
+ RegDeleteKey(hKeyDock, _T("Dock Windows"));
+ RegCloseKey(hKeyDock);
+ }
+
+ RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str());
+ RegCloseKey(hKey);
+ }
+
+ e.what();
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ inline void CDocker::SendNotify(UINT nMessageID)
+ // Sends a docking notification to the docker below the cursor
+ {
+ DRAGPOS DragPos;
+ DragPos.hdr.code = nMessageID;
+ DragPos.hdr.hwndFrom = m_hWnd;
+ DragPos.ptPos = GetCursorPos();
+ DragPos.DockZone = m_dwDockZone;
+ m_dwDockZone = 0;
+
+ CDocker* pDock = GetDockFromPoint(DragPos.ptPos);
+
+ if (pDock)
+ pDock->SendMessage(WM_NOTIFY, 0L, (LPARAM)&DragPos);
+ else
+ {
+ if (GetDockHint().IsWindow()) GetDockHint().Destroy();
+ CloseAllTargets();
+ m_BlockMove = FALSE;
+ }
+ }
+
+ inline void CDocker::SetDockStyle(DWORD dwDockStyle)
+ {
+ if (IsWindow())
+ {
+ if ((dwDockStyle & DS_CLIENTEDGE) != (m_DockStyle & DS_CLIENTEDGE))
+ {
+ if (dwDockStyle & DS_CLIENTEDGE)
+ {
+ DWORD dwExStyle = (DWORD)GetDockClient().GetWindowLongPtr(GWL_EXSTYLE)|WS_EX_CLIENTEDGE;
+ GetDockClient().SetWindowLongPtr(GWL_EXSTYLE, dwExStyle);
+ GetDockClient().RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_FRAME);
+ }
+ else
+ {
+ DWORD dwExStyle = (DWORD)GetDockClient().GetWindowLongPtr(GWL_EXSTYLE);
+ dwExStyle &= ~WS_EX_CLIENTEDGE;
+ GetDockClient().SetWindowLongPtr(GWL_EXSTYLE, dwExStyle);
+ GetDockClient().RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_FRAME);
+ }
+ }
+
+ RecalcDockLayout();
+ }
+
+ m_DockStyle = dwDockStyle;
+ }
+
+ inline void CDocker::SetCaption(LPCTSTR szCaption)
+ // Sets the caption text
+ {
+ GetDockClient().SetCaption(szCaption);
+
+ if (IsWindow())
+ SetWindowText(szCaption);
+ }
+
+ inline void CDocker::SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2)
+ {
+ GetDockClient().SetCaptionColors(Foregnd1, Backgnd1, ForeGnd2, BackGnd2);
+ }
+
+ inline void CDocker::SetCaptionHeight(int nHeight)
+ // Sets the height of the caption
+ {
+ m_NCHeight = nHeight;
+ RedrawWindow();
+ RecalcDockLayout();
+ }
+
+ inline void CDocker::SetDockSize(int DockSize)
+ // Sets the size of a docked docker
+ {
+ if (IsDocked())
+ {
+ assert (m_pDockParent);
+ switch (GetDockStyle() & 0xF)
+ {
+ case DS_DOCKED_LEFT:
+ m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Width());
+ m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Width());
+ break;
+ case DS_DOCKED_RIGHT:
+ m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Width());
+ m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Width());
+ break;
+ case DS_DOCKED_TOP:
+ m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Height());
+ m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Height());
+ break;
+ case DS_DOCKED_BOTTOM:
+ m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Height());
+ m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Height());
+ break;
+ }
+
+ RecalcDockLayout();
+ }
+ else
+ {
+ m_DockStartSize = DockSize;
+ m_DockSizeRatio = 1.0;
+ }
+ }
+
+ inline void CDocker::SetDragAutoResize(BOOL bAutoResize)
+ {
+ m_bDragAutoResize = bAutoResize;
+ }
+
+ inline void CDocker::SetView(CWnd& wndView)
+ // Assigns the view window to the docker
+ {
+ CWnd* pWnd = &wndView;
+ GetDockClient().SetView(wndView);
+ if (dynamic_cast<CDockContainer*>(pWnd))
+ {
+ CDockContainer* pContainer = (CDockContainer*)&wndView;
+ SetCaption(pContainer->GetDockCaption().c_str());
+ }
+ }
+
+ inline void CDocker::PromoteFirstChild()
+ // One of the steps required for undocking
+ {
+ // Promote our first child to replace ourself
+ if (m_pDockParent)
+ {
+ for (UINT u = 0 ; u < m_pDockParent->m_vDockChildren.size(); ++u)
+ {
+ if (m_pDockParent->m_vDockChildren[u] == this)
+ {
+ if (m_vDockChildren.size() > 0)
+ // swap our first child for ourself as a child of the parent
+ m_pDockParent->m_vDockChildren[u] = m_vDockChildren[0];
+ else
+ // remove ourself as a child of the parent
+ m_pDockParent->m_vDockChildren.erase(m_pDockParent->m_vDockChildren.begin() + u);
+ break;
+ }
+ }
+ }
+
+ // Transfer styles and data and children to the child docker
+ CDocker* pDockFirstChild = NULL;
+ if (m_vDockChildren.size() > 0)
+ {
+ pDockFirstChild = m_vDockChildren[0];
+ pDockFirstChild->m_DockStyle = (pDockFirstChild->m_DockStyle & 0xFFFFFFF0 ) | (m_DockStyle & 0xF);
+ pDockFirstChild->m_DockStartSize = m_DockStartSize;
+ pDockFirstChild->m_DockSizeRatio = m_DockSizeRatio;
+
+ if (m_pDockParent)
+ {
+ pDockFirstChild->m_pDockParent = m_pDockParent;
+ pDockFirstChild->SetParent(m_pDockParent);
+ pDockFirstChild->GetDockBar().SetParent(m_pDockParent);
+ }
+ else
+ {
+ std::vector<CDocker*>::iterator iter;
+ for (iter = GetDockChildren().begin() + 1; iter < GetDockChildren().end(); ++iter)
+ (*iter)->ShowWindow(SW_HIDE);
+
+ pDockFirstChild->ConvertToPopup(GetWindowRect());
+ pDockFirstChild->GetDockBar().ShowWindow(SW_HIDE);
+ }
+
+ m_vDockChildren.erase(m_vDockChildren.begin());
+ MoveDockChildren(pDockFirstChild);
+ }
+ }
+
+ inline void CDocker::ConvertToChild(HWND hWndParent)
+ {
+ DWORD dwStyle = WS_CHILD | WS_VISIBLE;
+ SetWindowLongPtr(GWL_STYLE, dwStyle);
+ SetParent(FromHandle(hWndParent));
+ GetDockBar().SetParent(FromHandle(hWndParent));
+ }
+
+ inline void CDocker::ConvertToPopup(RECT rc)
+ {
+ // Change the window to an "undocked" style
+ ShowWindow(SW_HIDE);
+ DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME;
+ SetWindowLongPtr(GWL_STYLE, dwStyle);
+
+ // Change the window's parent and reposition it
+ GetDockBar().ShowWindow(SW_HIDE);
+ SetWindowPos(0, 0, 0, 0, 0, SWP_NOSENDCHANGING|SWP_HIDEWINDOW|SWP_NOREDRAW);
+ m_pDockParent = 0;
+ SetParent(0);
+ SetWindowPos(NULL, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOOWNERZORDER);
+ GetDockClient().SetWindowPos(NULL, GetClientRect(), SWP_SHOWWINDOW);
+
+ SetWindowText(GetCaption().c_str());
+ }
+
+ inline CDocker* CDocker::SeparateFromDock()
+ {
+ // This performs some of the tasks required for undocking.
+ // It is also used when a docker is hidden.
+ CDocker* pDockUndockedFrom = GetDockParent();
+ if (!pDockUndockedFrom && (GetDockChildren().size() > 0))
+ pDockUndockedFrom = GetDockChildren()[0];
+
+ GetTopmostDocker()->m_hOldFocus = 0;
+ PromoteFirstChild();
+ m_pDockParent = 0;
+
+ GetDockBar().ShowWindow(SW_HIDE);
+ m_DockStyle = m_DockStyle & 0xFFFFFFF0;
+ m_DockStyle &= ~DS_DOCKED_CONTAINER;
+
+ return pDockUndockedFrom;
+ }
+
+ inline void CDocker::SetUndockPosition(CPoint pt)
+ {
+ m_Undocking = TRUE;
+ CRect rc;
+ rc = GetDockClient().GetWindowRect();
+ CRect rcTest = rc;
+ rcTest.bottom = MIN(rcTest.bottom, rcTest.top + m_NCHeight);
+ if ( !rcTest.PtInRect(pt))
+ rc.SetRect(pt.x - rc.Width()/2, pt.y - m_NCHeight/2, pt.x + rc.Width()/2, pt.y - m_NCHeight/2 + rc.Height());
+
+ ConvertToPopup(rc);
+ m_Undocking = FALSE;
+
+ // Send the undock notification to the frame
+ NMHDR nmhdr = {0};
+ nmhdr.hwndFrom = m_hWnd;
+ nmhdr.code = UWM_UNDOCKED;
+ nmhdr.idFrom = m_nDockID;
+ CWnd* pFrame = GetDockAncestor()->GetAncestor();
+ pFrame->SendMessage(WM_NOTIFY, m_nDockID, (LPARAM)&nmhdr);
+
+ // Initiate the window move
+ SetCursorPos(pt.x, pt.y);
+ ScreenToClient(pt);
+ PostMessage(WM_SYSCOMMAND, (WPARAM)(SC_MOVE|0x0002), MAKELPARAM(pt.x, pt.y));
+ }
+
+ inline void CDocker::Undock(CPoint pt, BOOL bShowUndocked)
+ {
+ // Return if we shouldn't undock
+ if (GetDockStyle() & DS_NO_UNDOCK) return;
+
+ // Undocking isn't supported on Win95
+ if (1400 == GetWinVersion()) return;
+
+ CDocker* pDockUndockedFrom = SeparateFromDock();
+
+ // Position and draw the undocked window, unless it is about to be closed
+ if (bShowUndocked)
+ {
+ SetUndockPosition(pt);
+ }
+
+ RecalcDockLayout();
+ if ((pDockUndockedFrom) && (pDockUndockedFrom->GetTopmostDocker() != GetTopmostDocker()))
+ pDockUndockedFrom->RecalcDockLayout();
+ }
+
+ inline void CDocker::UndockContainer(CDockContainer* pContainer, CPoint pt, BOOL bShowUndocked)
+ {
+ assert(pContainer);
+ assert(this == GetDockFromView(pContainer->GetContainerParent()));
+
+ // Return if we shouldn't undock
+ if (GetDockFromView(pContainer)->GetDockStyle() & DS_NO_UNDOCK) return;
+
+ // Undocking isn't supported on Win95
+ if (1400 == GetWinVersion()) return;
+
+ GetTopmostDocker()->m_hOldFocus = 0;
+ CDocker* pDockUndockedFrom = GetDockFromView(pContainer->GetContainerParent());
+ pDockUndockedFrom->ShowWindow(SW_HIDE);
+ if (GetView() == pContainer)
+ {
+ // The parent container is being undocked, so we need
+ // to transfer our child containers to a different docker
+
+ // Choose a new docker from among the dockers for child containers
+ CDocker* pDockNew = 0;
+ CDocker* pDockOld = GetDockFromView(pContainer);
+ std::vector<ContainerInfo> AllContainers = pContainer->GetAllContainers();
+ std::vector<ContainerInfo>::iterator iter = AllContainers.begin();
+ while ((0 == pDockNew) && (iter < AllContainers.end()))
+ {
+ if ((*iter).pContainer != pContainer)
+ pDockNew = (CDocker*)FromHandle(::GetParent((*iter).pContainer->GetParent()->GetHwnd()));
+
+ ++iter;
+ }
+
+ if (pDockNew)
+ {
+ // Move containers from the old docker to the new docker
+ CDockContainer* pContainerNew = (CDockContainer*)pDockNew->GetView();
+ for (iter = AllContainers.begin(); iter != AllContainers.end(); ++iter)
+ {
+ if ((*iter).pContainer != pContainer)
+ {
+ CDockContainer* pChildContainer = (CDockContainer*)(*iter).pContainer;
+ pContainer->RemoveContainer(pChildContainer);
+ if (pContainerNew != pChildContainer)
+ {
+ pContainerNew->AddContainer(pChildContainer);
+ CDocker* pDock = GetDockFromView(pChildContainer);
+ pDock->SetParent(pDockNew);
+ pDock->m_pDockParent = pDockNew;
+ }
+ }
+ }
+
+ // Now transfer the old docker's settings to the new one.
+ pDockUndockedFrom = pDockNew;
+ pDockNew->m_DockStyle = pDockOld->m_DockStyle;
+ pDockNew->m_DockStartSize = pDockOld->m_DockStartSize;
+ pDockNew->m_DockSizeRatio = pDockOld->m_DockSizeRatio;
+ if (pDockOld->IsDocked())
+ {
+ pDockNew->m_pDockParent = pDockOld->m_pDockParent;
+ pDockNew->SetParent(pDockOld->GetParent());
+ }
+ else
+ {
+ CRect rc = pDockOld->GetWindowRect();
+ pDockNew->ShowWindow(SW_HIDE);
+ DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE;
+ pDockNew->SetWindowLongPtr(GWL_STYLE, dwStyle);
+ pDockNew->m_pDockParent = 0;
+ pDockNew->SetParent(0);
+ pDockNew->SetWindowPos(NULL, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED| SWP_NOOWNERZORDER);
+ }
+ pDockNew->GetDockBar().SetParent(pDockOld->GetParent());
+ pDockNew->GetView()->SetFocus();
+
+ // Transfer the Dock children to the new docker
+ pDockOld->MoveDockChildren(pDockNew);
+
+ // insert pDockNew into its DockParent's DockChildren vector
+ if (pDockNew->m_pDockParent)
+ {
+ std::vector<CDocker*>::iterator p;
+ for (p = pDockNew->m_pDockParent->m_vDockChildren.begin(); p != pDockNew->m_pDockParent->m_vDockChildren.end(); ++p)
+ {
+ if (*p == this)
+ {
+ pDockNew->m_pDockParent->m_vDockChildren.insert(p, pDockNew);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // This is a child container, so simply remove it from the parent
+ CDockContainer* pContainerParent = (CDockContainer*)GetView();
+ pContainerParent->RemoveContainer(pContainer);
+ pContainerParent->SetTabSize();
+ pContainerParent->SetFocus();
+ pContainerParent->GetViewPage().SetParent(pContainerParent);
+ }
+
+ // Finally do the actual undocking
+ CDocker* pDock = GetDockFromView(pContainer);
+ CRect rc = GetDockClient().GetWindowRect();
+ ScreenToClient(rc);
+ pDock->GetDockClient().SetWindowPos(NULL, rc, SWP_SHOWWINDOW);
+ pDock->Undock(pt, bShowUndocked);
+ pDockUndockedFrom->ShowWindow();
+ pDockUndockedFrom->RecalcDockLayout();
+ pDock->BringWindowToTop();
+ }
+
+ inline LRESULT CDocker::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_ACTIVATE:
+ OnActivate(wParam, lParam);
+ break;
+ case WM_SYSCOMMAND:
+ return OnSysCommand(wParam, lParam);
+
+ case WM_EXITSIZEMOVE:
+ OnExitSizeMove(wParam, lParam);
+ break;
+ case WM_WINDOWPOSCHANGING:
+ return OnWindowPosChanging(wParam, lParam);
+
+ case WM_WINDOWPOSCHANGED:
+ OnWindowPosChanged(wParam, lParam);
+ break;
+ case WM_DESTROY:
+ OnDestroy(wParam, lParam);
+ break;
+ case WM_SETFOCUS:
+ OnSetFocus(wParam, lParam);
+ break;
+ case WM_TIMER:
+ OnCaptionTimer(wParam, lParam);
+ break;
+ case UWM_DOCK_DESTROYED:
+ OnDockDestroyed(wParam, lParam);
+ break;
+ case UWM_DOCK_ACTIVATED:
+ DrawAllCaptions();
+ SetTimer(1, 100, NULL);
+ break;
+ case WM_SYSCOLORCHANGE:
+ OnSysColorChange(wParam, lParam);
+ break;
+ case WM_NCLBUTTONDBLCLK :
+ m_bIsDragging = FALSE;
+ break;
+ }
+
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+ //////////////////////////////////////
+ // Declaration of the CDockContainer class
+ inline CDockContainer::CDockContainer() : m_iCurrentPage(0), m_hTabIcon(0), m_nTabPressed(-1)
+ {
+ m_pContainerParent = this;
+ }
+
+ inline CDockContainer::~CDockContainer()
+ {
+ if (m_hTabIcon)
+ DestroyIcon(m_hTabIcon);
+ }
+
+ inline void CDockContainer::AddContainer(CDockContainer* pContainer)
+ {
+ assert(pContainer);
+
+ if (this == m_pContainerParent)
+ {
+ ContainerInfo ci = {0};
+ ci.pContainer = pContainer;
+ lstrcpy(ci.szTitle, pContainer->GetTabText());
+ ci.iImage = ImageList_AddIcon(GetImageList(), pContainer->GetTabIcon());
+ int iNewPage = (int)m_vContainerInfo.size();
+ m_vContainerInfo.push_back(ci);
+
+ if (m_hWnd)
+ {
+ TCITEM tie = {0};
+ tie.mask = TCIF_TEXT | TCIF_IMAGE;
+ tie.iImage = ci.iImage;
+ tie.pszText = m_vContainerInfo[iNewPage].szTitle;
+ TabCtrl_InsertItem(m_hWnd, iNewPage, &tie);
+
+ SetTabSize();
+ }
+
+ pContainer->m_pContainerParent = this;
+ if (pContainer->IsWindow())
+ {
+ // Set the parent container relationships
+ pContainer->GetViewPage().SetParent(this);
+ pContainer->GetViewPage().ShowWindow(SW_HIDE);
+ }
+ }
+ }
+
+ inline void CDockContainer::AddToolBarButton(UINT nID, BOOL bEnabled /* = TRUE */)
+ // Adds Resource IDs to toolbar buttons.
+ // A resource ID of 0 is a separator
+ {
+ GetToolBar().AddButton(nID, bEnabled);
+ }
+
+ inline CDockContainer* CDockContainer::GetContainerFromIndex(UINT nPage)
+ {
+ CDockContainer* pContainer = NULL;
+ if (nPage < m_vContainerInfo.size())
+ pContainer = (CDockContainer*)m_vContainerInfo[nPage].pContainer;
+
+ return pContainer;
+ }
+
+ inline CWnd* CDockContainer::GetActiveView() const
+ // Returns a pointer to the active view window, or NULL if there is no active veiw.
+ {
+ CWnd* pWnd = NULL;
+ if (m_pContainerParent->m_vContainerInfo.size() > 0)
+ {
+ CDockContainer* pActiveContainer = m_pContainerParent->m_vContainerInfo[m_pContainerParent->m_iCurrentPage].pContainer;
+ if (pActiveContainer->GetViewPage().GetView()->IsWindow())
+ pWnd = pActiveContainer->GetViewPage().GetView();
+ }
+
+ return pWnd;
+ }
+
+ inline CDockContainer* CDockContainer::GetContainerFromView(CWnd* pView) const
+ {
+ assert(pView);
+
+ std::vector<ContainerInfo>::iterator iter;
+ CDockContainer* pViewContainer = 0;
+ for (iter = GetAllContainers().begin(); iter != GetAllContainers().end(); ++iter)
+ {
+ CDockContainer* pContainer = (*iter).pContainer;
+ if (pContainer->GetView() == pView)
+ pViewContainer = pContainer;
+ }
+
+ return pViewContainer;
+ }
+
+ inline int CDockContainer::GetContainerIndex(CDockContainer* pContainer)
+ {
+ assert(pContainer);
+ int iReturn = -1;
+
+ for (int i = 0; i < (int)m_pContainerParent->m_vContainerInfo.size(); ++i)
+ {
+ if (m_pContainerParent->m_vContainerInfo[i].pContainer == pContainer)
+ iReturn = i;
+ }
+
+ return iReturn;
+ }
+
+ inline SIZE CDockContainer::GetMaxTabTextSize()
+ {
+ CSize Size;
+
+ // Allocate an iterator for the ContainerInfo vector
+ std::vector<ContainerInfo>::iterator iter;
+
+ for (iter = m_vContainerInfo.begin(); iter != m_vContainerInfo.end(); ++iter)
+ {
+ CSize TempSize;
+ CClientDC dc(this);
+ NONCLIENTMETRICS info = {0};
+ info.cbSize = GetSizeofNonClientMetrics();
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
+ dc.CreateFontIndirect(&info.lfStatusFont);
+ TempSize = dc.GetTextExtentPoint32(iter->szTitle, lstrlen(iter->szTitle));
+ if (TempSize.cx > Size.cx)
+ Size = TempSize;
+ }
+
+ return Size;
+ }
+
+ inline void CDockContainer::SetupToolBar()
+ {
+ // Use this function to set the Resource IDs for the toolbar(s).
+
+/* // Set the Resource IDs for the toolbar buttons
+ AddToolBarButton( IDM_FILE_NEW );
+ AddToolBarButton( IDM_FILE_OPEN );
+ AddToolBarButton( IDM_FILE_SAVE );
+ AddToolBarButton( 0 ); // Separator
+ AddToolBarButton( IDM_EDIT_CUT );
+ AddToolBarButton( IDM_EDIT_COPY );
+ AddToolBarButton( IDM_EDIT_PASTE );
+ AddToolBarButton( 0 ); // Separator
+ AddToolBarButton( IDM_FILE_PRINT );
+ AddToolBarButton( 0 ); // Separator
+ AddToolBarButton( IDM_HELP_ABOUT );
+*/
+ }
+
+ inline void CDockContainer::OnCreate()
+ {
+ assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window
+
+ ContainerInfo ci = {0};
+ ci.pContainer = this;
+ lstrcpy(ci.szTitle, GetTabText());
+ ci.iImage = ImageList_AddIcon(GetImageList(), GetTabIcon());
+ m_vContainerInfo.push_back(ci);
+
+ // Create the page window
+ GetViewPage().Create(this);
+
+ // Create the toolbar
+ GetToolBar().Create(&GetViewPage());
+ DWORD style = (DWORD)GetToolBar().GetWindowLongPtr(GWL_STYLE);
+ style |= CCS_NODIVIDER ;
+ GetToolBar().SetWindowLongPtr(GWL_STYLE, style);
+ SetupToolBar();
+ if (GetToolBar().GetToolBarData().size() > 0)
+ {
+ // Set the toolbar images
+ // A mask of 192,192,192 is compatible with AddBitmap (for Win95)
+ if (!GetToolBar().SendMessage(TB_GETIMAGELIST, 0L, 0L))
+ GetToolBar().SetImages(RGB(192,192,192), IDW_MAIN, 0, 0);
+
+ GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L);
+ }
+ else
+ GetToolBar().Destroy();
+
+ SetFixedWidth(TRUE);
+ SetOwnerDraw(TRUE);
+
+ // Add tabs for each container.
+ for (int i = 0; i < (int)m_vContainerInfo.size(); ++i)
+ {
+ // Add tabs for each view.
+ TCITEM tie = {0};
+ tie.mask = TCIF_TEXT | TCIF_IMAGE;
+ tie.iImage = i;
+ tie.pszText = m_vContainerInfo[i].szTitle;
+ TabCtrl_InsertItem(m_hWnd, i, &tie);
+ }
+ }
+
+ inline void CDockContainer::OnLButtonDown(WPARAM wParam, LPARAM lParam)
+ {
+ // Overrides CTab::OnLButtonDown
+
+ UNREFERENCED_PARAMETER(wParam);
+
+ CPoint pt((DWORD)lParam);
+ TCHITTESTINFO info = {0};
+ info.pt = pt;
+ m_nTabPressed = HitTest(info);
+ }
+
+ inline void CDockContainer::OnLButtonUp(WPARAM wParam, LPARAM lParam)
+ {
+ // Overrides CTab::OnLButtonUp and takes no action
+
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ }
+
+ inline void CDockContainer::OnMouseLeave(WPARAM wParam, LPARAM lParam)
+ {
+ // Overrides CTab::OnMouseLeave
+
+ if (IsLeftButtonDown() && (m_nTabPressed >= 0))
+ {
+ CDocker* pDock = (CDocker*)FromHandle(::GetParent(GetParent()->GetHwnd()));
+ if (dynamic_cast<CDocker*>(pDock))
+ {
+ CDockContainer* pContainer = GetContainerFromIndex(m_iCurrentPage);
+ pDock->UndockContainer(pContainer, GetCursorPos(), TRUE);
+ }
+ }
+
+ m_nTabPressed = -1;
+ CTab::OnMouseLeave(wParam, lParam);
+ }
+
+ inline LRESULT CDockContainer::OnNotifyReflect(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case TCN_SELCHANGE:
+ {
+ // Display the newly selected tab page
+ int nPage = GetCurSel();
+ SelectPage(nPage);
+ }
+ break;
+ }
+
+ return 0L;
+ }
+
+ inline void CDockContainer::PreCreate(CREATESTRUCT &cs)
+ {
+ // For Tabs on the bottom, add the TCS_BOTTOM style
+ CTab::PreCreate(cs);
+ cs.style |= TCS_BOTTOM;
+ }
+
+ inline void CDockContainer::RecalcLayout()
+ {
+ if (GetContainerParent() == this)
+ {
+ // Set the tab sizes
+ SetTabSize();
+
+ // Position the View over the tab control's display area
+ CRect rc = GetClientRect();
+ AdjustRect(FALSE, &rc);
+ CDockContainer* pContainer = m_vContainerInfo[m_iCurrentPage].pContainer;
+ pContainer->GetViewPage().SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW);
+ }
+ }
+
+ inline void CDockContainer::RemoveContainer(CDockContainer* pWnd)
+ {
+ assert(pWnd);
+
+ // Remove the tab
+ int iTab = GetContainerIndex(pWnd);
+ if (iTab > 0)
+ {
+ // DeleteItem(iTab);
+ TabCtrl_DeleteItem(m_hWnd, iTab);
+ }
+
+ // Remove the ContainerInfo entry
+ std::vector<ContainerInfo>::iterator iter;
+ int iImage = -1;
+ for (iter = m_vContainerInfo.begin(); iter != m_vContainerInfo.end(); ++iter)
+ {
+ if (iter->pContainer == pWnd)
+ {
+ iImage = (*iter).iImage;
+ if (iImage >= 0)
+ TabCtrl_RemoveImage(m_hWnd, iImage);
+
+ m_vContainerInfo.erase(iter);
+ break;
+ }
+ }
+
+ // Set the parent container relationships
+ pWnd->GetViewPage().SetParent(pWnd);
+ pWnd->m_pContainerParent = pWnd;
+
+ // Display the first page
+ m_iCurrentPage = 0;
+ if (IsWindow())
+ SelectPage(0);
+ }
+
+ inline void CDockContainer::SelectPage(int nPage)
+ {
+ if (this != m_pContainerParent)
+ m_pContainerParent->SelectPage(nPage);
+ else
+ {
+ if ((nPage >= 0) && (nPage < (int)m_vContainerInfo.size() ))
+ {
+ if (GetCurSel() != nPage)
+ SetCurSel(nPage);
+
+ // Create the new container window if required
+ if (!m_vContainerInfo[nPage].pContainer->IsWindow())
+ {
+ CDockContainer* pContainer = m_vContainerInfo[nPage].pContainer;
+ pContainer->Create(GetParent());
+ pContainer->GetViewPage().SetParent(this);
+ }
+
+ // Determine the size of the tab page's view area
+ CRect rc = GetClientRect();
+ AdjustRect(FALSE, &rc);
+
+ // Swap the pages over
+ CDockContainer* pOldContainer = m_vContainerInfo[m_iCurrentPage].pContainer;
+ CDockContainer* pNewContainer = m_vContainerInfo[nPage].pContainer;
+ pOldContainer->GetViewPage().ShowWindow(SW_HIDE);
+ pNewContainer->GetViewPage().SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW);
+ pNewContainer->GetViewPage().GetView()->SetFocus();
+
+ // Adjust the docking caption
+ CDocker* pDock = (CDocker*)FromHandle(::GetParent(::GetParent(m_hWnd)));
+ if (dynamic_cast<CDocker*>(pDock))
+ {
+ pDock->SetCaption(pNewContainer->GetDockCaption().c_str());
+ pDock->RedrawWindow();
+ }
+
+ m_iCurrentPage = nPage;
+ }
+ }
+ }
+
+ inline void CDockContainer::SetActiveContainer(CDockContainer* pContainer)
+ {
+ int nPage = GetContainerIndex(pContainer);
+ assert (0 <= nPage);
+ SelectPage(nPage);
+ }
+
+ inline void CDockContainer::SetTabIcon(UINT nID_Icon)
+ {
+ HICON hIcon = (HICON)LoadImage(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(nID_Icon), IMAGE_ICON, 0, 0, LR_SHARED);
+ SetTabIcon(hIcon);
+ }
+
+ inline void CDockContainer::SetTabSize()
+ {
+ CRect rc = GetClientRect();
+ AdjustRect(FALSE, &rc);
+ if (rc.Width() < 0 )
+ rc.SetRectEmpty();
+
+ int nItemWidth = MIN(25 + GetMaxTabTextSize().cx, (rc.Width()-2)/(int)m_vContainerInfo.size());
+ int nItemHeight = MAX(20, GetTextHeight() + 5);
+ SendMessage(TCM_SETITEMSIZE, 0L, MAKELPARAM(nItemWidth, nItemHeight));
+ }
+
+ inline void CDockContainer::SetTabText(UINT nTab, LPCTSTR szText)
+ {
+ CDockContainer* pContainer = GetContainerParent()->GetContainerFromIndex(nTab);
+ pContainer->SetTabText(szText);
+
+ CTab::SetTabText(nTab, szText);
+ }
+
+ inline void CDockContainer::SetView(CWnd& Wnd)
+ {
+ GetViewPage().SetView(Wnd);
+ }
+
+ inline LRESULT CDockContainer::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_SIZE:
+ RecalcLayout();
+ return 0;
+
+ // The following are called in CTab::WndProcDefault
+ // case WM_LBUTTONDOWN:
+ // OnLButtonDown(wParam, lParam);
+ // break;
+ // case WM_LBUTTONUP:
+ // OnLButtonUp(wParam, lParam);
+ // break;
+ // case WM_MOUSELEAVE:
+ // OnMouseLeave(wParam, lParam);
+ // break;
+
+ case WM_SETFOCUS:
+ {
+ // Pass focus on to the current view
+ GetActiveView()->SetFocus();
+ }
+ break;
+ }
+
+ // pass unhandled messages on to CTab for processing
+ return CTab::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+ ///////////////////////////////////////////
+ // Declaration of the nested CViewPage class
+ inline BOOL CDockContainer::CViewPage::OnCommand(WPARAM wParam, LPARAM lParam)
+ {
+ CDockContainer* pContainer = (CDockContainer*)GetParent();
+ BOOL bResult = FALSE;
+ if (pContainer && pContainer->GetActiveContainer())
+ bResult = (BOOL)pContainer->GetActiveContainer()->SendMessage(WM_COMMAND, wParam, lParam);
+
+ return bResult;
+ }
+
+ inline void CDockContainer::CViewPage::OnCreate()
+ {
+ if (m_pView)
+ m_pView->Create(this);
+ }
+
+ inline LRESULT CDockContainer::CViewPage::OnNotify(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+
+ // Display tooltips for the toolbar
+ case TTN_GETDISPINFO:
+ {
+ int iIndex = GetToolBar().HitTest();
+ LPNMTTDISPINFO lpDispInfo = (LPNMTTDISPINFO)lParam;
+ if (iIndex >= 0)
+ {
+ int nID = GetToolBar().GetCommandID(iIndex);
+ if (nID > 0)
+ {
+ m_tsTooltip = LoadString(nID);
+ lpDispInfo->lpszText = (LPTSTR)m_tsTooltip.c_str();
+ }
+ else
+ m_tsTooltip = _T("");
+ }
+ }
+ break;
+ } // switch LPNMHDR
+
+ return 0L;
+ }
+
+ inline void CDockContainer::CViewPage::PreRegisterClass(WNDCLASS &wc)
+ {
+ wc.lpszClassName = _T("Win32++ TabPage");
+ wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+ }
+
+ inline void CDockContainer::CViewPage::RecalcLayout()
+ {
+ CRect rc = GetClientRect();
+ if (GetToolBar().IsWindow())
+ {
+ GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L);
+ CRect rcToolBar = m_ToolBar.GetClientRect();
+ rc.top += rcToolBar.Height();
+ }
+
+ GetView()->SetWindowPos(NULL, rc, SWP_SHOWWINDOW);
+ }
+
+ inline void CDockContainer::CViewPage::SetView(CWnd& wndView)
+ // Sets or changes the View window displayed within the frame
+ {
+ // Assign the view window
+ m_pView = &wndView;
+
+ if (m_hWnd)
+ {
+ if (!m_pView->IsWindow())
+ {
+ // The container is already created, so create and position the new view too
+ GetView()->Create(this);
+ }
+
+ RecalcLayout();
+ }
+ }
+
+ inline LRESULT CDockContainer::CViewPage::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_SIZE:
+ RecalcLayout();
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ // Send the focus change notifications to the grandparent
+ case NM_KILLFOCUS:
+ case NM_SETFOCUS:
+ case UWM_FRAMELOSTFOCUS:
+ ::SendMessage(::GetParent(::GetParent(m_hWnd)), WM_NOTIFY, wParam, lParam);
+ break;
+ }
+
+ break;
+ }
+
+ // pass unhandled messages on for default processing
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_DOCKING_H_
+
diff --git a/mmc_updater/depends/win32cpp/file.h b/mmc_updater/depends/win32cpp/file.h
new file mode 100644
index 00000000..4316dc94
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/file.h
@@ -0,0 +1,392 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+#ifndef _WIN32XX_FILE_H_
+#define _WIN32XX_FILE_H_
+
+
+#include "wincore.h"
+
+namespace Win32xx
+{
+
+ class CFile
+ {
+ public:
+ CFile();
+ CFile(HANDLE hFile);
+ CFile(LPCTSTR pszFileName, UINT nOpenFlags);
+ ~CFile();
+ operator HANDLE() const;
+
+ BOOL Close();
+ BOOL Flush();
+ HANDLE GetHandle() const;
+ ULONGLONG GetLength() const;
+ const CString& GetFileName() const;
+ const CString& GetFilePath() const;
+ const CString& GetFileTitle() const;
+ ULONGLONG GetPosition() const;
+ BOOL LockRange(ULONGLONG Pos, ULONGLONG Count);
+ BOOL Open(LPCTSTR pszFileName, UINT nOpenFlags);
+ CString OpenFileDialog(LPCTSTR pszFilePathName = NULL,
+ DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR pszFilter = NULL,
+ CWnd* pOwnerWnd = NULL);
+ UINT Read(void* pBuf, UINT nCount);
+ static BOOL Remove(LPCTSTR pszFileName);
+ static BOOL Rename(LPCTSTR pszOldName, LPCTSTR pszNewName);
+ CString SaveFileDialog(LPCTSTR pszFilePathName = NULL,
+ DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR pszFilter = NULL,
+ LPCTSTR pszDefExt = NULL, CWnd* pOwnerWnd = NULL);
+ ULONGLONG Seek(LONGLONG lOff, UINT nFrom);
+ void SeekToBegin();
+ ULONGLONG SeekToEnd();
+ void SetFilePath(LPCTSTR pszNewName);
+ BOOL SetLength(ULONGLONG NewLen);
+ BOOL UnlockRange(ULONGLONG Pos, ULONGLONG Count);
+ BOOL Write(const void* pBuf, UINT nCount);
+
+ private:
+ CFile(const CFile&); // Disable copy construction
+ CFile& operator = (const CFile&); // Disable assignment operator
+ CString m_FileName;
+ CString m_FilePath;
+ CString m_FileTitle;
+ HANDLE m_hFile;
+ };
+
+}
+
+
+
+namespace Win32xx
+{
+ inline CFile::CFile() : m_hFile(0)
+ {
+ }
+
+ inline CFile::CFile(HANDLE hFile) : m_hFile(hFile)
+ {
+ }
+
+ inline CFile::CFile(LPCTSTR pszFileName, UINT nOpenFlags) : m_hFile(0)
+ {
+ assert(pszFileName);
+ Open(pszFileName, nOpenFlags);
+ assert(m_hFile);
+ }
+
+ inline CFile::~CFile()
+ {
+ Close();
+ }
+
+ inline CFile::operator HANDLE() const
+ {
+ return m_hFile;
+ }
+
+ inline BOOL CFile::Close()
+ // Closes the file associated with this object. Closed file can no longer be read or written to.
+ {
+ BOOL bResult = TRUE;
+ if (m_hFile)
+ bResult = CloseHandle(m_hFile);
+
+ m_hFile = 0;
+ return bResult;
+ }
+
+ inline BOOL CFile::Flush()
+ // Causes any remaining data in the file buffer to be written to the file.
+ {
+ assert(m_hFile);
+ return FlushFileBuffers(m_hFile);
+ }
+
+ inline HANDLE CFile::GetHandle() const
+ {
+ return m_hFile;
+ }
+
+ inline ULONGLONG CFile::GetLength( ) const
+ // Returns the length of the file in bytes.
+ {
+ assert(m_hFile);
+
+ LONG High = 0;
+ DWORD LowPos = SetFilePointer(m_hFile, 0, &High, FILE_END);
+
+ ULONGLONG Result = ((ULONGLONG)High << 32) + LowPos;
+ return Result;
+ }
+
+ inline const CString& CFile::GetFileName() const
+ // Returns the filename of the file associated with this object.
+ {
+ return (const CString&)m_FileName;
+ }
+
+ inline const CString& CFile::GetFilePath() const
+ // Returns the full filename including the directory of the file associated with this object.
+ {
+ return (const CString&)m_FilePath;
+ }
+
+ inline const CString& CFile::GetFileTitle() const
+ // Returns the filename of the file associated with this object, excluding the path and the file extension
+ {
+ return (const CString&)m_FileTitle;
+ }
+
+ inline ULONGLONG CFile::GetPosition() const
+ // Returns the current value of the file pointer, which can be used in subsequent calls to Seek.
+ {
+ assert(m_hFile);
+ LONG High = 0;
+ DWORD LowPos = SetFilePointer(m_hFile, 0, &High, FILE_CURRENT);
+
+ ULONGLONG Result = ((ULONGLONG)High << 32) + LowPos;
+ return Result;
+ }
+
+ inline BOOL CFile::LockRange(ULONGLONG Pos, ULONGLONG Count)
+ // Locks a range of bytes in and open file.
+ {
+ assert(m_hFile);
+
+ DWORD dwPosHigh = (DWORD)(Pos >> 32);
+ DWORD dwPosLow = (DWORD)(Pos & 0xFFFFFFFF);
+ DWORD dwCountHigh = (DWORD)(Count >> 32);
+ DWORD dwCountLow = (DWORD)(Count & 0xFFFFFFFF);
+
+ return ::LockFile(m_hFile, dwPosLow, dwPosHigh, dwCountLow, dwCountHigh);
+ }
+
+ inline BOOL CFile::Open(LPCTSTR pszFileName, UINT nOpenFlags)
+ // Prepares a file to be written to or read from.
+ {
+ if (m_hFile) Close();
+
+ m_hFile = ::CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, nOpenFlags, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (INVALID_HANDLE_VALUE == m_hFile)
+ {
+ TRACE(_T("Failed\n"));
+ m_hFile = 0;
+ }
+
+ if (m_hFile)
+ {
+ SetFilePath(pszFileName);
+ }
+
+ return (m_hFile != 0);
+ }
+
+ inline CString CFile::OpenFileDialog(LPCTSTR pszFilePathName, DWORD dwFlags, LPCTSTR pszFilter, CWnd* pOwnerWnd)
+ // Displays the file open dialog.
+ // Returns a CString containing either the selected file name or an empty CString.
+ {
+ CString str;
+ if (pszFilePathName)
+ str = pszFilePathName;
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(OPENFILENAME);
+
+#if defined OPENFILENAME_SIZE_VERSION_400
+ if (GetWinVersion() < 2500)
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#endif
+
+ ofn.hwndOwner = pOwnerWnd? pOwnerWnd->GetHwnd() : NULL;
+ ofn.hInstance = GetApp()->GetInstanceHandle();
+ ofn.lpstrFilter = pszFilter;
+ ofn.lpstrTitle = _T("Open File");
+ ofn.Flags = dwFlags;
+ ofn.nMaxFile = _MAX_PATH;
+
+ ofn.lpstrFile = (LPTSTR)str.GetBuffer(_MAX_PATH);
+ ::GetOpenFileName(&ofn);
+ str.ReleaseBuffer();
+
+ return str;
+ }
+
+ inline UINT CFile::Read(void* pBuf, UINT nCount)
+ // Reads from the file, storing the contents in the specified buffer.
+ {
+ assert(m_hFile);
+ DWORD dwRead = 0;
+
+ if (!::ReadFile(m_hFile, pBuf, nCount, &dwRead, NULL))
+ dwRead = 0;
+
+ return dwRead;
+ }
+
+ inline BOOL CFile::Rename(LPCTSTR pszOldName, LPCTSTR pszNewName)
+ // Renames the specified file.
+ {
+ return ::MoveFile(pszOldName, pszNewName);
+ }
+
+ inline BOOL CFile::Remove(LPCTSTR pszFileName)
+ // Deletes the specified file.
+ {
+ return ::DeleteFile(pszFileName);
+ }
+
+ inline CString CFile::SaveFileDialog(LPCTSTR pszFilePathName, DWORD dwFlags, LPCTSTR pszFilter, LPCTSTR pszDefExt, CWnd* pOwnerWnd)
+ // Displays the SaveFileDialog.
+ // Returns a CString containing either the selected file name or an empty CString
+ {
+ CString str;
+ if (pszFilePathName)
+ str = pszFilePathName;
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(OPENFILENAME);
+
+#if defined OPENFILENAME_SIZE_VERSION_400
+ if (GetWinVersion() < 2500)
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#endif
+
+ ofn.hwndOwner = pOwnerWnd? pOwnerWnd->GetHwnd() : NULL;
+ ofn.hInstance = GetApp()->GetInstanceHandle();
+ ofn.lpstrFilter = pszFilter;
+ ofn.lpstrFile = (LPTSTR)pszFilePathName;
+ ofn.lpstrFileTitle = (LPTSTR)pszFilePathName;
+ ofn.lpstrDefExt = pszDefExt;
+ ofn.nMaxFile = lstrlen(pszFilePathName);
+ ofn.lpstrTitle = _T("Save File");
+ ofn.Flags = dwFlags;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.lpstrFile = (LPTSTR)str.GetBuffer(_MAX_PATH);
+ ::GetSaveFileName(&ofn);
+ str.ReleaseBuffer();
+
+ return str;
+ }
+
+ inline ULONGLONG CFile::Seek(LONGLONG lOff, UINT nFrom)
+ // Positions the current file pointer.
+ // Permitted values for nFrom are: FILE_BEGIN, FILE_CURRENT, or FILE_END.
+ {
+ assert(m_hFile);
+ assert(nFrom == FILE_BEGIN || nFrom == FILE_CURRENT || nFrom == FILE_END);
+
+ LONG High = LONG(lOff >> 32);
+ LONG Low = (LONG)(lOff & 0xFFFFFFFF);
+
+ DWORD LowPos = SetFilePointer(m_hFile, Low, &High, nFrom);
+
+ ULONGLONG Result = ((ULONGLONG)High << 32) + LowPos;
+ return Result;
+ }
+
+ inline void CFile::SeekToBegin()
+ // Sets the current file pointer to the beginning of the file.
+ {
+ assert(m_hFile);
+ Seek(0, FILE_BEGIN);
+ }
+
+ inline ULONGLONG CFile::SeekToEnd()
+ // Sets the current file pointer to the end of the file.
+ {
+ assert(m_hFile);
+ return Seek(0, FILE_END);
+ }
+
+ inline void CFile::SetFilePath(LPCTSTR pszFileName)
+ // Specifies the full file name, including its path
+ {
+ TCHAR* pFileName = NULL;
+ int nBuffSize = ::GetFullPathName(pszFileName, 0, 0, 0);
+ if (nBuffSize > 0)
+ {
+ TCHAR* pBuff = m_FilePath.GetBuffer(nBuffSize);
+ ::GetFullPathName(pszFileName, nBuffSize, pBuff, &pFileName);
+ m_FilePath.ReleaseBuffer();
+ m_FileName = pFileName;
+ int nPos = m_FileName.ReverseFind(_T("."));
+ if (nPos >= 0)
+ m_FileTitle = m_FileName.Left(nPos);
+ }
+ }
+
+ inline BOOL CFile::SetLength(ULONGLONG NewLen)
+ // Changes the length of the file to the specified value.
+ {
+ assert(m_hFile);
+
+ Seek(NewLen, FILE_BEGIN);
+ return ::SetEndOfFile(m_hFile);
+ }
+
+ inline BOOL CFile::UnlockRange(ULONGLONG Pos, ULONGLONG Count)
+ // Unlocks a range of bytes in an open file.
+ {
+ assert(m_hFile);
+
+ DWORD dwPosHigh = (DWORD)(Pos >> 32);
+ DWORD dwPosLow = (DWORD)(Pos & 0xFFFFFFFF);
+ DWORD dwCountHigh = (DWORD)(Count >> 32);
+ DWORD dwCountLow = (DWORD)(Count & 0xFFFFFFFF);
+
+ return ::UnlockFile(m_hFile, dwPosLow, dwPosHigh, dwCountLow, dwCountHigh);
+ }
+
+ inline BOOL CFile::Write(const void* pBuf, UINT nCount)
+ // Writes the specified buffer to the file.
+ {
+ assert(m_hFile);
+ DWORD dwWritten = 0;
+ BOOL bResult = ::WriteFile(m_hFile, pBuf, nCount, &dwWritten, NULL);
+ if (dwWritten != nCount)
+ bResult = FALSE;
+
+ return bResult;
+ }
+
+
+} // namespace Win32xx
+
+#endif
diff --git a/mmc_updater/depends/win32cpp/frame.h b/mmc_updater/depends/win32cpp/frame.h
new file mode 100644
index 00000000..1f439fd7
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/frame.h
@@ -0,0 +1,3303 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// frame.h
+// Declaration of the CFrame and CMenuBar classes
+
+// The classes declared in this file support SDI (Single Document Interface)
+// frames on Win32/Win64 operating systems (not Windows CE). For Windows CE,
+// use wceframe.h instead. SDI frames are a simple frame which supports a
+// single view window. Refer to mdi.h for frames that support several
+// child windows.
+
+// CFrame also includes each of the following classes as members:
+// * CReBar for managing the frame's rebar control.
+// * CMenuBar for managing the menu inside the rebar.
+// * CToolBar for managing the frame's toolbar.
+// * CStatusBar for managing the frame's status bar.
+// In each case these members are exposed by a GetXXX function, allowing
+// them to be accessed or sent messages.
+
+// CFrame is responsible for creating a "frame" window. This window has a
+// menu and and several child windows, including a toolbar (usualy hosted
+// within a rebar), a status bar, and a view positioned over the frame
+// window's non-client area. The "view" window is a seperate CWnd object
+// assigned to the frame with the SetView function.
+
+// When compiling an application with these classes, it will need to be linked
+// with Comctl32.lib.
+
+// To create a SDI frame application, inherit a CMainFrame class from CFrame.
+// Use the Frame sample application as the starting point for your own frame
+// applications.
+// Refer to the Notepad and Scribble samples for examples on how to use these
+// classes to create a frame application.
+
+
+#ifndef _WIN32XX_FRAME_H_
+#define _WIN32XX_FRAME_H_
+
+#include "wincore.h"
+#include "dialog.h"
+#include "gdi.h"
+#include "statusbar.h"
+#include "toolbar.h"
+#include "rebar.h"
+#include "default_resource.h"
+
+#ifndef RBN_MINMAX
+ #define RBN_MINMAX (RBN_FIRST - 21)
+#endif
+
+
+namespace Win32xx
+{
+
+ ////////////////////////////////////////////////
+ // Declarations for structures for themes
+ //
+ struct MenuTheme
+ {
+ BOOL UseThemes; // TRUE if themes are used
+ COLORREF clrHot1; // Colour 1 for top menu. Color of selected menu item
+ COLORREF clrHot2; // Colour 2 for top menu. Color of checkbox
+ COLORREF clrPressed1; // Colour 1 for pressed top menu and side bar
+ COLORREF clrPressed2; // Colour 2 for pressed top menu and side bar
+ COLORREF clrOutline; // Colour for border outline
+ };
+
+
+ // Forward declaration of CFrame. Its defined later.
+ class CFrame;
+
+
+ ////////////////////////////////////
+ // Declaration of the CMenuBar class
+ //
+ class CMenuBar : public CToolBar
+ {
+ friend class CFrame;
+
+ public:
+ CMenuBar();
+ virtual ~CMenuBar();
+ virtual void SetMenu(HMENU hMenu);
+ virtual void SetMenuBarTheme(MenuTheme& Theme);
+
+ HMENU GetMenu() const {return m_hTopMenu;}
+ MenuTheme& GetMenuBarTheme() {return m_ThemeMenu;}
+
+ protected:
+ //Overridables
+ virtual void OnCreate();
+ virtual LRESULT OnCustomDraw(NMHDR* pNMHDR);
+ virtual void OnKeyDown(WPARAM wParam, LPARAM lParam);
+ virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam);
+ virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam);
+ virtual void OnMenuChar(WPARAM wParam, LPARAM lParam);
+ virtual BOOL OnMenuInput(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual void OnMouseLeave();
+ virtual void OnMouseMove(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam);
+ virtual void OnSysCommand(WPARAM wParam, LPARAM lParam);
+ virtual void OnWindowPosChanged();
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CMenuBar(const CMenuBar&); // Disable copy construction
+ CMenuBar& operator = (const CMenuBar&); // Disable assignment operator
+ void DoAltKey(WORD KeyCode);
+ void DoPopupMenu();
+ void DrawAllMDIButtons(CDC& DrawDC);
+ void DrawMDIButton(CDC& DrawDC, int iButton, UINT uState);
+ void ExitMenu();
+ HWND GetActiveMDIChild();
+ void GrabFocus();
+ BOOL IsMDIChildMaxed() const;
+ BOOL IsMDIFrame() const;
+ void ReleaseFocus();
+ void SetHotItem(int nHot);
+ static LRESULT CALLBACK StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam);
+
+ enum MDIButtonType
+ {
+ MDI_MIN = 0,
+ MDI_RESTORE = 1,
+ MDI_CLOSE = 2,
+ };
+
+ BOOL m_bExitAfter; // Exit after Popup menu ends
+ BOOL m_bKeyMode; // keyboard navigation mode
+ BOOL m_bMenuActive; // popup menu active
+ BOOL m_bSelPopup; // a popup (cascade) menu is selected
+ HMENU m_hPopupMenu; // handle to the popup menu
+ HMENU m_hSelMenu; // handle to the casceded popup menu
+ HMENU m_hTopMenu; // handle to the top level menu
+ HWND m_hPrevFocus; // handle to window which had focus
+ CRect m_MDIRect[3]; // array of CRect for MDI buttons
+ int m_nHotItem; // hot item
+ int m_nMDIButton; // the MDI button (MDIButtonType) pressed
+ CPoint m_OldMousePos; // old Mouse position
+ MenuTheme m_ThemeMenu; // Theme structure
+ CFrame* m_pFrame; // Pointer to the frame
+
+ }; // class CMenuBar
+
+
+
+ //////////////////////////////////
+ // Declaration of the CFrame class
+ //
+ class CFrame : public CWnd
+ {
+ friend class CMenuBar;
+
+ struct ItemData
+ // Each Dropdown menu item has this data
+ {
+ HMENU hMenu;
+ UINT nPos;
+ UINT fType;
+ std::vector<TCHAR> vItemText;
+ HMENU hSubMenu;
+
+ ItemData() : hMenu(0), nPos(0), fType(0), hSubMenu(0) { vItemText.assign(MAX_MENU_STRING, _T('\0')); }
+ LPTSTR GetItemText() {return &vItemText[0];}
+ };
+
+ typedef Shared_Ptr<ItemData> ItemDataPtr;
+
+ public:
+ CFrame();
+ virtual ~CFrame();
+
+ // Override these functions as required
+ virtual void AdjustFrameRect(RECT rcView) const;
+ virtual CRect GetViewRect() const;
+ virtual BOOL IsMDIFrame() const { return FALSE; }
+ virtual void SetStatusIndicators();
+ virtual void SetStatusText();
+ virtual void RecalcLayout();
+ virtual MenuTheme& GetMenuTheme() const { return (MenuTheme&) m_ThemeMenu; }
+ virtual ReBarTheme& GetReBarTheme() const { return (ReBarTheme&)GetReBar().GetReBarTheme(); }
+ virtual ToolBarTheme& GetToolBarTheme() const { return (ToolBarTheme&)GetToolBar().GetToolBarTheme(); }
+
+ // Virtual Attributes
+ // If you need to modify the default behaviour of the menubar, rebar,
+ // statusbar or toolbar, inherit from those classes, and override
+ // the following attribute functions.
+ virtual CMenuBar& GetMenuBar() const { return (CMenuBar&)m_MenuBar; }
+ virtual CReBar& GetReBar() const { return (CReBar&)m_ReBar; }
+ virtual CStatusBar& GetStatusBar() const { return (CStatusBar&)m_StatusBar; }
+ virtual CToolBar& GetToolBar() const { return (CToolBar&)m_ToolBar; }
+
+ // These functions aren't virtual, and shouldn't be overridden
+ HACCEL GetFrameAccel() const { return m_hAccel; }
+ CMenu& GetFrameMenu() const { return (CMenu&)m_Menu; }
+ std::vector<tString> GetMRUEntries() const { return m_vMRUEntries; }
+ tString GetRegistryKeyName() const { return m_tsKeyName; }
+ CWnd* GetView() const { return m_pView; }
+ tString GetMRUEntry(UINT nIndex);
+ void SetFrameMenu(INT ID_MENU);
+ void SetFrameMenu(HMENU hMenu);
+ void SetMenuTheme(MenuTheme& Theme);
+ void SetView(CWnd& wndView);
+ BOOL IsMenuBarUsed() const { return (GetMenuBar() != 0); }
+ BOOL IsReBarSupported() const { return (GetComCtlVersion() >= 470); }
+ BOOL IsReBarUsed() const { return (GetReBar() != 0); }
+
+ protected:
+ // Override these functions as required
+ virtual BOOL AddMenuIcon(int nID_MenuItem, HICON hIcon, int cx = 16, int cy = 16);
+ virtual UINT AddMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID);
+ virtual void AddMenuBarBand();
+ virtual void AddMRUEntry(LPCTSTR szMRUEntry);
+ virtual void AddToolBarBand(CToolBar& TB, DWORD dwStyle, UINT nID);
+ virtual void AddToolBarButton(UINT nID, BOOL bEnabled = TRUE, LPCTSTR szText = 0);
+ virtual void CreateToolBar();
+ virtual void DrawCheckmark(LPDRAWITEMSTRUCT pdis, CDC& DrawDC);
+ virtual void DrawMenuIcon(LPDRAWITEMSTRUCT pdis, CDC& DrawDC, BOOL bDisabled);
+ virtual void DrawMenuText(CDC& DrawDC, LPCTSTR ItemText, CRect& rc, COLORREF colorText);
+ virtual int GetMenuItemPos(HMENU hMenu, LPCTSTR szItem);
+ virtual BOOL LoadRegistrySettings(LPCTSTR szKeyName);
+ virtual BOOL LoadRegistryMRUSettings(UINT nMaxMRU = 0);
+ virtual void OnActivate(WPARAM wParam, LPARAM lParam);
+ virtual void OnClose();
+ virtual void OnCreate();
+ virtual void OnDestroy();
+ virtual LRESULT OnDrawItem(WPARAM wParam, LPARAM lParam);
+ virtual void OnExitMenuLoop();
+ virtual void OnHelp();
+ virtual void OnInitMenuPopup(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnMeasureItem(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnMenuChar(WPARAM wParam, LPARAM lParam);
+ virtual void OnMenuSelect(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam);
+ virtual void OnSetFocus();
+ virtual void OnSysColorChange();
+ virtual LRESULT OnSysCommand(WPARAM wParam, LPARAM lParam);
+ virtual void OnTimer(WPARAM wParam);
+ virtual void OnViewStatusBar();
+ virtual void OnViewToolBar();
+ virtual void PreCreate(CREATESTRUCT& cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual void RemoveMRUEntry(LPCTSTR szMRUEntry);
+ virtual BOOL SaveRegistrySettings();
+ virtual void SetMenuBarBandSize();
+ virtual UINT SetMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID);
+ virtual void SetupToolBar();
+ virtual void SetTheme();
+ virtual void SetToolBarImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID);
+ virtual void ShowMenu(BOOL bShow);
+ virtual void ShowStatusBar(BOOL bShow);
+ virtual void ShowToolBar(BOOL bShow);
+ virtual void UpdateMRUMenu();
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ enum Constants
+ {
+ ID_STATUS_TIMER = 1,
+ POST_TEXT_GAP = 16, // for owner draw menu item
+ };
+
+ tString m_tsStatusText; // TCHAR std::string for status text
+ BOOL m_bShowIndicatorStatus; // set to TRUE to see indicators in status bar
+ BOOL m_bShowMenuStatus; // set to TRUE to see menu and toolbar updates in status bar
+ BOOL m_bUseReBar; // set to TRUE if ReBars are to be used
+ BOOL m_bUseThemes; // set to TRUE if themes are to be used
+ BOOL m_bUpdateTheme; // set to TRUE to run SetThemes when theme changes
+ BOOL m_bUseToolBar; // set to TRUE if the toolbar is used
+ BOOL m_bUseCustomDraw; // set to TRUE to perform custom drawing on menu items
+ BOOL m_bShowStatusBar; // A flag to indicate if the StatusBar should be displayed
+ BOOL m_bShowToolBar; // A flag to indicate if the ToolBar should be displayed
+ MenuTheme m_ThemeMenu; // Theme structure for popup menus
+ HIMAGELIST m_himlMenu; // Imagelist of menu icons
+ HIMAGELIST m_himlMenuDis; // Imagelist of disabled menu icons
+
+ private:
+ CFrame(const CFrame&); // Disable copy construction
+ CFrame& operator = (const CFrame&); // Disable assignment operator
+ void LoadCommonControls();
+
+ std::vector<ItemDataPtr> m_vMenuItemData;// vector of ItemData pointers
+ std::vector<UINT> m_vMenuIcons; // vector of menu icon resource IDs
+ std::vector<tString> m_vMRUEntries; // Vector of tStrings for MRU entires
+ CDialog m_AboutDialog; // Help about dialog
+ CMenuBar m_MenuBar; // CMenuBar object
+ CReBar m_ReBar; // CReBar object
+ CStatusBar m_StatusBar; // CStatusBar object
+ CToolBar m_ToolBar; // CToolBar object
+ CMenu m_Menu; // handle to the frame menu
+ HACCEL m_hAccel; // handle to the frame's accelerator table
+ CWnd* m_pView; // pointer to the View CWnd object
+ LPCTSTR m_OldStatus[3]; // Array of TCHAR pointers;
+ tString m_tsKeyName; // TCHAR std::string for Registry key name
+ tString m_tsTooltip; // TCHAR std::string for tool tips
+ UINT m_nMaxMRU; // maximum number of MRU entries
+ CRect m_rcPosition; // CRect of the starting window position
+ HWND m_hOldFocus; // The window which had focus prior to the app'a deactivation
+ int m_nOldID; // The previous ToolBar ID displayed in the statusbar
+
+ }; // class CFrame
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ /////////////////////////////////////
+ // Definitions for the CMenuBar class
+ //
+ inline CMenuBar::CMenuBar()
+ {
+ m_bExitAfter = FALSE;
+ m_hTopMenu = NULL;
+ m_nHotItem = -1;
+ m_bSelPopup = FALSE;
+ m_hSelMenu = NULL;
+ m_bMenuActive = FALSE;
+ m_bKeyMode = FALSE;
+ m_hPrevFocus = NULL;
+ m_nMDIButton = 0;
+ m_hPopupMenu = 0;
+
+ ZeroMemory(&m_ThemeMenu, sizeof(MenuTheme));
+ }
+
+ inline CMenuBar::~CMenuBar()
+ {
+ }
+
+ inline void CMenuBar::DoAltKey(WORD KeyCode)
+ {
+ //Handle key pressed with Alt held down
+ UINT ID;
+ if (SendMessage(TB_MAPACCELERATOR, KeyCode, (LPARAM) &ID))
+ {
+ GrabFocus();
+ m_bKeyMode = TRUE;
+ SetHotItem(ID);
+ m_bMenuActive = TRUE;
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ }
+ else
+ ::MessageBeep(MB_OK);
+ }
+
+ inline void CMenuBar::DoPopupMenu()
+ {
+ if (m_bKeyMode)
+ // Simulate a down arrow key press
+ PostMessage(WM_KEYDOWN, VK_DOWN, 0L);
+
+ m_bKeyMode = FALSE;
+ m_bExitAfter = FALSE;
+ m_OldMousePos = GetCursorPos();
+
+ HWND hMaxMDIChild = NULL;
+ if (IsMDIChildMaxed())
+ hMaxMDIChild = GetActiveMDIChild();
+
+ // Load the submenu
+ int nMaxedOffset = IsMDIChildMaxed()? 1:0;
+ m_hPopupMenu = ::GetSubMenu(m_hTopMenu, m_nHotItem - nMaxedOffset);
+ if (IsMDIChildMaxed() && (0 == m_nHotItem) )
+ m_hPopupMenu = ::GetSystemMenu(hMaxMDIChild, FALSE);
+
+ // Retrieve the bounding rectangle for the toolbar button
+ CRect rc = GetItemRect(m_nHotItem);
+
+ // convert rectangle to desktop coordinates
+ ClientToScreen(rc);
+
+ // Position popup above toolbar if it won't fit below
+ TPMPARAMS tpm;
+ tpm.cbSize = sizeof(TPMPARAMS);
+ tpm.rcExclude = rc;
+
+ // Set the hot button
+ SendMessage(TB_SETHOTITEM, m_nHotItem, 0L);
+ SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(TRUE, 0));
+
+ m_bSelPopup = FALSE;
+ m_hSelMenu = NULL;
+ m_bMenuActive = TRUE;
+
+ // We hook mouse input to process mouse and keyboard input during
+ // the popup menu. Messages are sent to StaticMsgHook.
+
+ // Remove any remaining hook first
+ TLSData* pTLSData = (TLSData*)::TlsGetValue(GetApp()->GetTlsIndex());
+ pTLSData->pMenuBar = this;
+ if (pTLSData->hHook != NULL)
+ ::UnhookWindowsHookEx(pTLSData->hHook);
+
+ // Hook messages about to be processed by the shortcut menu
+ pTLSData->hHook = ::SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC)StaticMsgHook, NULL, ::GetCurrentThreadId());
+
+ // Display the shortcut menu
+ BOOL bRightToLeft = FALSE;
+
+#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500)
+ bRightToLeft = ((GetAncestor()->GetWindowLongPtr(GWL_EXSTYLE)) & WS_EX_LAYOUTRTL);
+#endif
+
+ int xPos = bRightToLeft? rc.right : rc.left;
+ UINT nID = ::TrackPopupMenuEx(m_hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
+ xPos, rc.bottom, m_hWnd, &tpm);
+
+ // We get here once the TrackPopupMenuEx has ended
+ m_bMenuActive = FALSE;
+
+ // Remove the message hook
+ ::UnhookWindowsHookEx(pTLSData->hHook);
+ pTLSData->hHook = NULL;
+
+ // Process MDI Child system menu
+ if (IsMDIChildMaxed())
+ {
+ if (::GetSystemMenu(hMaxMDIChild, FALSE) == m_hPopupMenu )
+ {
+ if (nID)
+ ::SendMessage(hMaxMDIChild, WM_SYSCOMMAND, nID, 0L);
+ }
+ }
+
+ // Resestablish Focus
+ if (m_bKeyMode)
+ GrabFocus();
+ }
+
+ inline void CMenuBar::DrawAllMDIButtons(CDC& DrawDC)
+ {
+ if (!IsMDIFrame())
+ return;
+
+ if (IsMDIChildMaxed())
+ {
+ int cx = GetSystemMetrics(SM_CXSMICON);
+ int cy = GetSystemMetrics(SM_CYSMICON);
+ CRect rc = GetClientRect();
+ int gap = 4;
+ rc.right -= gap;
+
+ // Assign values to each element of the CRect array
+ for (int i = 0 ; i < 3 ; ++i)
+ {
+ int left = rc.right - (i+1)*cx - gap*(i+1);
+ int top = rc.bottom/2 - cy/2;
+ int right = rc.right - i*cx - gap*(i+1);
+ int bottom = rc.bottom/2 + cy/2;
+ ::SetRect(&m_MDIRect[2 - i], left, top, right, bottom);
+ }
+
+ // Hide the MDI button if it won't fit
+ for (int k = 0 ; k <= 2 ; ++k)
+ {
+
+ if (m_MDIRect[k].left < GetMaxSize().cx)
+ {
+ ::SetRectEmpty(&m_MDIRect[k]);
+ }
+ }
+
+ DrawMDIButton(DrawDC, MDI_MIN, 0);
+ DrawMDIButton(DrawDC, MDI_RESTORE, 0);
+ DrawMDIButton(DrawDC, MDI_CLOSE, 0);
+ }
+ }
+
+ inline void CMenuBar::DrawMDIButton(CDC& DrawDC, int iButton, UINT uState)
+ {
+ if (!IsRectEmpty(&m_MDIRect[iButton]))
+ {
+ switch (uState)
+ {
+ case 0:
+ {
+ // Draw a grey outline
+ DrawDC.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE));
+ DrawDC.MoveTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom);
+ DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].bottom);
+ DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].top);
+ DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].top);
+ DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom);
+ }
+ break;
+ case 1:
+ {
+ // Draw outline, white at top, black on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.MoveTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom);
+ DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].bottom);
+ DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].top);
+ DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom);
+ }
+
+ break;
+ case 2:
+ {
+ // Draw outline, black on top, white on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.MoveTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom);
+ DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].bottom);
+ DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].top);
+ DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom);
+ }
+ break;
+ }
+
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+
+ switch (iButton)
+ {
+ case MDI_MIN:
+ // Manually Draw Minimise button
+ DrawDC.MoveTo(m_MDIRect[0].left + 4, m_MDIRect[0].bottom -4);
+ DrawDC.LineTo(m_MDIRect[0].right - 4, m_MDIRect[0].bottom - 4);
+
+ DrawDC.MoveTo(m_MDIRect[0].left + 4, m_MDIRect[0].bottom -5);
+ DrawDC.LineTo(m_MDIRect[0].right - 4, m_MDIRect[0].bottom - 5);
+ break;
+ case MDI_RESTORE:
+ // Manually Draw Restore Button
+ DrawDC.MoveTo(m_MDIRect[1].left + 3, m_MDIRect[1].top + 7);
+ DrawDC.LineTo(m_MDIRect[1].left + 3, m_MDIRect[1].bottom -4);
+ DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].bottom -4);
+ DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].top + 7);
+ DrawDC.LineTo(m_MDIRect[1].left + 3, m_MDIRect[1].top + 7);
+
+ DrawDC.MoveTo(m_MDIRect[1].left + 3, m_MDIRect[1].top + 8);
+ DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].top + 8);
+
+ DrawDC.MoveTo(m_MDIRect[1].left + 5, m_MDIRect[1].top + 7);
+ DrawDC.LineTo(m_MDIRect[1].left + 5, m_MDIRect[1].top + 4);
+ DrawDC.LineTo(m_MDIRect[1].right - 4, m_MDIRect[1].top + 4);
+ DrawDC.LineTo(m_MDIRect[1].right - 4, m_MDIRect[1].bottom -6);
+ DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].bottom -6);
+
+ DrawDC.MoveTo(m_MDIRect[1].left + 5, m_MDIRect[1].top + 5);
+ DrawDC.LineTo(m_MDIRect[1].right - 4, m_MDIRect[1].top + 5);
+ break;
+ case MDI_CLOSE:
+ // Manually Draw Close Button
+ DrawDC.MoveTo(m_MDIRect[2].left + 4, m_MDIRect[2].top +5);
+ DrawDC.LineTo(m_MDIRect[2].right - 4, m_MDIRect[2].bottom -3);
+
+ DrawDC.MoveTo(m_MDIRect[2].left + 5, m_MDIRect[2].top +5);
+ DrawDC.LineTo(m_MDIRect[2].right - 4, m_MDIRect[2].bottom -4);
+
+ DrawDC.MoveTo(m_MDIRect[2].left + 4, m_MDIRect[2].top +6);
+ DrawDC.LineTo(m_MDIRect[2].right - 5, m_MDIRect[2].bottom -3);
+
+ DrawDC.MoveTo(m_MDIRect[2].right -5, m_MDIRect[2].top +5);
+ DrawDC.LineTo(m_MDIRect[2].left + 3, m_MDIRect[2].bottom -3);
+
+ DrawDC.MoveTo(m_MDIRect[2].right -5, m_MDIRect[2].top +6);
+ DrawDC.LineTo(m_MDIRect[2].left + 4, m_MDIRect[2].bottom -3);
+
+ DrawDC.MoveTo(m_MDIRect[2].right -6, m_MDIRect[2].top +5);
+ DrawDC.LineTo(m_MDIRect[2].left + 3, m_MDIRect[2].bottom -4);
+ break;
+ }
+ }
+ }
+
+ inline void CMenuBar::ExitMenu()
+ {
+ ReleaseFocus();
+ m_bKeyMode = FALSE;
+ m_bMenuActive = FALSE;
+ SendMessage(TB_PRESSBUTTON, m_nHotItem, (LPARAM) MAKELONG (FALSE, 0));
+ SetHotItem(-1);
+
+ CPoint pt = GetCursorPos();
+ ScreenToClient(pt);
+
+ // Update mouse mouse position for hot tracking
+ SendMessage(WM_MOUSEMOVE, 0L, MAKELONG(pt.x, pt.y));
+ }
+
+ inline HWND CMenuBar::GetActiveMDIChild()
+ {
+ HWND hwndMDIChild = NULL;
+ if (IsMDIFrame())
+ {
+ hwndMDIChild = (HWND)::SendMessage(m_pFrame->GetView()->GetHwnd(), WM_MDIGETACTIVE, 0L, 0L);
+ }
+
+ return hwndMDIChild;
+ }
+
+ inline void CMenuBar::GrabFocus()
+ {
+ if (::GetFocus() != m_hWnd)
+ m_hPrevFocus = ::SetFocus(m_hWnd);
+ ::SetCapture(m_hWnd);
+ ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
+ }
+
+ inline BOOL CMenuBar::IsMDIChildMaxed() const
+ {
+ BOOL bMaxed = FALSE;
+
+ if (IsMDIFrame() && m_pFrame->GetView()->IsWindow())
+ {
+ m_pFrame->GetView()->SendMessage(WM_MDIGETACTIVE, 0L, (LPARAM)&bMaxed);
+ }
+
+ return bMaxed;
+ }
+
+ inline BOOL CMenuBar::IsMDIFrame() const
+ {
+ return (m_pFrame->IsMDIFrame());
+ }
+
+ inline void CMenuBar::OnMenuChar(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(lParam);
+
+ if (!m_bMenuActive)
+ DoAltKey(LOWORD(wParam));
+ }
+
+ inline void CMenuBar::OnCreate()
+ {
+ // We must send this message before sending the TB_ADDBITMAP or TB_ADDBUTTONS message
+ SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0L);
+
+ m_pFrame = (CFrame*)GetAncestor();
+ assert(m_pFrame);
+ }
+
+ inline LRESULT CMenuBar::OnCustomDraw(NMHDR* pNMHDR)
+ // CustomDraw is used to render the MenuBar's toolbar buttons
+ {
+ if (m_ThemeMenu.UseThemes)
+ {
+ LPNMTBCUSTOMDRAW lpNMCustomDraw = (LPNMTBCUSTOMDRAW)pNMHDR;
+
+ switch (lpNMCustomDraw->nmcd.dwDrawStage)
+ {
+ // Begin paint cycle
+ case CDDS_PREPAINT:
+ // Send NM_CUSTOMDRAW item draw, and post-paint notification messages.
+ return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT ;
+
+ // An item is about to be drawn
+ case CDDS_ITEMPREPAINT:
+ {
+ CDC* pDrawDC = FromHandle(lpNMCustomDraw->nmcd.hdc);
+ CRect rcRect = lpNMCustomDraw->nmcd.rc;
+ int nState = lpNMCustomDraw->nmcd.uItemState;
+ DWORD dwItem = (DWORD)lpNMCustomDraw->nmcd.dwItemSpec;
+
+ // Leave a pixel gap above and below the drawn rectangle
+ if (IsAeroThemed())
+ rcRect.InflateRect(0, -2);
+ else
+ rcRect.InflateRect(0, -1);
+
+ if (IsMDIChildMaxed() && (0 == dwItem))
+ // Draw over MDI Max button
+ {
+ HICON hIcon = (HICON)::SendMessage(GetActiveMDIChild(), WM_GETICON, ICON_SMALL, 0L);
+ if (NULL == hIcon)
+ hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
+
+ int cx = ::GetSystemMetrics (SM_CXSMICON);
+ int cy = ::GetSystemMetrics (SM_CYSMICON);
+ int y = 1 + (GetWindowRect().Height() - cy)/2;
+ int x = (rcRect.Width() - cx)/2;
+ pDrawDC->DrawIconEx(x, y, hIcon, cx, cy, 0, NULL, DI_NORMAL);
+
+ pDrawDC->Detach(); // Optional, deletes GDI objects sooner
+ return CDRF_SKIPDEFAULT; // No further drawing
+ }
+
+ else if (nState & (CDIS_HOT | CDIS_SELECTED))
+ {
+ if ((nState & CDIS_SELECTED) || (GetButtonState(dwItem) & TBSTATE_PRESSED))
+ {
+ pDrawDC->GradientFill(m_ThemeMenu.clrPressed1, m_ThemeMenu.clrPressed2, rcRect, FALSE);
+ }
+ else if (nState & CDIS_HOT)
+ {
+ pDrawDC->GradientFill(m_ThemeMenu.clrHot1, m_ThemeMenu.clrHot2, rcRect, FALSE);
+ }
+
+ // Draw border
+ pDrawDC->CreatePen(PS_SOLID, 1, m_ThemeMenu.clrOutline);
+ pDrawDC->MoveTo(rcRect.left, rcRect.bottom);
+ pDrawDC->LineTo(rcRect.left, rcRect.top);
+ pDrawDC->LineTo(rcRect.right-1, rcRect.top);
+ pDrawDC->LineTo(rcRect.right-1, rcRect.bottom);
+ pDrawDC->MoveTo(rcRect.right-1, rcRect.bottom);
+ pDrawDC->LineTo(rcRect.left, rcRect.bottom);
+
+ TCHAR str[80] = _T("");
+ int nLength = (int)SendMessage(TB_GETBUTTONTEXT, lpNMCustomDraw->nmcd.dwItemSpec, 0L);
+ if ((nLength > 0) && (nLength < 80))
+ SendMessage(TB_GETBUTTONTEXT, lpNMCustomDraw->nmcd.dwItemSpec, (LPARAM)str);
+
+ // Draw highlight text
+ pDrawDC->SelectObject(GetFont());
+ rcRect.bottom += 1;
+ int iMode = pDrawDC->SetBkMode(TRANSPARENT);
+ pDrawDC->DrawText(str, lstrlen(str), rcRect, DT_VCENTER | DT_CENTER | DT_SINGLELINE);
+
+ pDrawDC->SetBkMode(iMode);
+ pDrawDC->Detach(); // Optional, deletes GDI objects sooner
+ return CDRF_SKIPDEFAULT; // No further drawing
+ }
+ }
+ return CDRF_DODEFAULT ; // Do default drawing
+
+ // Painting cycle has completed
+ case CDDS_POSTPAINT:
+ // Draw MDI Minimise, Restore and Close buttons
+ {
+ CDC* pDrawDC = FromHandle(lpNMCustomDraw->nmcd.hdc);
+ DrawAllMDIButtons(*pDrawDC);
+ pDrawDC->Detach(); // Optional, deletes GDI objects sooner
+ }
+ break;
+ }
+ }
+ return 0L;
+ }
+
+ inline void CMenuBar::OnKeyDown(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(lParam);
+
+ switch (wParam)
+ {
+ case VK_ESCAPE:
+ ExitMenu();
+ break;
+
+ case VK_SPACE:
+ ExitMenu();
+ // Bring up the system menu
+ GetAncestor()->PostMessage(WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE);
+ break;
+
+ // Handle VK_DOWN,VK_UP and VK_RETURN together
+ case VK_DOWN:
+ case VK_UP:
+ case VK_RETURN:
+ // Always use PostMessage for USER_POPUPMENU (not SendMessage)
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ break;
+
+ case VK_LEFT:
+ // Move left to next topmenu item
+ (m_nHotItem > 0)? SetHotItem(m_nHotItem -1) : SetHotItem(GetButtonCount()-1);
+ break;
+
+ case VK_RIGHT:
+ // Move right to next topmenu item
+ (m_nHotItem < GetButtonCount() -1)? SetHotItem(m_nHotItem +1) : SetHotItem(0);
+ break;
+
+ default:
+ // Handle Accelerator keys with Alt toggled down
+ if (m_bKeyMode)
+ {
+ UINT ID;
+ if (SendMessage(TB_MAPACCELERATOR, wParam, (LPARAM) &ID))
+ {
+ m_nHotItem = ID;
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ }
+ else
+ ::MessageBeep(MB_OK);
+ }
+ break;
+ } // switch (wParam)
+ }
+
+ inline void CMenuBar::OnLButtonDown(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ GrabFocus();
+ m_nMDIButton = 0;
+ CPoint pt;
+
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+
+ if (IsMDIFrame())
+ {
+ if (IsMDIChildMaxed())
+ {
+ CClientDC MenuBarDC(this);
+ m_nMDIButton = -1;
+
+ if (m_MDIRect[0].PtInRect(pt)) m_nMDIButton = 0;
+ if (m_MDIRect[1].PtInRect(pt)) m_nMDIButton = 1;
+ if (m_MDIRect[2].PtInRect(pt)) m_nMDIButton = 2;
+
+ if (m_nMDIButton >= 0)
+ {
+ DrawMDIButton(MenuBarDC, MDI_MIN, (0 == m_nMDIButton)? 2 : 0);
+ DrawMDIButton(MenuBarDC, MDI_RESTORE, (1 == m_nMDIButton)? 2 : 0);
+ DrawMDIButton(MenuBarDC, MDI_CLOSE, (2 == m_nMDIButton)? 2 : 0);
+ }
+
+ // Bring up the MDI Child window's system menu when the icon is pressed
+ if (0 == HitTest())
+ {
+ m_nHotItem = 0;
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ }
+ }
+ }
+ }
+
+ inline void CMenuBar::OnLButtonUp(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ CPoint pt;
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+
+ if (IsMDIFrame())
+ {
+ HWND MDIClient = m_pFrame->GetView()->GetHwnd();
+ HWND MDIChild = GetActiveMDIChild();
+
+ if (IsMDIChildMaxed())
+ {
+ CPoint pt = GetCursorPos();
+ ScreenToClient(pt);
+
+ // Process the MDI button action when the left mouse button is up
+ if (m_MDIRect[0].PtInRect(pt))
+ {
+ if (MDI_MIN == m_nMDIButton)
+ ::ShowWindow(MDIChild, SW_MINIMIZE);
+ }
+
+ if (m_MDIRect[1].PtInRect(pt))
+ {
+ if (MDI_RESTORE == m_nMDIButton)
+ ::PostMessage(MDIClient, WM_MDIRESTORE, (WPARAM)MDIChild, 0L);
+ }
+
+ if (m_MDIRect[2].PtInRect(pt))
+ {
+ if (MDI_CLOSE == m_nMDIButton)
+ ::PostMessage(MDIChild, WM_CLOSE, 0L, 0L);
+ }
+ }
+ }
+ m_nMDIButton = 0;
+ ExitMenu();
+ }
+
+ inline BOOL CMenuBar::OnMenuInput(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // When a popup menu is active, StaticMsgHook directs all menu messages here
+ {
+ switch(uMsg)
+ {
+ case WM_KEYDOWN:
+ m_bExitAfter = FALSE;
+ {
+ switch (wParam)
+ {
+ case VK_ESCAPE:
+ // Use default processing if inside a Sub Menu
+ if ((m_hSelMenu) &&(m_hSelMenu != m_hPopupMenu))
+ return FALSE;
+
+ m_bMenuActive = FALSE;
+ m_bKeyMode = TRUE;
+ SendMessage(WM_CANCELMODE, 0L, 0L);
+ SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0));
+ SendMessage(TB_SETHOTITEM, m_nHotItem, 0L);
+ break;
+
+ case VK_LEFT:
+ // Use default processing if inside a Sub Menu
+ if ((m_hSelMenu) &&(m_hSelMenu != m_hPopupMenu))
+ return FALSE;
+
+ SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0));
+
+ // Move left to next topmenu item
+ (m_nHotItem > 0)? --m_nHotItem : m_nHotItem = GetButtonCount()-1;
+ SendMessage(WM_CANCELMODE, 0L, 0L);
+
+ // Always use PostMessage for USER_POPUPMENU (not SendMessage)
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ PostMessage(WM_KEYDOWN, VK_DOWN, 0L);
+ break;
+
+ case VK_RIGHT:
+ // Use default processing to open Sub Menu
+ if (m_bSelPopup)
+ return FALSE;
+
+ SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0));
+
+ // Move right to next topmenu item
+ (m_nHotItem < GetButtonCount()-1)? ++m_nHotItem : m_nHotItem = 0;
+ SendMessage(WM_CANCELMODE, 0L, 0L);
+
+ // Always use PostMessage for USER_POPUPMENU (not SendMessage)
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ PostMessage(WM_KEYDOWN, VK_DOWN, 0L);
+ break;
+
+ case VK_RETURN:
+ m_bExitAfter = TRUE;
+ break;
+
+ } // switch (wParam)
+
+ } // case WM_KEYDOWN
+
+ return FALSE;
+
+ case WM_CHAR:
+ m_bExitAfter = TRUE;
+ return FALSE;
+
+ case WM_LBUTTONDOWN:
+ {
+ m_bExitAfter = TRUE;
+ if (HitTest() >= 0)
+ {
+ // Cancel popup when we hit a button a second time
+ SendMessage(WM_CANCELMODE, 0L, 0L);
+ return TRUE;
+ }
+ }
+ return FALSE;
+
+ case WM_LBUTTONDBLCLK:
+ // Perform default action for DblClick on MDI Maxed icon
+ if (IsMDIChildMaxed() && (0 == HitTest()))
+ {
+ CWnd* pMDIChild = FromHandle(GetActiveMDIChild());
+ CMenu* pChildMenu = pMDIChild->GetSystemMenu(FALSE);
+
+ UINT nID = pChildMenu->GetDefaultItem(FALSE, 0);
+ if (nID)
+ pMDIChild->PostMessage(WM_SYSCOMMAND, nID, 0L);
+ }
+
+ m_bExitAfter = TRUE;
+ return FALSE;
+
+ case WM_MENUSELECT:
+ {
+ // store info about selected item
+ m_hSelMenu = (HMENU)lParam;
+ m_bSelPopup = ((HIWORD(wParam) & MF_POPUP) != 0);
+
+ // Reflect message back to the frame window
+ GetAncestor()->SendMessage(WM_MENUSELECT, wParam, lParam);
+ }
+ return TRUE;
+
+ case WM_MOUSEMOVE:
+ {
+ CPoint pt;
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+
+ // Skip if mouse hasn't moved
+ if ((pt.x == m_OldMousePos.x) && (pt.y == m_OldMousePos.y))
+ return FALSE;
+
+ m_OldMousePos.x = pt.x;
+ m_OldMousePos.y = pt.y;
+ ScreenToClient(pt);
+
+ // Reflect messages back to the MenuBar for hot tracking
+ SendMessage(WM_MOUSEMOVE, 0L, MAKELPARAM(pt.x, pt.y));
+ }
+ break;
+
+ }
+ return FALSE;
+ }
+
+ inline void CMenuBar::OnMouseLeave()
+ {
+ if (IsMDIFrame())
+ {
+ if (IsMDIChildMaxed())
+ {
+ CClientDC MenuBarDC(this);
+
+ DrawMDIButton(MenuBarDC, MDI_MIN, 0);
+ DrawMDIButton(MenuBarDC, MDI_RESTORE, 0);
+ DrawMDIButton(MenuBarDC, MDI_CLOSE, 0);
+ }
+ }
+ }
+
+ inline void CMenuBar::OnMouseMove(WPARAM wParam, LPARAM lParam)
+ {
+ CPoint pt;
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+
+ if (IsMDIFrame())
+ {
+ if (IsMDIChildMaxed())
+ {
+ CClientDC MenuBarDC(this);
+ int MDIButton = -1;
+ if (m_MDIRect[0].PtInRect(pt)) MDIButton = 0;
+ if (m_MDIRect[1].PtInRect(pt)) MDIButton = 1;
+ if (m_MDIRect[2].PtInRect(pt)) MDIButton = 2;
+
+ if (MK_LBUTTON == wParam) // mouse moved with left mouse button is held down
+ {
+ // toggle the MDI button image pressed/unpressed as required
+ if (MDIButton >= 0)
+ {
+ DrawMDIButton(MenuBarDC, MDI_MIN, ((0 == MDIButton) && (0 == m_nMDIButton))? 2 : 0);
+ DrawMDIButton(MenuBarDC, MDI_RESTORE, ((1 == MDIButton) && (1 == m_nMDIButton))? 2 : 0);
+ DrawMDIButton(MenuBarDC, MDI_CLOSE, ((2 == MDIButton) && (2 == m_nMDIButton))? 2 : 0);
+ }
+ else
+ {
+ DrawMDIButton(MenuBarDC, MDI_MIN, 0);
+ DrawMDIButton(MenuBarDC, MDI_RESTORE, 0);
+ DrawMDIButton(MenuBarDC, MDI_CLOSE, 0);
+ }
+ }
+ else // mouse moved without left mouse button held down
+ {
+ if (MDIButton >= 0)
+ {
+ DrawMDIButton(MenuBarDC, MDI_MIN, (0 == MDIButton)? 1 : 0);
+ DrawMDIButton(MenuBarDC, MDI_RESTORE, (1 == MDIButton)? 1 : 0);
+ DrawMDIButton(MenuBarDC, MDI_CLOSE, (2 == MDIButton)? 1 : 0);
+ }
+ else
+ {
+ DrawMDIButton(MenuBarDC, MDI_MIN, 0);
+ DrawMDIButton(MenuBarDC, MDI_RESTORE, 0);
+ DrawMDIButton(MenuBarDC, MDI_CLOSE, 0);
+ }
+ }
+ }
+ }
+ }
+
+ inline LRESULT CMenuBar::OnNotifyReflect(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case NM_CUSTOMDRAW:
+ {
+ return OnCustomDraw((LPNMHDR) lParam);
+ }
+
+ case TBN_DROPDOWN:
+ // Always use PostMessage for USER_POPUPMENU (not SendMessage)
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ break;
+
+ case TBN_HOTITEMCHANGE:
+ // This is the notification that a hot item change is about to occur
+ // This is used to bring up a new popup menu when required
+ {
+ CPoint pt = GetCursorPos();
+ if (this == WindowFromPoint(pt)) // MenuBar window must be on top
+ {
+ DWORD flag = ((LPNMTBHOTITEM)lParam)->dwFlags;
+ if ((flag & HICF_MOUSE) && !(flag & HICF_LEAVING))
+ {
+ int nButton = HitTest();
+ if ((m_bMenuActive) && (nButton != m_nHotItem))
+ {
+ SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0));
+ m_nHotItem = nButton;
+ SendMessage(WM_CANCELMODE, 0L, 0L);
+
+ //Always use PostMessage for USER_POPUPMENU (not SendMessage)
+ PostMessage(UWM_POPUPMENU, 0L, 0L);
+ }
+ m_nHotItem = nButton;
+ }
+
+ // Handle escape from popup menu
+ if ((flag & HICF_LEAVING) && m_bKeyMode)
+ {
+ m_nHotItem = ((LPNMTBHOTITEM)lParam)->idOld;
+ PostMessage(TB_SETHOTITEM, m_nHotItem, 0L);
+ }
+
+ }
+ break;
+ } //case TBN_HOTITEMCHANGE:
+
+ } // switch(((LPNMHDR)lParam)->code)
+ return 0L;
+ } // CMenuBar::OnNotify(...)
+
+ inline void CMenuBar::OnWindowPosChanged()
+ {
+ InvalidateRect(&m_MDIRect[0], TRUE);
+ InvalidateRect(&m_MDIRect[1], TRUE);
+ InvalidateRect(&m_MDIRect[2], TRUE);
+ {
+ CClientDC MenuBarDC(this);
+ DrawAllMDIButtons(MenuBarDC);
+ }
+ }
+
+ inline void CMenuBar::PreCreate(CREATESTRUCT &cs)
+ {
+ cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_LIST | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE;
+ }
+
+ inline void CMenuBar::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = TOOLBARCLASSNAME;
+ }
+
+ inline void CMenuBar::ReleaseFocus()
+ {
+ if (m_hPrevFocus)
+ ::SetFocus(m_hPrevFocus);
+
+ m_hPrevFocus = NULL;
+ ::ReleaseCapture();
+ }
+
+ inline void CMenuBar::SetHotItem(int nHot)
+ {
+ m_nHotItem = nHot;
+ SendMessage(TB_SETHOTITEM, m_nHotItem, 0L);
+ }
+
+ inline void CMenuBar::SetMenu(HMENU hMenu)
+ {
+ assert(::IsWindow(m_hWnd));
+
+ m_hTopMenu = hMenu;
+ int nMaxedOffset = (IsMDIChildMaxed()? 1:0);
+
+ // Remove any existing buttons
+ while (SendMessage(TB_BUTTONCOUNT, 0L, 0L) > 0)
+ {
+ if(!SendMessage(TB_DELETEBUTTON, 0L, 0L))
+ break;
+ }
+
+ // Set the Bitmap size to zero
+ SendMessage(TB_SETBITMAPSIZE, 0L, MAKELPARAM(0, 0));
+
+ if (IsMDIChildMaxed())
+ {
+ // Create an extra button for the MDI child system menu
+ // Later we will custom draw the window icon over this button
+ TBBUTTON tbb = {0};
+ tbb.fsState = TBSTATE_ENABLED;
+ tbb.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE ;
+ tbb.iString = (INT_PTR)_T(" ");
+ SendMessage(TB_ADDBUTTONS, 1, (WPARAM)&tbb);
+ SetButtonText(0, _T(" "));
+ }
+
+ for (int i = 0 ; i < ::GetMenuItemCount(hMenu); ++i)
+ {
+ // Assign the ToolBar Button struct
+ TBBUTTON tbb = {0};
+ tbb.idCommand = i + nMaxedOffset; // Each button needs a unique ID
+ tbb.fsState = TBSTATE_ENABLED;
+ tbb.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE | TBSTYLE_DROPDOWN;
+ tbb.iString = (INT_PTR)_T(" ");
+ SendMessage(TB_ADDBUTTONS, 1, (WPARAM)&tbb);
+
+ // Add the menu title to the string table
+ std::vector<TCHAR> vMenuName( MAX_MENU_STRING+1, _T('\0') );
+ TCHAR* szMenuName = &vMenuName[0];
+ GetMenuString(hMenu, i, szMenuName, MAX_MENU_STRING, MF_BYPOSITION);
+ SetButtonText(i + nMaxedOffset, szMenuName);
+ }
+ }
+
+ inline void CMenuBar::SetMenuBarTheme(MenuTheme& Theme)
+ {
+ m_ThemeMenu.UseThemes = Theme.UseThemes;
+ m_ThemeMenu.clrHot1 = Theme.clrHot1;
+ m_ThemeMenu.clrHot2 = Theme.clrHot2;
+ m_ThemeMenu.clrPressed1 = Theme.clrPressed1;
+ m_ThemeMenu.clrPressed2 = Theme.clrPressed2;
+ m_ThemeMenu.clrOutline = Theme.clrOutline;
+
+ if (IsWindow())
+ Invalidate();
+ }
+
+ inline LRESULT CALLBACK CMenuBar::StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam)
+ {
+ assert(GetApp());
+ MSG* pMsg = (MSG*)lParam;
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ assert(pTLSData);
+ CMenuBar* pMenuBar = (CMenuBar*)pTLSData->pMenuBar;
+
+ if (pMenuBar && (MSGF_MENU == nCode))
+ {
+ // process menu message
+ if (pMenuBar->OnMenuInput(pMsg->message, pMsg->wParam, pMsg->lParam))
+ {
+ return TRUE;
+ }
+ }
+
+ return CallNextHookEx(pTLSData->hHook, nCode, wParam, lParam);
+ }
+
+ inline void CMenuBar::OnSysCommand(WPARAM wParam, LPARAM lParam)
+ {
+ if (SC_KEYMENU == wParam)
+ {
+ if (0 == lParam)
+ {
+ // Alt/F10 key toggled
+ GrabFocus();
+ m_bKeyMode = TRUE;
+ int nMaxedOffset = (IsMDIChildMaxed()? 1:0);
+ SetHotItem(nMaxedOffset);
+ }
+ else
+ // Handle key pressed with Alt held down
+ DoAltKey((WORD)lParam);
+ }
+ }
+
+ inline LRESULT CMenuBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_CHAR:
+ return 0L; // Discard these messages
+ case WM_DRAWITEM:
+ m_pFrame->OnDrawItem(wParam, lParam);
+ return TRUE; // handled
+ case WM_EXITMENULOOP:
+ if (m_bExitAfter)
+ ExitMenu();
+ m_pFrame->OnExitMenuLoop();
+ break;
+ case WM_INITMENUPOPUP:
+ m_pFrame->OnInitMenuPopup(wParam, lParam);
+ break;
+ case WM_KEYDOWN:
+ OnKeyDown(wParam, lParam);
+ return 0L; // Discard these messages
+ case WM_KILLFOCUS:
+ ExitMenu();
+ return 0L;
+ case WM_LBUTTONDOWN:
+ // Do default processing first
+ CallWindowProc(GetPrevWindowProc(), uMsg, wParam, lParam);
+
+ OnLButtonDown(wParam, lParam);
+ return 0L;
+ case WM_LBUTTONUP:
+ OnLButtonUp(wParam, lParam);
+ break;
+ case WM_MEASUREITEM:
+ m_pFrame->OnMeasureItem(wParam, lParam);
+ return TRUE; // handled
+ case WM_MOUSELEAVE:
+ OnMouseLeave();
+ break;
+ case WM_MOUSEMOVE:
+ OnMouseMove(wParam, lParam);
+ break;
+ case UWM_POPUPMENU:
+ DoPopupMenu();
+ return 0L;
+ case WM_SYSKEYDOWN:
+ if ((VK_MENU == wParam) || (VK_F10 == wParam))
+ return 0L;
+ break;
+ case WM_SYSKEYUP:
+ if ((VK_MENU == wParam) || (VK_F10 == wParam))
+ {
+ ExitMenu();
+ return 0L;
+ }
+ break;
+ case UWM_GETMENUTHEME:
+ {
+ MenuTheme& tm = GetMenuBarTheme();
+ return (LRESULT)&tm;
+ }
+ case WM_WINDOWPOSCHANGED:
+ OnWindowPosChanged();
+ break;
+ case WM_WINDOWPOSCHANGING:
+ // Bypass CToolBar::WndProcDefault for this message
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+
+ } // switch (uMsg)
+
+ return CToolBar::WndProcDefault(uMsg, wParam, lParam);
+ } // LRESULT CMenuBar::WndProcDefault(...)
+
+
+
+ ///////////////////////////////////
+ // Definitions for the CFrame class
+ //
+ inline CFrame::CFrame() : m_tsStatusText(_T("Ready")), m_bShowIndicatorStatus(TRUE), m_bShowMenuStatus(TRUE),
+ m_bUseReBar(FALSE), m_bUseThemes(TRUE), m_bUpdateTheme(FALSE), m_bUseToolBar(TRUE), m_bUseCustomDraw(TRUE),
+ m_bShowStatusBar(TRUE), m_bShowToolBar(TRUE), m_himlMenu(NULL), m_himlMenuDis(NULL),
+ m_AboutDialog(IDW_ABOUT), m_pView(NULL), m_nMaxMRU(0), m_hOldFocus(0), m_nOldID(-1)
+ {
+ ZeroMemory(&m_ThemeMenu, sizeof(m_ThemeMenu));
+
+ // Do either InitCommonControls or InitCommonControlsEx
+ LoadCommonControls();
+
+ // By default, we use the rebar if we can
+ if (GetComCtlVersion() >= 470)
+ m_bUseReBar = TRUE;
+
+ for (int i = 0 ; i < 3 ; ++i)
+ m_OldStatus[i] = _T('\0');
+ }
+
+ inline CFrame::~CFrame()
+ {
+ if (m_himlMenu) ImageList_Destroy(m_himlMenu);
+ if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis);
+ }
+
+ inline BOOL CFrame::AddMenuIcon(int nID_MenuItem, HICON hIcon, int cx /*= 16*/, int cy /*= 16*/)
+ {
+ // Get ImageList image size
+ int cxOld = 0;
+ int cyOld = 0;
+ ImageList_GetIconSize(m_himlMenu, &cxOld, &cyOld );
+
+ // Create a new ImageList if required
+ if ((cx != cxOld) || (cy != cyOld) || (NULL == m_himlMenu))
+ {
+ if (m_himlMenu) ImageList_Destroy(m_himlMenu);
+ m_himlMenu = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, 1, 0);
+ m_vMenuIcons.clear();
+ }
+
+ if (ImageList_AddIcon(m_himlMenu, hIcon) != -1)
+ {
+ m_vMenuIcons.push_back(nID_MenuItem);
+
+ // Recreate the Disabled imagelist
+ if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis);
+ m_himlMenuDis = NULL;
+ m_himlMenuDis = CreateDisabledImageList(m_himlMenu);
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ inline UINT CFrame::AddMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID)
+ // Adds the icons from a bitmap resouce to an internal ImageList for use with popup menu items.
+ // Note: If existing are a different size to the new ones, the old ones will be removed!
+ // The ToolBarDisabledID is ignored unless ToolBarID and ToolBarDisabledID bitmaps are the same size.
+ {
+ // Count the MenuData entries excluding seperators
+ int iImages = 0;
+ for (UINT i = 0 ; i < MenuData.size(); ++i)
+ {
+ if (MenuData[i] != 0) // Don't count seperators
+ {
+ ++iImages;
+ }
+ }
+
+ // Load the button images from Resouce ID
+ CBitmap Bitmap(ToolBarID);
+
+ if ((0 == iImages) || (!Bitmap))
+ return (UINT)m_vMenuIcons.size(); // No valid images, so nothing to do!
+
+ BITMAP bm = Bitmap.GetBitmapData();
+ int iImageWidth = bm.bmWidth / iImages;
+ int iImageHeight = bm.bmHeight;
+
+ // Create the ImageList if required
+ if (NULL == m_himlMenu)
+ {
+ m_himlMenu = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iImages, 0);
+ m_vMenuIcons.clear();
+ }
+ else
+ {
+ int Oldcx;
+ int Oldcy;
+
+ ImageList_GetIconSize(m_himlMenu, &Oldcx, &Oldcy);
+ if (iImageHeight != Oldcy)
+ {
+ TRACE(_T("Unable to add icons. The new icons are a different size to the old ones\n"));
+ return (UINT)m_vMenuIcons.size();
+ }
+ }
+
+ // Add the resource IDs to the m_vMenuIcons vector
+ for (UINT j = 0 ; j < MenuData.size(); ++j)
+ {
+ if (MenuData[j] != 0)
+ {
+ m_vMenuIcons.push_back(MenuData[j]);
+ }
+ }
+
+ // Add the images to the ImageList
+ ImageList_AddMasked(m_himlMenu, Bitmap, crMask);
+
+ // Create the Disabled imagelist
+ if (ToolBarDisabledID)
+ {
+ if (0 != m_himlMenuDis)
+ m_himlMenuDis = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iImages, 0);
+
+ CBitmap BitmapDisabled(ToolBarDisabledID);
+ BITMAP bmDis = BitmapDisabled.GetBitmapData();
+
+ int iImageWidthDis = bmDis.bmWidth / iImages;
+ int iImageHeightDis = bmDis.bmHeight;
+
+ // Normal and Disabled icons must be the same size
+ if ((iImageWidthDis == iImageWidth) && (iImageHeightDis == iImageHeight))
+ {
+ ImageList_AddMasked(m_himlMenu, BitmapDisabled, crMask);
+ }
+ else
+ {
+ ImageList_Destroy(m_himlMenuDis);
+ m_himlMenuDis = CreateDisabledImageList(m_himlMenu);
+ }
+ }
+ else
+ {
+ if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis);
+ m_himlMenuDis = CreateDisabledImageList(m_himlMenu);
+ }
+
+ // return the number of menu icons
+ return (UINT)m_vMenuIcons.size();
+ }
+
+ inline void CFrame::AddMenuBarBand()
+ {
+ // Adds a MenuBar to the rebar control
+ REBARBANDINFO rbbi = {0};
+ CSize sz = GetMenuBar().GetMaxSize();
+
+ // Calculate the MenuBar height from the menu font
+ CSize csMenuBar;
+ CClientDC dcMenuBar(&GetMenuBar());
+ dcMenuBar.SelectObject(GetMenuBar().GetFont());
+ csMenuBar = dcMenuBar.GetTextExtentPoint32(_T("\tSomeText"), lstrlen(_T("\tSomeText")));
+ int MenuBar_Height = csMenuBar.cy + 6;
+
+ rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_SIZE | RBBIM_ID;
+ rbbi.cxMinChild = sz.cx;
+ rbbi.cx = sz.cx;
+ rbbi.cyMinChild = MenuBar_Height;
+ rbbi.cyMaxChild = MenuBar_Height;
+ rbbi.fStyle = RBBS_BREAK | RBBS_VARIABLEHEIGHT | RBBS_GRIPPERALWAYS ;
+ rbbi.hwndChild = GetMenuBar();
+ rbbi.wID = IDW_MENUBAR;
+
+ // Note: rbbi.cbSize is set inside the InsertBand function
+ GetReBar().InsertBand(-1, rbbi);
+ SetMenuBarBandSize();
+ GetReBar().SetMenuBar(GetMenuBar());
+
+ if (GetReBar().GetReBarTheme().LockMenuBand)
+ GetReBar().ShowGripper(GetReBar().GetBand(GetMenuBar()), FALSE);
+ }
+
+ inline void CFrame::AddMRUEntry(LPCTSTR szMRUEntry)
+ {
+ // Erase possible duplicate entries from vector
+ RemoveMRUEntry(szMRUEntry);
+
+ // Insert the entry at the beginning of the vector
+ m_vMRUEntries.insert(m_vMRUEntries.begin(), szMRUEntry);
+
+ // Delete excessive MRU entries
+ if (m_vMRUEntries.size() > m_nMaxMRU)
+ m_vMRUEntries.erase(m_vMRUEntries.begin() + m_nMaxMRU, m_vMRUEntries.end());
+
+ UpdateMRUMenu();
+ }
+
+ inline void CFrame::AddToolBarBand(CToolBar& TB, DWORD dwStyle, UINT nID)
+ {
+ // Adds a ToolBar to the rebar control
+
+ // Create the ToolBar Window
+ TB.Create(&GetReBar());
+
+ // Fill the REBARBAND structure
+ REBARBANDINFO rbbi = {0};
+ CSize sz = TB.GetMaxSize();
+
+ rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_SIZE | RBBIM_ID;
+ rbbi.cyMinChild = sz.cy;
+ rbbi.cyMaxChild = sz.cy;
+ rbbi.cx = sz.cx +2;
+ rbbi.cxMinChild = sz.cx +2;
+
+ rbbi.fStyle = dwStyle;
+ rbbi.hwndChild = TB;
+ rbbi.wID = nID;
+
+ // Note: rbbi.cbSize is set inside the InsertBand function
+ GetReBar().InsertBand(-1, rbbi);
+ }
+
+ inline void CFrame::AddToolBarButton(UINT nID, BOOL bEnabled /* = TRUE*/, LPCTSTR szText)
+ // Adds Resource IDs to toolbar buttons.
+ // A resource ID of 0 is a separator
+ {
+ GetToolBar().AddButton(nID, bEnabled);
+
+ if(0 != szText)
+ GetToolBar().SetButtonText(nID, szText);
+
+ if (!IsWindow()) TRACE(_T("Warning ... Resource IDs for toolbars should be added in SetupToolBar\n"));
+ }
+
+ inline void CFrame::AdjustFrameRect(RECT rcView) const
+ // Adjust the size of the frame to accommodate the View window's dimensions
+ {
+ // Adjust for the view styles
+ CRect rc = rcView;
+ DWORD dwStyle = (DWORD)GetView()->GetWindowLongPtr(GWL_STYLE);
+ DWORD dwExStyle = (DWORD)GetView()->GetWindowLongPtr(GWL_EXSTYLE);
+ AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
+
+ // Calculate the new frame height
+ CRect rcFrameBefore = GetWindowRect();
+ CRect rcViewBefore = GetViewRect();
+ int Height = rc.Height() + rcFrameBefore.Height() - rcViewBefore.Height();
+
+ // Adjust for the frame styles
+ dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
+ dwExStyle = (DWORD)GetWindowLongPtr(GWL_EXSTYLE);
+ AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle);
+
+ // Calculate final rect size, and reposition frame
+ SetWindowPos(NULL, 0, 0, rc.Width(), Height, SWP_NOMOVE);
+ }
+
+ inline void CFrame::CreateToolBar()
+ {
+ if (IsReBarSupported() && m_bUseReBar)
+ AddToolBarBand(GetToolBar(), RBBS_BREAK, IDW_TOOLBAR); // Create the toolbar inside rebar
+ else
+ GetToolBar().Create(this); // Create the toolbar without a rebar
+
+ SetupToolBar();
+
+ if (IsReBarSupported() && m_bUseReBar)
+ {
+ if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().LockMenuBand)
+ {
+ // Hide gripper for single toolbar
+ if (GetReBar().GetBandCount() <= 2)
+ GetReBar().ShowGripper(GetReBar().GetBand(GetToolBar()), FALSE);
+ }
+ }
+
+ if (GetToolBar().GetToolBarData().size() > 0)
+ {
+ // Set the toolbar images (if not already set in SetupToolBar)
+ // A mask of 192,192,192 is compatible with AddBitmap (for Win95)
+ if (!GetToolBar().SendMessage(TB_GETIMAGELIST, 0L, 0L))
+ SetToolBarImages(RGB(192,192,192), IDW_MAIN, 0, 0);
+
+ // Add the icons for popup menu
+ AddMenuIcons(GetToolBar().GetToolBarData(), RGB(192, 192, 192), IDW_MAIN, 0);
+ }
+ else
+ {
+ TRACE(_T("Warning ... No resource IDs assigned to the toolbar\n"));
+ }
+ }
+
+ inline void CFrame::DrawCheckmark(LPDRAWITEMSTRUCT pdis, CDC& DrawDC)
+ // Draws the checkmark or radiocheck transparently
+ {
+ CRect rc = pdis->rcItem;
+ UINT fType = ((ItemData*)pdis->itemData)->fType;
+ MenuTheme tm = GetMenuTheme();
+ CRect rcBk;
+
+ // Draw the checkmark's background rectangle first
+ int Iconx = 16, Icony = 16;
+ if (m_himlMenu) ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony);
+ int BarWidth = Iconx + 8;
+ int left = (BarWidth - Iconx)/2;
+ int top = rc.top + (rc.Height() - Icony)/2;
+ rcBk.SetRect(left, top, left + Iconx, top + Icony);
+
+ if (tm.UseThemes)
+ {
+ DrawDC.CreateSolidBrush(tm.clrHot2);
+ DrawDC.CreatePen(PS_SOLID, 1, tm.clrOutline);
+
+ // Draw the checkmark's background rectangle
+ DrawDC.Rectangle(rcBk.left, rcBk.top, rcBk.right, rcBk.bottom);
+ }
+
+ CMemDC MemDC(FromHandle(pdis->hDC));
+ int cxCheck = ::GetSystemMetrics(SM_CXMENUCHECK);
+ int cyCheck = ::GetSystemMetrics(SM_CYMENUCHECK);
+ MemDC.CreateBitmap(cxCheck, cyCheck, 1, 1, NULL);
+ CRect rcCheck( 0, 0, cxCheck, cyCheck);
+
+ // Copy the check mark bitmap to hdcMem
+ if (MFT_RADIOCHECK == fType)
+ MemDC.DrawFrameControl(rcCheck, DFC_MENU, DFCS_MENUBULLET);
+ else
+ MemDC.DrawFrameControl(rcCheck, DFC_MENU, DFCS_MENUCHECK);
+
+ int xoffset = (rcBk.Width() - rcCheck.Width()-1)/2;
+ int yoffset = (rcBk.Height() - rcCheck.Height()-1)/2;
+
+ if (tm.UseThemes)
+ xoffset += 2;
+
+ // Draw a white or black check mark as required
+ // Unfortunately MaskBlt isn't supported on Win95, 98 or ME, so we do it the hard way
+ CMemDC MaskDC(FromHandle(pdis->hDC));
+ MaskDC.CreateCompatibleBitmap(FromHandle(pdis->hDC), cxCheck, cyCheck);
+ MaskDC.BitBlt(0, 0, cxCheck, cyCheck, &MaskDC, 0, 0, WHITENESS);
+
+ if ((pdis->itemState & ODS_SELECTED) && (!tm.UseThemes))
+ {
+ // Draw a white checkmark
+ MemDC.BitBlt(0, 0, cxCheck, cyCheck, &MemDC, 0, 0, DSTINVERT);
+ MaskDC.BitBlt(0, 0, cxCheck, cyCheck, &MemDC, 0, 0, SRCAND);
+ DrawDC.BitBlt(rcBk.left + xoffset, rcBk.top + yoffset, cxCheck, cyCheck, &MaskDC, 0, 0, SRCPAINT);
+ }
+ else
+ {
+ // Draw a black checkmark
+ int BullitOffset = ((MFT_RADIOCHECK == fType) && tm.UseThemes)? 1 : 0;
+ MaskDC.BitBlt( -BullitOffset, BullitOffset, cxCheck, cyCheck, &MemDC, 0, 0, SRCAND);
+ DrawDC.BitBlt(rcBk.left + xoffset, rcBk.top + yoffset, cxCheck, cyCheck, &MaskDC, 0, 0, SRCAND);
+ }
+ }
+
+ inline void CFrame::DrawMenuIcon(LPDRAWITEMSTRUCT pdis, CDC& DrawDC, BOOL bDisabled)
+ {
+ if (!m_himlMenu)
+ return;
+ // Get icon size
+ int Iconx;
+ int Icony;
+ ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony);
+ int BarWidth = Iconx + 8;
+
+ // get the drawing rectangle
+ CRect rc = pdis->rcItem;
+ int left = (BarWidth - Iconx)/2;
+ int top = rc.top + (rc.Height() - Icony)/2;
+ rc.SetRect(left, top, left + Iconx, top + Icony);
+
+ // get the icon's location in the imagelist
+ int iImage = -1;
+ for (int i = 0 ; i < (int)m_vMenuIcons.size(); ++i)
+ {
+ if (pdis->itemID == m_vMenuIcons[i])
+ iImage = i;
+ }
+
+ // draw the image
+ if (iImage >= 0 )
+ {
+ if ((bDisabled) && (m_himlMenuDis))
+ ImageList_Draw(m_himlMenuDis, iImage, DrawDC, rc.left, rc.top, ILD_TRANSPARENT);
+ else
+ ImageList_Draw(m_himlMenu, iImage, DrawDC, rc.left, rc.top, ILD_TRANSPARENT);
+ }
+ }
+
+ inline void CFrame::DrawMenuText(CDC& DrawDC, LPCTSTR ItemText, CRect& rc, COLORREF colorText)
+ {
+ // find the position of tab character
+ int nTab = -1;
+ for(int i = 0; i < lstrlen(ItemText); ++i)
+ {
+ if(_T('\t') == ItemText[i])
+ {
+ nTab = i;
+ break;
+ }
+ }
+
+ // Draw the item text
+ DrawDC.SetTextColor(colorText);
+ DrawDC.DrawText(ItemText, nTab, rc, DT_SINGLELINE | DT_LEFT | DT_VCENTER);
+
+ // Draw text after tab, right aligned
+ if(nTab != -1)
+ DrawDC.DrawText( &ItemText[nTab + 1], -1, rc, DT_SINGLELINE | DT_RIGHT | DT_VCENTER);
+ }
+
+ inline int CFrame::GetMenuItemPos(HMENU hMenu, LPCTSTR szItem)
+ // Returns the position of the menu item, given it's name
+ {
+ int nMenuItemCount = GetMenuItemCount(hMenu);
+ MENUITEMINFO mii = {0};
+ mii.cbSize = GetSizeofMenuItemInfo();
+
+ for (int nItem = 0 ; nItem < nMenuItemCount; ++nItem)
+ {
+ std::vector<TCHAR> vTChar( MAX_MENU_STRING+1, _T('\0') );
+ TCHAR* szStr = &vTChar[0];
+
+ std::vector<TCHAR> vStripped( MAX_MENU_STRING+1, _T('\0') );
+ TCHAR* szStripped = &vStripped[0];
+
+ mii.fMask = MIIM_TYPE;
+ mii.fType = MFT_STRING;
+ mii.dwTypeData = szStr;
+ mii.cch = MAX_MENU_STRING;
+
+ // Fill the contents of szStr from the menu item
+ if (::GetMenuItemInfo(hMenu, nItem, TRUE, &mii) && (lstrlen(szStr) <= MAX_MENU_STRING))
+ {
+ // Strip out any & characters
+ int j = 0;
+ for (int i = 0; i < lstrlen(szStr); ++i)
+ {
+ if (szStr[i] != _T('&'))
+ szStripped[j++] = szStr[i];
+ }
+ szStripped[j] = _T('\0'); // Append null tchar
+
+ // Compare the strings
+ if (0 == lstrcmp(szStripped, szItem))
+ return nItem;
+ }
+ }
+
+ return -1;
+ }
+
+ inline tString CFrame::GetMRUEntry(UINT nIndex)
+ {
+ tString tsPathName;
+ if (nIndex < m_vMRUEntries.size())
+ {
+ tsPathName = m_vMRUEntries[nIndex];
+
+ // Now put the selected entry at Index 0
+ AddMRUEntry(tsPathName.c_str());
+ }
+ return tsPathName;
+ }
+
+ inline CRect CFrame::GetViewRect() const
+ {
+ // Get the frame's client area
+ CRect rcFrame = GetClientRect();
+
+ // Get the statusbar's window area
+ CRect rcStatus;
+ if (GetStatusBar().IsWindowVisible() || !IsWindowVisible())
+ rcStatus = GetStatusBar().GetWindowRect();
+
+ // Get the top rebar or toolbar's window area
+ CRect rcTop;
+ if (IsReBarSupported() && m_bUseReBar)
+ rcTop = GetReBar().GetWindowRect();
+ else
+ if (GetToolBar().IsWindow() && GetToolBar().IsWindowVisible())
+ rcTop = GetToolBar().GetWindowRect();
+
+ // Return client size less the rebar and status windows
+ int top = rcFrame.top + rcTop.Height();
+ int left = rcFrame.left;
+ int right = rcFrame.right;
+ int bottom = rcFrame.Height() - (rcStatus.Height());
+ if ((bottom <= top) ||( right <= left))
+ top = left = right = bottom = 0;
+
+ CRect rcView(left, top, right, bottom);
+ return rcView;
+ }
+
+ inline void CFrame::LoadCommonControls()
+ {
+ HMODULE hComCtl;
+
+ try
+ {
+ // Load the Common Controls DLL
+ hComCtl = ::LoadLibrary(_T("COMCTL32.DLL"));
+ if (!hComCtl)
+ throw CWinException(_T("Failed to load COMCTL32.DLL"));
+
+ if (GetComCtlVersion() > 470)
+ {
+ // Declare a pointer to the InItCommonControlsEx function
+ typedef BOOL WINAPI INIT_EX(INITCOMMONCONTROLSEX*);
+ INIT_EX* pfnInit = (INIT_EX*)::GetProcAddress(hComCtl, "InitCommonControlsEx");
+
+ // Load the full set of common controls
+ INITCOMMONCONTROLSEX InitStruct = {0};
+ InitStruct.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitStruct.dwICC = ICC_COOL_CLASSES|ICC_DATE_CLASSES|ICC_INTERNET_CLASSES|ICC_NATIVEFNTCTL_CLASS|
+ ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|ICC_WIN95_CLASSES;
+
+ // Call InitCommonControlsEx
+ if(!((*pfnInit)(&InitStruct)))
+ throw CWinException(_T("InitCommonControlsEx failed"));
+ }
+ else
+ {
+ ::InitCommonControls();
+ }
+
+ ::FreeLibrary(hComCtl);
+ }
+
+ catch (const CWinException &e)
+ {
+ e.what();
+ if (hComCtl)
+ ::FreeLibrary(hComCtl);
+
+ throw;
+ }
+ }
+
+ inline BOOL CFrame::LoadRegistryMRUSettings(UINT nMaxMRU /*= 0*/)
+ {
+ // Load the MRU list from the registry
+
+ assert(!m_tsKeyName.empty()); // KeyName must be set before calling LoadRegistryMRUSettings
+ HKEY hKey = NULL;
+ BOOL bRet = FALSE;
+
+ try
+ {
+ m_nMaxMRU = MIN(nMaxMRU, 16);
+ std::vector<tString> vMRUEntries;
+ tString tsKey = _T("Software\\") + m_tsKeyName + _T("\\Recent Files");
+
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey))
+ {
+ for (UINT i = 0; i < m_nMaxMRU; ++i)
+ {
+ DWORD dwType = REG_SZ;
+ DWORD dwBufferSize = 0;
+ TCHAR szSubKey[10] = _T("");
+ wsprintf(szSubKey, _T("File %d\0"), i+1);
+
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, szSubKey, NULL, &dwType, NULL, &dwBufferSize))
+ throw CWinException(_T("RegQueryValueEx failed\n"));
+
+ std::vector<TCHAR> PathName( dwBufferSize, _T('\0') );
+ TCHAR* pTCharArray = &PathName[0];
+
+ // load the entry from the registry
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, szSubKey, NULL, &dwType, (LPBYTE)pTCharArray, &dwBufferSize))
+ throw CWinException(_T("RegQueryValueEx failed\n"));
+
+ if ( lstrlen( pTCharArray ) )
+ vMRUEntries.push_back( pTCharArray );
+ }
+
+ // successfully loaded all MRU values, so store them
+ m_vMRUEntries = vMRUEntries;
+ RegCloseKey(hKey);
+ bRet = TRUE;
+ }
+ }
+
+ catch(const CWinException& e)
+ {
+ TRACE(_T("Failed to load MRU values from registry\n"));
+ e.what();
+
+ if (hKey)
+ RegCloseKey(hKey);
+ }
+
+ return bRet;
+ }
+
+ inline BOOL CFrame::LoadRegistrySettings(LPCTSTR szKeyName)
+ {
+ assert (NULL != szKeyName);
+ m_tsKeyName = szKeyName;
+
+ tString tsKey = _T("Software\\") + m_tsKeyName + _T("\\Frame Settings");
+ HKEY hKey = 0;
+ BOOL bRet = FALSE;
+
+ try
+ {
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey))
+ {
+ DWORD dwType = REG_BINARY;
+ DWORD BufferSize = sizeof(DWORD);
+ DWORD dwTop, dwLeft, dwWidth, dwHeight, dwStatusBar, dwToolBar;
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Top"), NULL, &dwType, (LPBYTE)&dwTop, &BufferSize))
+ throw CWinException(_T("RegQueryValueEx Failed"));
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Left"), NULL, &dwType, (LPBYTE)&dwLeft, &BufferSize))
+ throw CWinException(_T("RegQueryValueEx Failed"));
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Width"), NULL, &dwType, (LPBYTE)&dwWidth, &BufferSize))
+ throw CWinException(_T("RegQueryValueEx Failed"));
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Height"), NULL, &dwType, (LPBYTE)&dwHeight, &BufferSize))
+ throw CWinException(_T("RegQueryValueEx Failed"));
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("StatusBar"), NULL, &dwType, (LPBYTE)&dwStatusBar, &BufferSize))
+ throw CWinException(_T("RegQueryValueEx Failed"));
+ if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("ToolBar"), NULL, &dwType, (LPBYTE)&dwToolBar, &BufferSize))
+ throw CWinException(_T("RegQueryValueEx Failed"));
+
+ m_rcPosition.top = dwTop;
+ m_rcPosition.left = dwLeft;
+ m_rcPosition.bottom = m_rcPosition.top + dwHeight;
+ m_rcPosition.right = m_rcPosition.left + dwWidth;
+ m_bShowStatusBar = dwStatusBar & 1;
+ m_bShowToolBar = dwToolBar & 1;
+
+ RegCloseKey(hKey);
+ bRet = TRUE;
+ }
+ }
+
+ catch (const CWinException& e)
+ {
+ TRACE(_T("Failed to load values from registry, using defaults!\n"));
+ e.what();
+
+ if (hKey)
+ RegCloseKey(hKey);
+ }
+
+ return bRet;
+ }
+
+ inline void CFrame::OnActivate(WPARAM wParam, LPARAM lParam)
+ {
+ // Do default processing first
+ DefWindowProc(WM_ACTIVATE, wParam, lParam);
+
+ if (LOWORD(wParam) == WA_INACTIVE)
+ {
+ // Save the hwnd of the window which currently has focus
+ // (this must be CFrame window itself or a child window
+ if (!IsIconic()) m_hOldFocus = ::GetFocus();
+
+ // Send a notification to the view window
+ int idCtrl = ::GetDlgCtrlID(m_hOldFocus);
+ NMHDR nhdr={0};
+ nhdr.hwndFrom = m_hOldFocus;
+ nhdr.idFrom = idCtrl;
+ nhdr.code = UWM_FRAMELOSTFOCUS;
+ if (GetView()->IsWindow())
+ GetView()->SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr);
+ }
+ else
+ {
+ // Now set the focus to the appropriate child window
+ if (m_hOldFocus) ::SetFocus(m_hOldFocus);
+
+ // Send a notification to the view window
+ int idCtrl = ::GetDlgCtrlID(m_hOldFocus);
+ NMHDR nhdr={0};
+ nhdr.hwndFrom = m_hOldFocus;
+ nhdr.idFrom = idCtrl;
+ nhdr.code = UWM_FRAMEGOTFOCUS;
+ if (GetView()->IsWindow())
+ GetView()->SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr);
+ }
+ }
+
+ inline void CFrame::OnClose()
+ {
+ // Called in response to a WM_CLOSE message for the frame.
+ ShowWindow(SW_HIDE);
+ SaveRegistrySettings();
+
+ GetMenuBar().Destroy();
+ GetToolBar().Destroy();
+ GetReBar().Destroy();
+ GetStatusBar().Destroy();
+ GetView()->Destroy();
+ }
+
+ inline void CFrame::OnCreate()
+ {
+ // This is called when the frame window is being created.
+ // Override this in CMainFrame if you wish to modify what happens here
+
+ // Set the icon
+ SetIconLarge(IDW_MAIN);
+ SetIconSmall(IDW_MAIN);
+
+ // Set the keyboard accelerators
+ m_hAccel = LoadAccelerators(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_MAIN));
+ GetApp()->SetAccelerators(m_hAccel, this);
+
+ // Set the Caption
+ SetWindowText(LoadString(IDW_MAIN));
+
+ // Set the theme for the frame elements
+ SetTheme();
+
+ // Create the rebar and menubar
+ if (IsReBarSupported() && m_bUseReBar)
+ {
+ // Create the rebar
+ GetReBar().Create(this);
+
+ // Create the menu inside rebar
+ GetMenuBar().Create(&GetReBar());
+ AddMenuBarBand();
+ }
+
+ // Setup the menu
+ SetFrameMenu(IDW_MAIN);
+ UpdateMRUMenu();
+
+ // Create the ToolBar
+ if (m_bUseToolBar)
+ {
+ CreateToolBar();
+ ShowToolBar(m_bShowToolBar);
+ }
+ else
+ {
+ ::CheckMenuItem(GetFrameMenu(), IDW_VIEW_TOOLBAR, MF_UNCHECKED);
+ ::EnableMenuItem(GetFrameMenu(), IDW_VIEW_TOOLBAR, MF_GRAYED);
+ }
+
+ // Create the status bar
+ GetStatusBar().Create(this);
+ ShowStatusBar(m_bShowStatusBar);
+
+ // Create the view window
+ assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window
+ GetView()->Create(this);
+
+ // Disable XP themes for the menubar
+ if ( m_bUseThemes || (GetWinVersion() < 2600) ) // themes or WinVersion < Vista
+ GetMenuBar().SetWindowTheme(L" ", L" ");
+
+ // Start timer for Status updates
+ if (m_bShowIndicatorStatus || m_bShowMenuStatus)
+ SetTimer(ID_STATUS_TIMER, 200, NULL);
+
+ // Reposition the child windows
+ RecalcLayout();
+ }
+
+ inline void CFrame::OnDestroy()
+ {
+ SetMenu(NULL);
+ KillTimer(ID_STATUS_TIMER);
+ ::PostQuitMessage(0); // Terminates the application
+ }
+
+ inline LRESULT CFrame::OnDrawItem(WPARAM wParam, LPARAM lParam)
+ // OwnerDraw is used to render the popup menu items
+ {
+ LPDRAWITEMSTRUCT pdis = (LPDRAWITEMSTRUCT) lParam;
+ if (pdis->CtlType != ODT_MENU)
+ return CWnd::WndProcDefault(WM_DRAWITEM, wParam, lParam);
+
+ CRect rc = pdis->rcItem;
+ ItemData* pmd = (ItemData*)pdis->itemData;
+ CDC* pDrawDC = FromHandle(pdis->hDC);
+ MenuTheme tm = GetMenuTheme();
+
+ int Iconx = 16;
+ int Icony = 16;
+ if (m_himlMenu) ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony);
+ int BarWidth = tm.UseThemes? Iconx + 8 : 0;
+
+ // Draw the side bar
+ if (tm.UseThemes)
+ {
+ CRect rcBar = rc;
+ rcBar.right = BarWidth;
+ pDrawDC->GradientFill(tm.clrPressed1, tm.clrPressed2, rcBar, TRUE);
+ }
+
+ if (pmd->fType & MFT_SEPARATOR)
+ {
+ // draw separator
+ CRect rcSep = rc;
+ rcSep.left = BarWidth;
+ if (tm.UseThemes)
+ pDrawDC->SolidFill(RGB(255,255,255), rcSep);
+ else
+ pDrawDC->SolidFill(GetSysColor(COLOR_MENU), rcSep);
+ rcSep.top += (rc.bottom - rc.top)/2;
+ rcSep.left = BarWidth + 2;
+ pDrawDC->DrawEdge(rcSep, EDGE_ETCHED, BF_TOP);
+ }
+ else
+ {
+ // draw menu item
+ BOOL bDisabled = pdis->itemState & ODS_GRAYED;
+ BOOL bSelected = pdis->itemState & ODS_SELECTED;
+ BOOL bChecked = pdis->itemState & ODS_CHECKED;
+ CRect rcDraw = rc;
+
+ if ((bSelected) && (!bDisabled))
+ {
+ // draw selected item background
+ if (tm.UseThemes)
+ {
+ pDrawDC->CreateSolidBrush(tm.clrHot1);
+ pDrawDC->CreatePen(PS_SOLID, 1, tm.clrOutline);
+ pDrawDC->Rectangle(rcDraw.left, rcDraw.top, rcDraw.right, rcDraw.bottom);
+ }
+ else
+ pDrawDC->SolidFill(GetSysColor(COLOR_HIGHLIGHT), rcDraw);
+ }
+ else
+ {
+ // draw non-selected item background
+ rcDraw.left = BarWidth;
+ if (tm.UseThemes)
+ pDrawDC->SolidFill(RGB(255,255,255), rcDraw);
+ else
+ pDrawDC->SolidFill(GetSysColor(COLOR_MENU), rcDraw);
+ }
+
+ if (bChecked)
+ DrawCheckmark(pdis, *pDrawDC);
+ else
+ DrawMenuIcon(pdis, *pDrawDC, bDisabled);
+
+ // Calculate the text rect size
+ rc.left = rc.bottom - rc.top + 2;
+ if (_tcschr(pmd->GetItemText(), _T('\t')))
+ rc.right -= POST_TEXT_GAP; // Add POST_TEXT_GAP if the text includes a tab
+
+ // Draw the text
+ int iMode = pDrawDC->SetBkMode(TRANSPARENT);
+ COLORREF colorText;
+ if (tm.UseThemes)
+ {
+ rc.left += 8;
+ colorText = GetSysColor(bDisabled ? COLOR_GRAYTEXT : COLOR_MENUTEXT);
+ }
+ else
+ colorText = GetSysColor(bDisabled ? COLOR_GRAYTEXT : bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
+
+ DrawMenuText(*pDrawDC, pmd->GetItemText(), rc, colorText);
+ pDrawDC->SetBkMode(iMode);
+ }
+
+ pDrawDC->Detach(); // Optional, deletes GDI objects sooner
+ return TRUE;
+ }
+
+ inline void CFrame::OnExitMenuLoop()
+ {
+ if (m_bUseCustomDraw)
+ {
+ for (UINT nItem = 0; nItem < m_vMenuItemData.size(); ++nItem)
+ {
+ // Undo OwnerDraw and put the text back
+ MENUITEMINFO mii = {0};
+ mii.cbSize = GetSizeofMenuItemInfo();
+
+ mii.fMask = MIIM_TYPE | MIIM_DATA;
+ mii.fType = m_vMenuItemData[nItem]->fType;
+ mii.dwTypeData = m_vMenuItemData[nItem]->GetItemText();
+ mii.cch = lstrlen(m_vMenuItemData[nItem]->GetItemText());
+ mii.dwItemData = 0;
+ ::SetMenuItemInfo(m_vMenuItemData[nItem]->hMenu, m_vMenuItemData[nItem]->nPos, TRUE, &mii);
+ }
+
+ m_vMenuItemData.clear();
+ }
+ }
+
+ inline void CFrame::OnHelp()
+ {
+ // Ensure only one dialog displayed even for multiple hits of the F1 button
+ if (!m_AboutDialog.IsWindow())
+ {
+ // Store the window handle that currently has keyboard focus
+ HWND hPrevFocus = ::GetFocus();
+ if (hPrevFocus == GetMenuBar())
+ hPrevFocus = m_hWnd;
+
+ m_AboutDialog.SetDlgParent(this);
+ m_AboutDialog.DoModal();
+
+ ::SetFocus(hPrevFocus);
+ }
+ }
+
+ inline void CFrame::OnInitMenuPopup(WPARAM wParam, LPARAM lParam)
+ {
+ // The system menu shouldn't be owner drawn
+ if (HIWORD(lParam)) return;
+
+ if (m_bUseCustomDraw)
+ {
+ CMenu* pMenu = FromHandle((HMENU)wParam);
+
+ for (UINT i = 0; i < pMenu->GetMenuItemCount(); ++i)
+ {
+ MENUITEMINFO mii = {0};
+ mii.cbSize = GetSizeofMenuItemInfo();
+
+ TCHAR szMenuItem[MAX_MENU_STRING] = _T("");
+
+ // Use old fashioned MIIM_TYPE instead of MIIM_FTYPE for MS VC6 compatibility
+ mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_SUBMENU;
+ mii.dwTypeData = szMenuItem;
+ mii.cch = MAX_MENU_STRING -1;
+
+ // Send message for menu updates
+ UINT menuItem = pMenu->GetMenuItemID(i);
+ SendMessage(UWM_UPDATE_COMMAND, (WPARAM)menuItem, 0);
+
+ // Specify owner-draw for the menu item type
+ if (pMenu->GetMenuItemInfo(i, &mii, TRUE))
+ {
+ if (0 == mii.dwItemData)
+ {
+ ItemData* pItem = new ItemData; // deleted in OnExitMenuLoop
+ pItem->hMenu = *pMenu;
+ pItem->nPos = i;
+ pItem->fType = mii.fType;
+ pItem->hSubMenu = mii.hSubMenu;
+ mii.fType |= MFT_OWNERDRAW;
+ lstrcpyn(pItem->GetItemText(), szMenuItem, MAX_MENU_STRING);
+ mii.dwItemData = (DWORD_PTR)pItem;
+
+ m_vMenuItemData.push_back(ItemDataPtr(pItem)); // Store pItem in m_vMenuItemData
+ pMenu->SetMenuItemInfo(i, &mii, TRUE); // Store pItem in mii
+ }
+ }
+ }
+ }
+ }
+
+ inline LRESULT CFrame::OnMeasureItem(WPARAM wParam, LPARAM lParam)
+ // Called before the Popup menu is displayed, so that the MEASUREITEMSTRUCT
+ // values can be assigned with the menu item's dimensions.
+ {
+ LPMEASUREITEMSTRUCT pmis = (LPMEASUREITEMSTRUCT) lParam;
+ if (pmis->CtlType != ODT_MENU)
+ return CWnd::WndProcDefault(WM_MEASUREITEM, wParam, lParam);
+
+ ItemData* pmd = (ItemData *) pmis->itemData;
+ assert(::IsMenu(pmd->hMenu)); // Does itemData contain a valid ItemData struct?
+ MenuTheme tm = GetMenuTheme();
+
+ if (pmd->fType & MFT_SEPARATOR)
+ {
+ pmis->itemHeight = 7;
+ pmis->itemWidth = 0;
+ }
+
+ else
+ {
+ CClientDC DesktopDC(NULL);
+
+ // Get the font used in menu items
+ NONCLIENTMETRICS nm = {0};
+ nm.cbSize = GetSizeofNonClientMetrics();
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nm), &nm, 0);
+ // Default menu items are bold, so take this into account
+ if ((int)::GetMenuDefaultItem(pmd->hMenu, TRUE, GMDI_USEDISABLED) != -1)
+ nm.lfMenuFont.lfWeight = FW_BOLD;
+
+ TCHAR* pItemText = &(pmd->vItemText[0]);
+ DesktopDC.CreateFontIndirect(&nm.lfMenuFont);
+
+ // Calculate the size of the text
+ CSize size = DesktopDC.GetTextExtentPoint32(pItemText, lstrlen(pItemText));
+
+ // Calculate the size of the icon
+ int Iconx = 16;
+ int Icony = 16;
+ if (m_himlMenu) ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony);
+
+ pmis->itemHeight = 2+ MAX(MAX(size.cy, GetSystemMetrics(SM_CYMENU)-2), Icony+2);
+ pmis->itemWidth = size.cx + MAX(::GetSystemMetrics(SM_CXMENUSIZE), Iconx+2);
+
+ // Allow extra width if the text includes a tab
+ if (_tcschr(pItemText, _T('\t')))
+ pmis->itemWidth += POST_TEXT_GAP;
+
+ // Allow extra width if the menu item has a sub menu
+ if (pmd->hSubMenu)
+ pmis->itemWidth += 10;
+
+ // Allow extra width for themed menu
+ if (tm.UseThemes)
+ pmis->itemWidth += 8;
+ }
+ return TRUE;
+ }
+
+ inline LRESULT CFrame::OnMenuChar(WPARAM wParam, LPARAM lParam)
+ {
+ if ((IsMenuBarUsed()) && (LOWORD(wParam)!= VK_SPACE))
+ {
+ // Activate MenuBar for key pressed with Alt key held down
+ GetMenuBar().OnMenuChar(wParam, lParam);
+ return -1L;
+ }
+ return CWnd::WndProcDefault(WM_MENUCHAR, wParam, lParam);
+ }
+
+ inline void CFrame::OnMenuSelect(WPARAM wParam, LPARAM lParam)
+ {
+ // Set the StatusBar text when we hover over a menu
+ // Only popup submenus have status strings
+ if (m_bShowMenuStatus)
+ {
+ int nID = LOWORD (wParam);
+ CMenu* pMenu = FromHandle((HMENU) lParam);
+
+ if ((pMenu != GetMenu()) && (nID != 0) && !(HIWORD(wParam) & MF_POPUP))
+ m_tsStatusText = LoadString(nID);
+ else
+ m_tsStatusText = _T("Ready");
+
+ SetStatusText();
+ }
+ }
+
+ inline LRESULT CFrame::OnNotify(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case UWM_UNDOCKED:
+ m_hOldFocus = 0;
+ break;
+ case RBN_HEIGHTCHANGE:
+ RecalcLayout();
+ Invalidate();
+ break;
+ // case RBN_LAYOUTCHANGED:
+ // if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft)
+ // GetReBar().MoveBandsLeft();
+ // break;
+ case RBN_MINMAX:
+ if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().ShortBands)
+ return 1L; // Supress maximise or minimise rebar band
+ break;
+
+ // Display tooltips for the toolbar
+ case TTN_GETDISPINFO:
+ if (GetToolBar().IsWindow())
+ {
+ CToolBar* pToolBar = 0;
+ if (IsReBarUsed())
+ {
+ // Get the ToolBar's CWnd
+ CWnd* pWnd = FromHandle(GetReBar().HitTest(GetCursorPos()));
+ if (pWnd && (lstrcmp(pWnd->GetClassName(), _T("ToolbarWindow32")) == 0))
+ {
+ pToolBar = (CToolBar*)pWnd;
+ }
+ }
+
+ if (pToolBar)
+ {
+ LPNMTTDISPINFO lpDispInfo = (LPNMTTDISPINFO)lParam;
+ int iIndex = pToolBar->HitTest();
+ if (iIndex >= 0)
+ {
+ int nID = pToolBar->GetCommandID(iIndex);
+ if (nID > 0)
+ {
+ m_tsTooltip = LoadString(nID);
+ lpDispInfo->lpszText = (LPTSTR)m_tsTooltip.c_str();
+ }
+ else
+ m_tsTooltip = _T("");
+ }
+ }
+ }
+ break;
+ } // switch LPNMHDR
+
+ return 0L;
+
+ } // CFrame::Onotify(...)
+
+ inline void CFrame::OnSetFocus()
+ {
+ SetStatusText();
+ }
+
+ inline void CFrame::OnSysColorChange()
+ {
+ // Honour theme color changes
+ for (int nBand = 0; nBand <= GetReBar().GetBandCount(); ++nBand)
+ {
+ GetReBar().SetBandColor(nBand, GetSysColor(COLOR_BTNTEXT), GetSysColor(COLOR_BTNFACE));
+ }
+
+ // Update the status bar font and text
+ NONCLIENTMETRICS nm = {0};
+ nm.cbSize = GetSizeofNonClientMetrics();
+ SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &nm, 0);
+ LOGFONT lf = nm.lfStatusFont;
+ CFont* pFont = FromHandle(CreateFontIndirect(&lf));
+ GetStatusBar().SetFont(pFont, FALSE);
+ SetStatusText();
+
+ if ((m_bUpdateTheme) && (m_bUseThemes)) SetTheme();
+
+ // Reposition and redraw everything
+ RecalcLayout();
+ RedrawWindow(NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
+
+ // Forward the message to the view window
+ m_pView->PostMessage(WM_SYSCOLORCHANGE, 0L, 0L);
+ }
+
+ inline LRESULT CFrame::OnSysCommand(WPARAM wParam, LPARAM lParam)
+ {
+ if ((SC_KEYMENU == wParam) && (VK_SPACE != lParam) && IsMenuBarUsed())
+ {
+ GetMenuBar().OnSysCommand(wParam, lParam);
+ return 0L;
+ }
+
+ if (SC_MINIMIZE == wParam)
+ m_hOldFocus = ::GetFocus();
+
+ return CWnd::WndProcDefault(WM_SYSCOMMAND, wParam, lParam);
+ }
+
+ inline void CFrame::OnTimer(WPARAM wParam)
+ {
+ if (ID_STATUS_TIMER == wParam)
+ {
+ if (m_bShowMenuStatus)
+ {
+ // Get the toolbar the point is over
+ CToolBar* pToolBar = 0;
+ if (IsReBarUsed())
+ {
+ // Get the ToolBar's CWnd
+ CWnd* pWnd = FromHandle(GetReBar().HitTest(GetCursorPos()));
+ if (pWnd && (dynamic_cast<CToolBar*>(pWnd)) && !(dynamic_cast<CMenuBar*>(pWnd)))
+ pToolBar = (CToolBar*)pWnd;
+ }
+ else
+ {
+ CPoint pt = GetCursorPos();
+ CWnd* pWnd = WindowFromPoint(GetCursorPos());
+ if (pWnd && (dynamic_cast<CToolBar*>(pWnd)))
+ pToolBar = (CToolBar*)pWnd;
+ }
+
+ if ((pToolBar) && (WindowFromPoint(GetCursorPos()) == pToolBar))
+ {
+ // Which toolbar button is the mouse cursor hovering over?
+ int nButton = pToolBar->HitTest();
+ if (nButton >= 0)
+ {
+ int nID = pToolBar->GetCommandID(nButton);
+ // Only update the statusbar if things have changed
+ if (nID != m_nOldID)
+ {
+ if (nID != 0)
+ m_tsStatusText = LoadString(nID);
+ else
+ m_tsStatusText = _T("Ready");
+
+ if (GetStatusBar().IsWindow())
+ SetStatusText();
+ }
+ m_nOldID = nID;
+ }
+ }
+ else
+ {
+ if (m_nOldID != -1)
+ {
+ m_tsStatusText = _T("Ready");
+ SetStatusText();
+ }
+ m_nOldID = -1;
+ }
+ }
+
+ if (m_bShowIndicatorStatus)
+ SetStatusIndicators();
+ }
+ }
+
+ inline void CFrame::OnViewStatusBar()
+ {
+ m_bShowStatusBar = !m_bShowStatusBar;
+ ShowStatusBar(m_bShowStatusBar);
+ }
+
+ inline void CFrame::OnViewToolBar()
+ {
+ m_bShowToolBar = !m_bShowToolBar;
+ ShowToolBar(m_bShowToolBar);
+ }
+
+ inline void CFrame::PreCreate(CREATESTRUCT& cs)
+ {
+ // Set the frame window styles
+ cs.style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
+
+ // Set the original window position
+ cs.x = m_rcPosition.left;
+ cs.y = m_rcPosition.top;
+ cs.cx = m_rcPosition.Width();
+ cs.cy = m_rcPosition.Height();
+ }
+
+ inline void CFrame::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = _T("Win32++ Frame");
+ }
+
+ inline void CFrame::RecalcLayout()
+ {
+ CWnd* pView = GetView();
+ if ((!pView) || (!pView->GetHwnd()))
+ return;
+
+ // Resize the status bar
+ if (GetStatusBar().IsWindow() && m_bShowStatusBar)
+ {
+ GetStatusBar().SetWindowPos(NULL, 0, 0, 0, 0, SWP_SHOWWINDOW);
+ GetStatusBar().Invalidate();
+ SetStatusText();
+ }
+
+ // Resize the rebar or toolbar
+ if (IsReBarUsed())
+ {
+ GetReBar().SendMessage(WM_SIZE, 0L, 0L);
+ GetReBar().Invalidate();
+ }
+ else if (m_bUseToolBar && m_bShowToolBar)
+ GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L);
+
+ // Resize the View window
+ CRect rClient = GetViewRect();
+ if ((rClient.bottom - rClient.top) >= 0)
+ {
+ int x = rClient.left;
+ int y = rClient.top;
+ int cx = rClient.Width();
+ int cy = rClient.Height();
+
+ pView->SetWindowPos( NULL, x, y, cx, cy, SWP_SHOWWINDOW|SWP_ASYNCWINDOWPOS );
+ }
+
+ // Adjust rebar bands
+ if (IsReBarUsed())
+ {
+ if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft)
+ GetReBar().MoveBandsLeft();
+
+ if (IsMenuBarUsed())
+ SetMenuBarBandSize();
+ }
+ }
+
+ inline void CFrame::RemoveMRUEntry(LPCTSTR szMRUEntry)
+ {
+ std::vector<tString>::iterator it;
+ for (it = m_vMRUEntries.begin(); it != m_vMRUEntries.end(); ++it)
+ {
+ if ((*it) == szMRUEntry)
+ {
+ m_vMRUEntries.erase(it);
+ break;
+ }
+ }
+
+ UpdateMRUMenu();
+ }
+
+ inline BOOL CFrame::SaveRegistrySettings()
+ {
+ // Store the window position in the registry
+ if (!m_tsKeyName.empty())
+ {
+ tString tsKeyName = _T("Software\\") + m_tsKeyName + _T("\\Frame Settings");
+ HKEY hKey = NULL;
+
+ try
+ {
+ if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL))
+ throw CWinException(_T("RegCreateKeyEx failed"));
+
+ WINDOWPLACEMENT Wndpl = {0};
+ Wndpl.length = sizeof(WINDOWPLACEMENT);
+
+ if (GetWindowPlacement(Wndpl))
+ {
+ // Get the Frame's window position
+ CRect rc = Wndpl.rcNormalPosition;
+ DWORD dwTop = MAX(rc.top, 0);
+ DWORD dwLeft = MAX(rc.left, 0);
+ DWORD dwWidth = MAX(rc.Width(), 100);
+ DWORD dwHeight = MAX(rc.Height(), 50);
+
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Top"), 0, REG_DWORD, (LPBYTE)&dwTop, sizeof(DWORD)))
+ throw CWinException(_T("RegSetValueEx failed"));
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Left"), 0, REG_DWORD, (LPBYTE)&dwLeft, sizeof(DWORD)))
+ throw CWinException(_T("RegSetValueEx failed"));
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Width"), 0, REG_DWORD, (LPBYTE)&dwWidth, sizeof(DWORD)))
+ throw CWinException(_T("RegSetValueEx failed"));
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Height"), 0, REG_DWORD, (LPBYTE)&dwHeight, sizeof(DWORD)))
+ throw CWinException(_T("RegSetValueEx failed"));
+ }
+
+ // Store the ToolBar and statusbar states
+ DWORD dwShowToolBar = m_bShowToolBar;
+ DWORD dwShowStatusBar = m_bShowStatusBar;
+
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("ToolBar"), 0, REG_DWORD, (LPBYTE)&dwShowToolBar, sizeof(DWORD)))
+ throw CWinException(_T("RegSetValueEx failed"));
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("StatusBar"), 0, REG_DWORD, (LPBYTE)&dwShowStatusBar, sizeof(DWORD)))
+ throw CWinException(_T("RegSetValueEx failed"));
+
+ RegCloseKey(hKey);
+ }
+
+ catch (const CWinException& e)
+ {
+ TRACE(_T("Failed to save registry settings\n"));
+
+ if (hKey)
+ {
+ // Roll back the registry changes by deleting this subkey
+ RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str());
+ RegCloseKey(hKey);
+ }
+
+ e.what();
+ return FALSE;
+ }
+
+ // Store the MRU entries in the registry
+ if (m_nMaxMRU > 0)
+ {
+ tString tsKeyName = _T("Software\\") + m_tsKeyName + _T("\\Recent Files");
+ HKEY hKey = NULL;
+
+ try
+ {
+ if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL))
+ throw CWinException(_T("RegCreateKeyEx failed"));
+
+ for (UINT i = 0; i < m_nMaxMRU; ++i)
+ {
+ TCHAR szSubKey[10];
+ wsprintf(szSubKey, _T("File %d\0"), i+1);
+ tString tsPathName;
+ if (i < m_vMRUEntries.size())
+ tsPathName = m_vMRUEntries[i];
+
+ if (ERROR_SUCCESS != RegSetValueEx(hKey, szSubKey, 0, REG_SZ, (LPBYTE)tsPathName.c_str(), (1 + lstrlen(tsPathName.c_str()))*sizeof(TCHAR)))
+ throw CWinException(_T("RegSetValueEx failed"));
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ catch (const CWinException& e)
+ {
+ TRACE(_T("Failed to save registry MRU settings\n"));
+
+ if (hKey)
+ {
+ // Roll back the registry changes by deleting this subkey
+ RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str());
+ RegCloseKey(hKey);
+ }
+
+ e.what();
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+ }
+
+ inline void CFrame::SetFrameMenu(INT ID_MENU)
+ {
+ // Sets the frame's menu from a Resouce ID.
+ // A resource ID of 0 removes the menu from the frame.
+ HMENU hMenu = 0;
+ if (ID_MENU != 0)
+ {
+ // Sets the frame's menu from a resource ID.
+ hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(ID_MENU));
+ assert (hMenu);
+ }
+
+ SetFrameMenu(hMenu);
+ }
+
+ inline void CFrame::SetFrameMenu(HMENU hMenu)
+ {
+ // Sets the frame's menu from a HMENU.
+ m_Menu.Attach(hMenu);
+
+ if (IsMenuBarUsed())
+ {
+ GetMenuBar().SetMenu(GetFrameMenu());
+ BOOL bShow = (hMenu != NULL); // boolean expression
+ ShowMenu(bShow);
+ }
+ else
+ SetMenu(&m_Menu);
+ }
+
+ inline UINT CFrame::SetMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID)
+ {
+ // Remove any existing menu icons
+ if (m_himlMenu) ImageList_Destroy(m_himlMenu);
+ if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis);
+ m_himlMenu = NULL;
+ m_himlMenuDis = NULL;
+ m_vMenuIcons.clear();
+
+ // Exit if no ToolBarID is specified
+ if (ToolBarID == 0) return 0;
+
+ // Add the menu icons from the bitmap IDs
+ return AddMenuIcons(MenuData, crMask, ToolBarID, ToolBarDisabledID);
+ }
+
+ inline void CFrame::SetMenuBarBandSize()
+ {
+ // Sets the minimum width of the MenuBar band to the width of the rebar
+ // This prevents other bands from moving to this MenuBar's row.
+
+ CRect rcClient = GetClientRect();
+ CReBar& RB = GetReBar();
+ int nBand = RB.GetBand(GetMenuBar());
+ CRect rcBorder = RB.GetBandBorders(nBand);
+
+ REBARBANDINFO rbbi = {0};
+ rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_SIZE;
+ RB.GetBandInfo(nBand, rbbi);
+
+ int Width;
+ if ((GetReBar().GetReBarTheme().UseThemes) && (GetReBar().GetReBarTheme().LockMenuBand))
+ Width = rcClient.Width() - rcBorder.Width() - 2;
+ else
+ Width = GetMenuBar().GetMaxSize().cx;
+
+ rbbi.cxMinChild = Width;
+ rbbi.cx = Width;
+
+ RB.SetBandInfo(nBand, rbbi);
+ }
+
+ inline void CFrame::SetMenuTheme(MenuTheme& Theme)
+ {
+ m_ThemeMenu.UseThemes = Theme.UseThemes;
+ m_ThemeMenu.clrHot1 = Theme.clrHot1;
+ m_ThemeMenu.clrHot2 = Theme.clrHot2;
+ m_ThemeMenu.clrPressed1 = Theme.clrPressed1;
+ m_ThemeMenu.clrPressed2 = Theme.clrPressed2;
+ m_ThemeMenu.clrOutline = Theme.clrOutline;
+
+ GetMenuBar().SetMenuBarTheme(Theme); // Sets the theme for MenuBar buttons
+ Invalidate();
+ }
+
+ inline void CFrame::SetStatusIndicators()
+ {
+ if (::IsWindow(GetStatusBar()))
+ {
+ LPCTSTR Status1 = (::GetKeyState(VK_CAPITAL) & 0x0001)? _T("\tCAP") : _T("");
+ LPCTSTR Status2 = (::GetKeyState(VK_NUMLOCK) & 0x0001)? _T("\tNUM") : _T("");
+ LPCTSTR Status3 = (::GetKeyState(VK_SCROLL) & 0x0001)? _T("\tSCRL"): _T("");
+
+ // Only update indictors if the text has changed
+ if (Status1 != m_OldStatus[0]) GetStatusBar().SetPartText(1, (Status1));
+ if (Status2 != m_OldStatus[1]) GetStatusBar().SetPartText(2, (Status2));
+ if (Status3 != m_OldStatus[2]) GetStatusBar().SetPartText(3, (Status3));
+
+ m_OldStatus[0] = Status1;
+ m_OldStatus[1] = Status2;
+ m_OldStatus[2] = Status3;
+ }
+ }
+
+ inline void CFrame::SetStatusText()
+ {
+ if (::IsWindow(GetStatusBar()))
+ {
+ // Calculate the width of the text indicators
+ CClientDC dcStatus(&GetStatusBar());
+ CSize csCAP = dcStatus.GetTextExtentPoint32(_T("\tCAP"), lstrlen(_T("\tCAP")));
+ CSize csNUM = dcStatus.GetTextExtentPoint32(_T("\tNUM"), lstrlen(_T("\tNUM")));
+ CSize csSCRL = dcStatus.GetTextExtentPoint32(_T("\tSCRL "), lstrlen(_T("\tSCRL ")));
+
+ // Get the coordinates of the parent window's client area.
+ CRect rcClient = GetClientRect();
+ int width = MAX(300, rcClient.right);
+
+ if (m_bShowIndicatorStatus)
+ {
+ // Create 4 panes
+ GetStatusBar().SetPartWidth(0, width - (csCAP.cx+csNUM.cx+csSCRL.cx+20));
+ GetStatusBar().SetPartWidth(1, csCAP.cx);
+ GetStatusBar().SetPartWidth(2, csNUM.cx);
+ GetStatusBar().SetPartWidth(3, csSCRL.cx);
+
+ SetStatusIndicators();
+ }
+
+ // Place text in the 1st pane
+ GetStatusBar().SetPartText(0, m_tsStatusText.c_str());
+ }
+ }
+
+ inline void CFrame::SetTheme()
+ {
+ // Note: To modify theme colors, override this function in CMainframe,
+ // and make any modifications there.
+
+ // Avoid themes if using less than 16 bit colors
+ CClientDC DesktopDC(NULL);
+ if (DesktopDC.GetDeviceCaps(BITSPIXEL) < 16)
+ m_bUseThemes = FALSE;
+
+ BOOL T = TRUE;
+ BOOL F = FALSE;
+
+ if (m_bUseThemes)
+ {
+ // Set a flag redo SetTheme when the theme changes
+ m_bUpdateTheme = TRUE;
+
+ // Detect the XP theme name
+ WCHAR Name[30] = L"";
+ HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll"));
+ if(hMod)
+ {
+ typedef HRESULT (__stdcall *PFNGETCURRENTTHEMENAME)(LPWSTR pszThemeFileName, int cchMaxNameChars,
+ LPWSTR pszColorBuff, int cchMaxColorChars, LPWSTR pszSizeBuff, int cchMaxSizeChars);
+
+ PFNGETCURRENTTHEMENAME pfn = (PFNGETCURRENTTHEMENAME)GetProcAddress(hMod, "GetCurrentThemeName");
+
+ (*pfn)(0, 0, Name, 30, 0, 0);
+
+ ::FreeLibrary(hMod);
+ }
+
+ enum Themetype{ Modern, Grey, Blue, Silver, Olive };
+
+ int Theme = Grey;
+ if (GetWinVersion() < 2600) // Not for Vista and above
+ {
+ if (0 == wcscmp(L"NormalColor", Name)) Theme = Blue;
+ if (0 == wcscmp(L"Metallic", Name)) Theme = Silver;
+ if (0 == wcscmp(L"HomeStead", Name)) Theme = Olive;
+ }
+ else
+ Theme = Modern;
+
+ switch (Theme)
+ {
+ case Modern:
+ {
+ ToolBarTheme tt = {T, RGB(180, 250, 255), RGB(140, 190, 255), RGB(150, 220, 255), RGB(80, 100, 255), RGB(127, 127, 255)};
+ ReBarTheme tr = {T, RGB(220, 225, 250), RGB(240, 242, 250), RGB(240, 240, 250), RGB(180, 200, 230), F, T, T, T, T, F};
+ MenuTheme tm = {T, RGB(180, 250, 255), RGB(140, 190, 255), RGB(240, 250, 255), RGB(120, 170, 220), RGB(127, 127, 255)};
+
+ GetToolBar().SetToolBarTheme(tt);
+ SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar
+
+ GetReBar().SetReBarTheme(tr);
+ }
+ break;
+
+ case Grey: // A color scheme suitable for 16 bit colors. Suitable for Windows older than XP.
+ {
+ ToolBarTheme tt = {T, RGB(182, 189, 210), RGB(182, 189, 210), RGB(133, 146, 181), RGB(133, 146, 181), RGB(10, 36, 106)};
+ ReBarTheme tr = {T, RGB(212, 208, 200), RGB(212, 208, 200), RGB(230, 226, 222), RGB(220, 218, 208), F, T, T, T, T, F};
+ MenuTheme tm = {T, RGB(182, 189, 210), RGB( 182, 189, 210), RGB(200, 196, 190), RGB(200, 196, 190), RGB(100, 100, 100)};
+
+ GetToolBar().SetToolBarTheme(tt);
+ SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar
+
+ GetReBar().SetReBarTheme(tr);
+ }
+ break;
+ case Blue:
+ {
+ // Used for XP default (blue) color scheme
+ ToolBarTheme tt = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(255, 140, 40), RGB(255, 180, 80), RGB(192, 128, 255)};
+ ReBarTheme tr = {T, RGB(150,190,245), RGB(196,215,250), RGB(220,230,250), RGB( 70,130,220), F, T, T, T, T, F};
+ MenuTheme tm = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(220,230,250), RGB(150,190,245), RGB(128, 128, 200)};
+
+ GetToolBar().SetToolBarTheme(tt);
+ SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar
+
+ GetReBar().SetReBarTheme(tr);
+ }
+ break;
+
+ case Silver:
+ {
+ // Used for XP Silver color scheme
+ ToolBarTheme tt = {T, RGB(192, 210, 238), RGB(192, 210, 238), RGB(152, 181, 226), RGB(152, 181, 226), RGB(49, 106, 197)};
+ ReBarTheme tr = {T, RGB(225, 220, 240), RGB(240, 240, 245), RGB(245, 240, 255), RGB(160, 155, 180), F, T, T, T, T, F};
+ MenuTheme tm = {T, RGB(196, 215, 250), RGB( 120, 180, 220), RGB(240, 240, 245), RGB(170, 165, 185), RGB(128, 128, 150)};
+
+ GetToolBar().SetToolBarTheme(tt);
+ SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar
+
+ GetReBar().SetReBarTheme(tr);
+ }
+ break;
+
+ case Olive:
+ {
+ // Used for XP Olive color scheme
+ ReBarTheme tr = {T, RGB(215, 216, 182), RGB(242, 242, 230), RGB(249, 255, 227), RGB(178, 191, 145), F, T, T, T, T, F};
+ ToolBarTheme tt = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(255, 140, 40), RGB(255, 180, 80), RGB(200, 128, 128)};
+ MenuTheme tm = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(249, 255, 227), RGB(178, 191, 145), RGB(128, 128, 128)};
+
+ GetToolBar().SetToolBarTheme(tt);
+ SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar
+
+ GetReBar().SetReBarTheme(tr);
+ }
+ break;
+ }
+ }
+ else
+ {
+ // Use a classic style by default
+ ReBarTheme tr = {T, 0, 0, 0, 0, F, T, T, F, F, F};
+ GetReBar().SetReBarTheme(tr);
+ }
+
+ RecalcLayout();
+ }
+
+ inline void CFrame::SetToolBarImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID)
+ // Either sets the imagelist or adds/replaces bitmap depending on ComCtl32.dll version
+ // Assumes the width of the button image = bitmap_size / buttons
+ // Assumes buttons have been already been added via AdddToolBarButton
+ // The colour mask is ignored for 32bit bitmaps, but is required for 24bit bitmaps
+ // The colour mask is often grey RGB(192,192,192) or magenta (255,0,255)
+ // The color mask is ignored for 32bit bitmap resources
+ // The Hot and disabled bitmap resources can be 0
+ {
+ GetToolBar().SetImages(crMask, ToolBarID, ToolBarHotID, ToolBarDisabledID);
+ }
+
+ inline void CFrame::SetupToolBar()
+ {
+ // Use this function to set the Resource IDs for the toolbar(s).
+
+/* // Set the Resource IDs for the toolbar buttons
+ AddToolBarButton( IDM_FILE_NEW );
+ AddToolBarButton( IDM_FILE_OPEN );
+ AddToolBarButton( IDM_FILE_SAVE );
+ AddToolBarButton( 0 ); // Separator
+ AddToolBarButton( IDM_EDIT_CUT );
+ AddToolBarButton( IDM_EDIT_COPY );
+ AddToolBarButton( IDM_EDIT_PASTE );
+ AddToolBarButton( 0 ); // Separator
+ AddToolBarButton( IDM_FILE_PRINT );
+ AddToolBarButton( 0 ); // Separator
+ AddToolBarButton( IDM_HELP_ABOUT );
+*/
+ }
+
+ inline void CFrame::SetView(CWnd& wndView)
+ // Sets or changes the View window displayed within the frame
+ {
+ if (m_pView != &wndView)
+ {
+ // Destroy the existing view window (if any)
+ if (m_pView) m_pView->Destroy();
+
+ // Assign the view window
+ m_pView = &wndView;
+
+ if (m_hWnd)
+ {
+ // The frame is already created, so create and position the new view too
+ assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window
+ GetView()->Create(this);
+ RecalcLayout();
+ }
+ }
+ }
+
+ inline void CFrame::ShowMenu(BOOL bShow)
+ {
+ if (bShow)
+ {
+ if (IsReBarUsed())
+ GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetMenuBar()), TRUE);
+ else
+ SetMenu(&m_Menu);
+ }
+ else
+ {
+ if (IsReBarUsed())
+ GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetMenuBar()), FALSE);
+ else
+ SetMenu(NULL);
+ }
+
+ if (GetReBar().IsWindow())
+ {
+ if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft)
+ GetReBar().MoveBandsLeft();
+ }
+
+ // Reposition the Windows
+ RecalcLayout();
+ }
+
+
+
+ inline void CFrame::ShowStatusBar(BOOL bShow)
+ {
+ if (bShow)
+ {
+ m_Menu.CheckMenuItem(IDW_VIEW_STATUSBAR, MF_CHECKED);
+ GetStatusBar().ShowWindow(SW_SHOW);
+ }
+ else
+ {
+ m_Menu.CheckMenuItem(IDW_VIEW_STATUSBAR, MF_UNCHECKED);
+ GetStatusBar().ShowWindow(SW_HIDE);
+ }
+
+ // Reposition the Windows
+ RecalcLayout();
+ }
+
+ inline void CFrame::ShowToolBar(BOOL bShow)
+ {
+ if (bShow)
+ {
+ m_Menu.CheckMenuItem(IDW_VIEW_TOOLBAR, MF_CHECKED);
+ if (IsReBarUsed())
+ GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetToolBar()), TRUE);
+ else
+ GetToolBar().ShowWindow(SW_SHOW);
+ }
+ else
+ {
+ m_Menu.CheckMenuItem(IDW_VIEW_TOOLBAR, MF_UNCHECKED);
+ if (IsReBarUsed())
+ GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetToolBar()), FALSE);
+ else
+ GetToolBar().ShowWindow(SW_HIDE);
+ }
+
+ if (GetReBar().IsWindow())
+ {
+ if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft)
+ GetReBar().MoveBandsLeft();
+ }
+
+ // Reposition the Windows
+ RecalcLayout();
+ }
+
+ inline void CFrame::UpdateMRUMenu()
+ {
+ if (0 >= m_nMaxMRU) return;
+
+ // Set the text for the MRU Menu
+ tString tsMRUArray[16];
+ UINT MaxMRUArrayIndex = 0;
+ if (m_vMRUEntries.size() > 0)
+ {
+ for (UINT n = 0; ((n < m_vMRUEntries.size()) && (n <= m_nMaxMRU)); ++n)
+ {
+ tsMRUArray[n] = m_vMRUEntries[n];
+ if (tsMRUArray[n].length() > MAX_MENU_STRING - 10)
+ {
+ // Truncate the string if its too long
+ tsMRUArray[n].erase(0, tsMRUArray[n].length() - MAX_MENU_STRING +10);
+ tsMRUArray[n] = _T("... ") + tsMRUArray[n];
+ }
+
+ // Prefix the string with its number
+ TCHAR tVal[5];
+ wsprintf(tVal, _T("%d "), n+1);
+ tsMRUArray[n] = tVal + tsMRUArray[n];
+ MaxMRUArrayIndex = n;
+ }
+ }
+ else
+ {
+ tsMRUArray[0] = _T("Recent Files");
+ }
+
+ // Set MRU menu items
+ MENUITEMINFO mii = {0};
+ mii.cbSize = GetSizeofMenuItemInfo();
+
+ int nFileItem = 0; // We place the MRU items under the left most menu item
+ CMenu* pFileMenu = GetFrameMenu().GetSubMenu(nFileItem);
+
+ if (pFileMenu)
+ {
+ // Remove all but the first MRU Menu entry
+ for (UINT u = IDW_FILE_MRU_FILE2; u <= IDW_FILE_MRU_FILE1 +16; ++u)
+ {
+ pFileMenu->DeleteMenu(u, MF_BYCOMMAND);
+ }
+
+ int MaxMRUIndex = (int)MIN(MaxMRUArrayIndex, m_nMaxMRU);
+
+ for (int index = MaxMRUIndex; index >= 0; --index)
+ {
+ mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mii.fState = (0 == m_vMRUEntries.size())? MFS_GRAYED : 0;
+ mii.fType = MFT_STRING;
+ mii.wID = IDW_FILE_MRU_FILE1 + index;
+ mii.dwTypeData = (LPTSTR)tsMRUArray[index].c_str();
+
+ BOOL bResult;
+ if (index == MaxMRUIndex)
+ // Replace the last MRU entry first
+ bResult = pFileMenu->SetMenuItemInfo(IDW_FILE_MRU_FILE1, &mii, FALSE);
+ else
+ // Insert the other MRU entries next
+ bResult = pFileMenu->InsertMenuItem(IDW_FILE_MRU_FILE1 + index + 1, &mii, FALSE);
+
+ if (!bResult)
+ {
+ TRACE(_T("Failed to set MRU menu item\n"));
+ break;
+ }
+ }
+ }
+
+ DrawMenuBar();
+ }
+
+ inline LRESULT CFrame::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_ACTIVATE:
+ OnActivate(wParam, lParam);
+ return 0L;
+ case WM_CLOSE:
+ OnClose();
+ break;
+ case WM_DESTROY:
+ OnDestroy();
+ return 0L;
+ case WM_ERASEBKGND:
+ return 0L;
+ case WM_HELP:
+ OnHelp();
+ return 0L;
+ case WM_MENUCHAR:
+ return OnMenuChar(wParam, lParam);
+ case WM_MENUSELECT:
+ OnMenuSelect(wParam, lParam);
+ return 0L;
+ case WM_SETFOCUS:
+ OnSetFocus();
+ break;
+ case WM_SIZE:
+ RecalcLayout();
+ return 0L;
+ case WM_SYSCOLORCHANGE:
+ // Changing themes trigger this
+ OnSysColorChange();
+ return 0L;
+ case WM_SYSCOMMAND:
+ return OnSysCommand(wParam, lParam);
+ case WM_TIMER:
+ OnTimer(wParam);
+ return 0L;
+ case WM_DRAWITEM:
+ // Owner draw menu items
+ return OnDrawItem(wParam, lParam);
+ case WM_INITMENUPOPUP:
+ OnInitMenuPopup(wParam, lParam);
+ break;
+ case WM_MEASUREITEM:
+ return OnMeasureItem(wParam, lParam);
+ case WM_EXITMENULOOP:
+ OnExitMenuLoop();
+ break;
+ case UWM_GETMENUTHEME:
+ {
+ MenuTheme& tm = GetMenuTheme();
+ return (LRESULT)&tm;
+ }
+ case UWM_GETREBARTHEME:
+ {
+ ReBarTheme& rm = GetReBarTheme();
+ return (LRESULT)&rm;
+ }
+ case UWM_GETTOOLBARTHEME:
+ {
+ ToolBarTheme& tt = GetToolBarTheme();
+ return (LRESULT)&tt;
+ }
+ } // switch uMsg
+
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ } // LRESULT CFrame::WndProcDefault(...)
+
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_FRAME_H_
diff --git a/mmc_updater/depends/win32cpp/gdi.h b/mmc_updater/depends/win32cpp/gdi.h
new file mode 100644
index 00000000..45141f7b
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/gdi.h
@@ -0,0 +1,3944 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// gdi.h
+// Declaration of the CDC class, and CBitmapInfoPtr class
+
+// The CDC class provides a device context, along with the various associated
+// objects such as Bitmaps, Brushes, Bitmaps, Fonts and Pens. This class
+// handles the creation, selection, de-selection and deletion of these objects
+// automatically. It also automatically deletes or releases the device context
+// itself as appropriate. Any failure to create the new GDI object throws an
+// exception.
+//
+// The CDC class is sufficient for most GDI programming needs. Sometimes
+// however we need to have the GDI object seperated from the device context.
+// Wrapper classes for GDI objects are provided for this purpose. The classes
+// are CBitmap, CBrush, CFont, CPalette, CPen and CRgn. These classes
+// automatically delete the GDI resouce assigned to them when their destructor
+// is called. These wrapper class objects can be attached to the CDC as
+// shown below.
+//
+// Coding Exampe without CDC ...
+// void DrawLine()
+// {
+// HDC hdcClient = ::GetDC(m_hWnd);
+// HDC hdcMem = ::CreateCompatibleDC(hdcClient);
+// HBITMAP hBitmap = ::CreateCompatibleBitmap(hdcClient, cx, cy);
+// HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hBitmap);
+// HPEN hPen = ::CreatePen(PS_SOLID, 1, RGB(255,0,0);
+// HPEN hOldPen = (HPEN)::SelectObject(hdcMem, hPen);
+// ::MoveToEx(hdcMem, 0, 0, NULL);
+// ::LineTo(hdcMem, 50, 50);
+// ::BitBlt(hdcClient, 0, 0, cx, cy, hdcMem, 0, 0);
+// ::SelectObject(hdcMem, hOldPen);
+// ::DeleteObject(hPen);
+// ::SelectObject(hdcMem, hOldBitmap);
+// ::DeleteObject(hBitmap);
+// ::DeleteDC(hdcMem);
+// ::ReleaseDC(m_hWnd, hdcClient);
+// }
+//
+// Coding Example with CDC classes ...
+// void DrawLine()
+// {
+// CClientDC dcClient(this)
+// CMemDC dcMem(&dcClient);
+// CBitmap* pOldBitmap = dcMem.CreateCompatibleBitmap(&dcClient, cx, cy);
+// CPen* pOldPen = CMemDC.CreatePen(PS_SOLID, 1, RGB(255,0,0);
+// CMemDC.MoveTo(0, 0);
+// CMemDC.LineTo(50, 50);
+// dcClient.BitBlt(0, 0, cx, cy, &CMemDC, 0, 0);
+// }
+//
+// Coding Example with CDC classes and CPen ...
+// void DrawLine()
+// {
+// CClientDC dcClient(this)
+// CMemDC CMemDC(&dcClient);
+// CBitmap* pOldBitmap = dcMem.CreateCompatibleBitmap(&dcClient, cx, cy);
+// CPen MyPen(PS_SOLID, 1, RGB(255,0,0));
+// CPen* pOldPen = CMemDC.SelectObject(&MyPen);
+// CMemDC.MoveTo(0, 0);
+// CMemDC.LineTo(50, 50);
+// dcClient.BitBlt(0, 0, cx, cy, &CMemDC, 0, 0);
+// }
+
+// Notes:
+// * When the CDC object drops out of scope, it's destructor is called, releasing
+// or deleting the device context as appropriate.
+// * When the destructor for CBitmap, CBrush, CPalette, CPen and CRgn are called,
+// the destructor is called deleting their GDI object.
+// * When the CDC object' destructor is called, any GDI objects created by one of
+// the CDC member functions (CDC::CreatePen for example) will be deleted.
+// * Bitmaps can only be selected into one device context at a time.
+// * Palettes use SelectPalatte to select them into device the context.
+// * Regions use SelectClipRgn to select them into the device context.
+// * The FromHandle function can be used to convert a GDI handle (HDC, HPEN,
+// HBITMAP etc) to a pointer of the appropriate GDI class (CDC, CPen CBitmap etc).
+// The FromHandle function creates a temporary object unless the HANDLE is already
+// assigned to a GDI class. Temporary objects don't delete their GDI object when
+// their destructor is called.
+// * All the GDI classes are reference counted. This allows functions to safely
+// pass these objects by value, as well as by pointer or by reference.
+
+// The CBitmapInfoPtr class is a convienient wrapper for the BITMAPINFO structure.
+// The size of the BITMAPINFO structure is dependant on the type of HBITMAP, and its
+// space needs to be allocated dynamically. CBitmapInfoPtr automatically allocates
+// and deallocates the memory for the structure. A CBitmapInfoPtr object can be
+// used anywhere in place of a LPBITMAPINFO. LPBITMAPINFO is used in functions like
+// GetDIBits and SetDIBits.
+//
+// Coding example ...
+// CDC MemDC = CreateCompatibleDC(NULL);
+// CBitmapInfoPtr pbmi(hBitmap);
+// MemDC.GetDIBits(hBitmap, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS);
+
+#ifndef _WIN32XX_GDI_H_
+#define _WIN32XX_GDI_H_
+
+#include "wincore.h"
+
+// Disable macros from Windowsx.h
+#undef CopyRgn
+
+namespace Win32xx
+{
+
+ /////////////////////////////////////////////////////////////////
+ // Declarations for some global functions in the Win32xx namespace
+ //
+#ifndef _WIN32_WCE
+ void GrayScaleBitmap(CBitmap* pbmSource);
+ void TintBitmap(CBitmap* pbmSource, int cRed, int cGreen, int cBlue);
+ HIMAGELIST CreateDisabledImageList(HIMAGELIST himlNormal);
+#endif
+
+ ///////////////////////////////////////////////
+ // Declarations for the CGDIObject class
+ //
+ class CGDIObject
+ {
+ friend CBitmap* FromHandle(HBITMAP hBitmap);
+ friend CBrush* FromHandle(HBRUSH hBrush);
+ friend CDC* FromHandle(HDC hDC);
+ friend CFont* FromHandle(HFONT hFont);
+ friend CPalette* FromHandle(HPALETTE hPalette);
+ friend CPen* FromHandle(HPEN hPen);
+ friend CRgn* FromHandle(HRGN hRgn);
+
+ public:
+ struct DataMembers // A structure that contains the data members for CGDIObject
+ {
+ HGDIOBJ hGDIObject;
+ long Count;
+ BOOL bRemoveObject;
+ };
+ CGDIObject();
+ CGDIObject(const CGDIObject& rhs);
+ virtual ~CGDIObject();
+ CGDIObject& operator = ( const CGDIObject& rhs );
+ void operator = (HGDIOBJ hObject);
+
+ void Attach(HGDIOBJ hObject);
+ HGDIOBJ Detach();
+ HGDIOBJ GetHandle() const;
+ int GetObject(int nCount, LPVOID pObject) const;
+
+ protected:
+ DataMembers* m_pData;
+
+ private:
+ void AddToMap();
+ BOOL RemoveFromMap();
+ void Release();
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CBitmap class
+ //
+ class CBitmap : public CGDIObject
+ {
+ public:
+ CBitmap();
+ CBitmap(HBITMAP hBitmap);
+ CBitmap(LPCTSTR lpstr);
+ CBitmap(int nID);
+ operator HBITMAP() const;
+ ~CBitmap();
+
+ // Create and load methods
+ BOOL LoadBitmap(LPCTSTR lpszName);
+ BOOL LoadBitmap(int nID);
+ BOOL LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad);
+ BOOL LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad);
+ BOOL LoadOEMBitmap(UINT nIDBitmap);
+ HBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, LPCVOID lpBits);
+ HBITMAP CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight);
+ HBITMAP CreateDIBSection(CDC* pDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, LPVOID* ppvBits, HANDLE hSection, DWORD dwOffset);
+
+#ifndef _WIN32_WCE
+ HBITMAP CreateDIBitmap(CDC* pDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, LPCVOID lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse);
+ HBITMAP CreateMappedBitmap(UINT nIDBitmap, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0);
+ HBITMAP CreateBitmapIndirect(LPBITMAP lpBitmap);
+ int GetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const;
+ int SetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse);
+ CSize GetBitmapDimensionEx() const;
+ CSize SetBitmapDimensionEx(int nWidth, int nHeight);
+#endif // !_WIN32_WCE
+
+ // Attributes
+ BITMAP GetBitmapData() const;
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CBrush class
+ //
+ class CBrush : public CGDIObject
+ {
+ public:
+ CBrush();
+ CBrush(HBRUSH hBrush);
+ CBrush(COLORREF crColor);
+ operator HBRUSH() const;
+ ~CBrush();
+
+ HBRUSH CreateSolidBrush(COLORREF crColor);
+ HBRUSH CreatePatternBrush(CBitmap* pBitmap);
+ LOGBRUSH GetLogBrush() const;
+
+#ifndef _WIN32_WCE
+ HBRUSH CreateHatchBrush(int nIndex, COLORREF crColor);
+ HBRUSH CreateBrushIndirect(LPLOGBRUSH lpLogBrush);
+ HBRUSH CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec);
+ HBRUSH CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT nUsage);
+#endif // !defined(_WIN32_WCE)
+
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CFont class
+ //
+ class CFont : public CGDIObject
+ {
+ public:
+ CFont();
+ CFont(HFONT hFont);
+ CFont(const LOGFONT* lpLogFont);
+ operator HFONT() const;
+ ~CFont();
+
+ // Create methods
+ HFONT CreateFontIndirect(const LOGFONT* lpLogFont);
+ HFONT CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, CDC* pDC = NULL, BOOL bBold = FALSE, BOOL bItalic = FALSE);
+ HFONT CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC = NULL);
+
+#ifndef _WIN32_WCE
+ HFONT CreateFont(int nHeight, int nWidth, int nEscapement,
+ int nOrientation, int nWeight, DWORD dwItalic, DWORD dwUnderline,
+ DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision,
+ DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily,
+ LPCTSTR lpszFacename);
+#endif // #ifndef _WIN32_WCE
+
+ // Attributes
+ LOGFONT GetLogFont() const;
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CPalette class
+ //
+ class CPalette : public CGDIObject
+ {
+ public:
+ CPalette();
+ CPalette(HPALETTE hPalette);
+ operator HPALETTE() const;
+ ~CPalette();
+
+ // Create methods
+ HPALETTE CreatePalette(LPLOGPALETTE lpLogPalette);
+
+#ifndef _WIN32_WCE
+ HPALETTE CreateHalftonePalette(CDC* pDC);
+#endif // !_WIN32_WCE
+
+ // Attributes
+ int GetEntryCount() const;
+ UINT GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const;
+ UINT SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors);
+
+ // Operations
+#ifndef _WIN32_WCE
+ BOOL ResizePalette(UINT nNumEntries);
+ void AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors);
+#endif // !_WIN32_WCE
+
+ UINT GetNearestPaletteIndex (COLORREF crColor) const;
+
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CPen class
+ //
+ class CPen : public CGDIObject
+ {
+ public:
+ CPen();
+ CPen(HPEN hPen);
+ CPen(int nPenStyle, int nWidth, COLORREF crColor);
+#ifndef _WIN32_WCE
+ CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL);
+#endif // !_WIN32_WCE
+ operator HPEN() const;
+ ~CPen();
+
+ HPEN CreatePen(int nPenStyle, int nWidth, COLORREF crColor);
+ HPEN CreatePenIndirect(LPLOGPEN lpLogPen);
+ LOGPEN GetLogPen() const;
+
+#ifndef _WIN32_WCE
+ HPEN ExtCreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL);
+ EXTLOGPEN GetExtLogPen() const;
+#endif // !_WIN32_WCE
+
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CRgn class
+ //
+ class CRgn : public CGDIObject
+ {
+ public:
+ CRgn();
+ CRgn(HRGN hRgn);
+ operator HRGN() const;
+ ~CRgn ();
+
+ // Create methods
+ HRGN CreateRectRgn(int x1, int y1, int x2, int y2);
+ HRGN CreateRectRgnIndirect(const RECT& rc);
+ HRGN CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData);
+
+#ifndef _WIN32_WCE
+ HRGN CreateEllipticRgn(int x1, int y1, int x2, int y2);
+ HRGN CreateEllipticRgnIndirect(const RECT& rc);
+ HRGN CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode);
+ HRGN CreatePolyPolygonRgn(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount, int nPolyFillMode);
+ HRGN CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3);
+ HRGN CreateFromPath(HDC hDC);
+#endif // !_WIN32_WCE
+
+ // Operations
+ void SetRectRgn(int x1, int y1, int x2, int y2);
+ void SetRectRgn(const RECT& rc);
+ int CombineRgn(CRgn* pRgnSrc1, CRgn* pRgnSrc2, int nCombineMode);
+ int CombineRgn(CRgn* pRgnSrc, int nCombineMode);
+ int CopyRgn(CRgn* pRgnSrc);
+ BOOL EqualRgn(CRgn* pRgn) const;
+ int OffsetRgn(int x, int y);
+ int OffsetRgn(POINT& pt);
+ int GetRgnBox(RECT& rc) const;
+ BOOL PtInRegion(int x, int y) const;
+ BOOL PtInRegion(POINT& pt) const;
+ BOOL RectInRegion(const RECT& rc) const;
+ int GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const;
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CDC class
+ //
+ class CDC
+ {
+ friend class CWinApp;
+ friend class CWnd;
+ friend CDC* FromHandle(HDC hDC);
+
+ public:
+ struct DataMembers // A structure that contains the data members for CDC
+ {
+ std::vector<GDIPtr> m_vGDIObjects; // Smart pointers to internally created Bitmaps, Brushes, Fonts, Bitmaps and Regions
+ HDC hDC; // The HDC belonging to this CDC
+ long Count; // Reference count
+ BOOL bRemoveHDC; // Delete/Release the HDC on destruction
+ HWND hWnd; // The HWND of a Window or Client window DC
+ int nSavedDCState; // The save state of the HDC.
+ };
+
+ CDC(); // Constructs a new CDC without assigning a HDC
+ CDC(HDC hDC, HWND hWnd = 0); // Assigns a HDC to a new CDC
+ CDC(const CDC& rhs); // Constructs a new copy of the CDC
+ virtual ~CDC();
+ operator HDC() const { return m_pData->hDC; } // Converts a CDC to a HDC
+ CDC& operator = (const CDC& rhs); // Assigns a CDC to an existing CDC
+
+ void Attach(HDC hDC, HWND hWnd = 0);
+ void Destroy();
+ HDC Detach();
+ HDC GetHDC() const { return m_pData->hDC; }
+ CPalette* SelectPalette(const CPalette* pPalette, BOOL bForceBkgnd);
+ CBitmap* SelectObject(const CBitmap* pBitmap);
+ CBrush* SelectObject(const CBrush* pBrush);
+ CFont* SelectObject(const CFont* pFont);
+ CPalette* SelectObject(const CPalette* pPalette);
+ CPen* SelectObject(const CPen* pPen);
+
+#ifndef _WIN32_WCE
+ void operator = (const HDC hDC);
+#endif
+
+ // Initialization
+ BOOL CreateCompatibleDC(CDC* pDC);
+ BOOL CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData);
+ int GetDeviceCaps(int nIndex) const;
+#ifndef _WIN32_WCE
+ BOOL CreateIC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData);
+#endif
+
+ // Create and Select Bitmaps
+ CBitmap* CreateBitmap(int cx, int cy, UINT Planes, UINT BitsPerPixel, LPCVOID pvColors);
+ CBitmap* CreateCompatibleBitmap(CDC* pDC, int cx, int cy);
+ CBitmap* CreateDIBSection(CDC* pDC, const BITMAPINFO& bmi, UINT iUsage, LPVOID *ppvBits,
+ HANDLE hSection, DWORD dwOffset);
+ BITMAP GetBitmapData() const;
+ CBitmap* LoadBitmap(UINT nID);
+ CBitmap* LoadBitmap(LPCTSTR lpszName);
+ CBitmap* LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad);
+ CBitmap* LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad);
+ CBitmap* LoadOEMBitmap(UINT nIDBitmap); // for OBM_/OCR_/OIC
+
+#ifndef _WIN32_WCE
+ CBitmap* CreateBitmapIndirect(LPBITMAP pBitmap);
+ CBitmap* CreateDIBitmap(CDC* pDC, const BITMAPINFOHEADER& bmih, DWORD fdwInit, LPCVOID lpbInit,
+ BITMAPINFO& bmi, UINT fuUsage);
+ CBitmap* CreateMappedBitmap(UINT nIDBitmap, UINT nFlags /*= 0*/, LPCOLORMAP lpColorMap /*= NULL*/, int nMapSize /*= 0*/);
+#endif
+
+ // Create and Select Brushes
+ CBrush* CreatePatternBrush(CBitmap* pBitmap);
+ CBrush* CreateSolidBrush(COLORREF rbg);
+ LOGBRUSH GetLogBrush() const;
+
+#ifndef _WIN32_WCE
+ CBrush* CreateBrushIndirect(LPLOGBRUSH pLogBrush);
+ CBrush* CreateHatchBrush(int fnStyle, COLORREF rgb);
+ CBrush* CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec);
+ CBrush* CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT iUsage);
+#endif
+
+ // Create and Select Fonts
+ CFont* CreateFontIndirect(LPLOGFONT plf);
+ LOGFONT GetLogFont() const;
+
+#ifndef _WIN32_WCE
+ CFont* CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight,
+ DWORD fdwItalic, DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD fdwCharSet,
+ DWORD fdwOutputPrecision, DWORD fdwClipPrecision, DWORD fdwQuality,
+ DWORD fdwPitchAndFamily, LPCTSTR lpszFace);
+#endif
+
+ // Create and Select Pens
+ CPen* CreatePen(int nStyle, int nWidth, COLORREF rgb);
+ CPen* CreatePenIndirect(LPLOGPEN pLogPen);
+ LOGPEN GetLogPen() const;
+
+ // Create Select Regions
+ int CreateRectRgn(int left, int top, int right, int bottom);
+ int CreateRectRgnIndirect(const RECT& rc);
+ int CreateFromData(const XFORM* Xform, DWORD nCount, const RGNDATA *pRgnData);
+#ifndef _WIN32_WCE
+ int CreateEllipticRgn(int left, int top, int right, int bottom);
+ int CreateEllipticRgnIndirect(const RECT& rc);
+ int CreatePolygonRgn(LPPOINT ppt, int cPoints, int fnPolyFillMode);
+ int CreatePolyPolygonRgn(LPPOINT ppt, LPINT pPolyCounts, int nCount, int fnPolyFillMode);
+#endif
+
+ // Wrappers for WinAPI functions
+
+ // Point and Line Drawing Functions
+ CPoint GetCurrentPosition() const;
+ CPoint MoveTo(int x, int y) const;
+ CPoint MoveTo(POINT pt) const;
+ BOOL LineTo(int x, int y) const;
+ BOOL LineTo(POINT pt) const;
+ COLORREF GetPixel(int x, int y) const;
+ COLORREF GetPixel(POINT pt) const;
+ COLORREF SetPixel(int x, int y, COLORREF crColor) const;
+ COLORREF SetPixel(POINT pt, COLORREF crColor) const;
+#ifndef _WIN32_WCE
+ BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const;
+ BOOL Arc(RECT& rc, POINT ptStart, POINT ptEnd) const;
+ BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const;
+ BOOL ArcTo(RECT& rc, POINT ptStart, POINT ptEnd) const;
+ BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle) const;
+ int GetArcDirection() const;
+ int SetArcDirection(int nArcDirection) const;
+ BOOL PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount) const;
+ BOOL Polyline(LPPOINT lpPoints, int nCount) const;
+ BOOL PolyPolyline(const POINT* lpPoints, const DWORD* lpPolyPoints, int nCount) const;
+ BOOL PolylineTo(const POINT* lpPoints, int nCount) const;
+ BOOL PolyBezier(const POINT* lpPoints, int nCount) const;
+ BOOL PolyBezierTo(const POINT* lpPoints, int nCount) const;
+ BOOL SetPixelV(int x, int y, COLORREF crColor) const;
+ BOOL SetPixelV(POINT pt, COLORREF crColor) const;
+#endif
+
+ // Shape Drawing Functions
+ void DrawFocusRect(const RECT& rc) const;
+ BOOL Ellipse(int x1, int y1, int x2, int y2) const;
+ BOOL Ellipse(const RECT& rc) const;
+ BOOL Polygon(LPPOINT lpPoints, int nCount) const;
+ BOOL Rectangle(int x1, int y1, int x2, int y2) const;
+ BOOL Rectangle(const RECT& rc) const;
+ BOOL RoundRect(int x1, int y1, int x2, int y2, int nWidth, int nHeight) const;
+ BOOL RoundRect(const RECT& rc, int nWidth, int nHeight) const;
+
+#ifndef _WIN32_WCE
+ BOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const;
+ BOOL Chord(const RECT& rc, POINT ptStart, POINT ptEnd) const;
+ BOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const;
+ BOOL Pie(const RECT& rc, POINT ptStart, POINT ptEnd) const;
+ BOOL PolyPolygon(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount) const;
+#endif
+
+ // Fill and Image Drawing functions
+ BOOL FillRect(const RECT& rc, CBrush* pBrushr) const;
+ BOOL InvertRect(const RECT& rc) const;
+ BOOL DrawIconEx(int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, CBrush* pFlickerFreeDraw, UINT diFlags) const;
+ BOOL DrawEdge(const RECT& rc, UINT nEdge, UINT nFlags) const;
+ BOOL DrawFrameControl(const RECT& rc, UINT nType, UINT nState) const;
+ BOOL FillRgn(CRgn* pRgn, CBrush* pBrush) const;
+ void GradientFill(COLORREF Color1, COLORREF Color2, const RECT& rc, BOOL bVertical);
+ void SolidFill(COLORREF Color, const RECT& rc);
+
+#ifndef _WIN32_WCE
+ BOOL DrawIcon(int x, int y, HICON hIcon) const;
+ BOOL DrawIcon(POINT point, HICON hIcon) const;
+ BOOL FrameRect(const RECT& rc, CBrush* pBrush) const;
+ BOOL PaintRgn(CRgn* pRgn) const;
+#endif
+
+ // Bitmap Functions
+ void DrawBitmap(int x, int y, int cx, int cy, CBitmap& Bitmap, COLORREF clrMask);
+ int StretchDIBits(int XDest, int YDest, int nDestWidth, int nDestHeight, int XSrc, int YSrc, int nSrcWidth,
+ int nSrcHeight, CONST VOID *lpBits, BITMAPINFO& bi, UINT iUsage, DWORD dwRop) const;
+ BOOL PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) const;
+ BOOL BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop) const;
+ BOOL StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop) const;
+
+#ifndef _WIN32_WCE
+ int GetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbi, UINT uUsage) const;
+ int SetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, CONST VOID *lpvBits, LPBITMAPINFO lpbi, UINT fuColorUse) const;
+ int GetStretchBltMode() const;
+ int SetStretchBltMode(int iStretchMode) const;
+ BOOL FloodFill(int x, int y, COLORREF crColor) const;
+ BOOL ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) const;
+#endif
+
+ // Brush Functions
+#ifdef GetDCBrushColor
+ COLORREF GetDCBrushColor() const;
+ COLORREF SetDCBrushColor(COLORREF crColor) const;
+#endif
+
+ // Clipping Functions
+ int ExcludeClipRect(int Left, int Top, int Right, int BottomRect);
+ int ExcludeClipRect(const RECT& rc);
+ int GetClipBox(RECT& rc);
+ int GetClipRgn(HRGN hrgn);
+ int IntersectClipRect(int Left, int Top, int Right, int Bottom);
+ int IntersectClipRect(const RECT& rc);
+ BOOL RectVisible(const RECT& rc);
+ int SelectClipRgn(CRgn* pRgn);
+
+#ifndef _WIN32_WCE
+ int ExtSelectClipRgn(CRgn* pRgn, int fnMode);
+ int OffsetClipRgn(int nXOffset, int nYOffset);
+ BOOL PtVisible(int X, int Y);
+#endif
+
+ // Co-ordinate Functions
+#ifndef _WIN32_WCE
+ BOOL DPtoLP(LPPOINT lpPoints, int nCount) const;
+ BOOL DPtoLP(RECT& rc) const;
+ BOOL LPtoDP(LPPOINT lpPoints, int nCount) const;
+ BOOL LPtoDP(RECT& rc) const;
+#endif
+
+ // Layout Functions
+ DWORD GetLayout() const;
+ DWORD SetLayout(DWORD dwLayout) const;
+
+ // Mapping functions
+#ifndef _WIN32_WCE
+ int GetMapMode() const;
+ int SetMapMode(int nMapMode) const;
+ BOOL GetViewportOrgEx(LPPOINT lpPoint) const;
+ BOOL SetViewportOrgEx(int x, int y, LPPOINT lpPoint = NULL) const;
+ BOOL SetViewportOrgEx(POINT point, LPPOINT lpPointRet = NULL) const;
+ BOOL OffsetViewportOrgEx(int nWidth, int nHeight, LPPOINT lpPoint = NULL) const;
+ BOOL GetViewportExtEx(LPSIZE lpSize) const;
+ BOOL SetViewportExtEx(int x, int y, LPSIZE lpSize) const;
+ BOOL SetViewportExtEx(SIZE size, LPSIZE lpSizeRet) const;
+ BOOL ScaleViewportExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize) const;
+ BOOL OffsetWindowOrg(int nWidth, int nHeight, LPPOINT lpPoint) const;
+ BOOL GetWindowExtEx(LPSIZE lpSize) const;
+ BOOL SetWindowExtEx(int x, int y, LPSIZE lpSize) const;
+ BOOL SetWindowExtEx(SIZE size, LPSIZE lpSizeRet) const;
+ BOOL ScaleWindowExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize) const;
+ BOOL GetWindowOrgEx(LPPOINT lpPoint) const;
+ BOOL SetWindowOrgEx(int x, int y, LPPOINT lpPoint) const;
+ BOOL SetWindowOrgEx(POINT point, LPPOINT lpPointRet) const;
+ BOOL OffsetWindowOrgEx(int nWidth, int nHeight, LPPOINT lpPoint) const;
+#endif
+
+ // Printer Functions
+ int StartDoc(LPDOCINFO lpDocInfo) const;
+ int EndDoc() const;
+ int StartPage() const;
+ int EndPage() const;
+ int AbortDoc() const;
+ int SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int)) const;
+
+ // Text Functions
+ int DrawText(LPCTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat) const;
+ BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lprc, LPCTSTR lpszString, int nCount = -1, LPINT lpDxWidths = NULL) const;
+ COLORREF GetBkColor() const;
+ int GetBkMode() const;
+ UINT GetTextAlign() const;
+ int GetTextFace(int nCount, LPTSTR lpszFacename) const;
+ COLORREF GetTextColor() const;
+ BOOL GetTextMetrics(TEXTMETRIC& Metrics) const;
+ COLORREF SetBkColor(COLORREF crColor) const;
+ int SetBkMode(int iBkMode) const;
+ UINT SetTextAlign(UINT nFlags) const;
+ COLORREF SetTextColor(COLORREF crColor) const;
+
+#ifndef _WIN32_WCE
+ int DrawTextEx(LPTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat, LPDRAWTEXTPARAMS lpDTParams) const;
+ CSize GetTabbedTextExtent(LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const;
+ int GetTextCharacterExtra() const;
+ CSize GetTextExtentPoint32(LPCTSTR lpszString, int nCount) const;
+ BOOL GrayString(CBrush* pBrush, GRAYSTRINGPROC lpOutputFunc, LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight) const;
+ int SetTextCharacterExtra(int nCharExtra) const;
+ int SetTextJustification(int nBreakExtra, int nBreakCount) const;
+ CSize TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin) const;
+ BOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount = -1) const;
+#endif
+
+ private:
+ void AddToMap();
+ static CDC* AddTempHDC(HDC hDC, HWND hWnd);
+ void Release();
+ BOOL RemoveFromMap();
+
+ DataMembers* m_pData; // pointer to the class's data members
+ };
+
+ class CClientDC : public CDC
+ {
+ public:
+ CClientDC(const CWnd* pWnd)
+ {
+ if (pWnd) assert(pWnd->IsWindow());
+ HWND hWnd = pWnd? pWnd->GetHwnd() : GetDesktopWindow();
+ Attach(::GetDC(hWnd), hWnd);
+ }
+ virtual ~CClientDC() {}
+ };
+
+ class CMemDC : public CDC
+ {
+ public:
+ CMemDC(const CDC* pDC)
+ {
+ if (pDC) assert(pDC->GetHDC());
+ HDC hDC = pDC? pDC->GetHDC() : NULL;
+ Attach(::CreateCompatibleDC(hDC));
+ }
+ virtual ~CMemDC() {}
+ };
+
+ class CPaintDC : public CDC
+ {
+ public:
+ CPaintDC(const CWnd* pWnd)
+ {
+ assert(pWnd->IsWindow());
+ m_hWnd = pWnd->GetHwnd();
+ Attach(::BeginPaint(pWnd->GetHwnd(), &m_ps), m_hWnd);
+ }
+
+ virtual ~CPaintDC() { ::EndPaint(m_hWnd, &m_ps); }
+
+ private:
+ HWND m_hWnd;
+ PAINTSTRUCT m_ps;
+ };
+
+ class CWindowDC : public CDC
+ {
+ public:
+ CWindowDC(const CWnd* pWnd)
+ {
+ if (pWnd) assert(pWnd->IsWindow());
+ HWND hWnd = pWnd? pWnd->GetHwnd() : GetDesktopWindow();
+ Attach(::GetWindowDC(hWnd), hWnd);
+ }
+ virtual ~CWindowDC() {}
+ };
+
+#ifndef _WIN32_WCE
+ class CMetaFileDC : public CDC
+ {
+ public:
+ CMetaFileDC() : m_hMF(0), m_hEMF(0) {}
+ virtual ~CMetaFileDC()
+ {
+ if (m_hMF)
+ {
+ ::CloseMetaFile(GetHDC());
+ ::DeleteMetaFile(m_hMF);
+ }
+ if (m_hEMF)
+ {
+ ::CloseEnhMetaFile(GetHDC());
+ ::DeleteEnhMetaFile(m_hEMF);
+ }
+ }
+ void Create(LPCTSTR lpszFilename = NULL) { Attach(::CreateMetaFile(lpszFilename)); }
+ void CreateEnhanced(CDC* pDCRef, LPCTSTR lpszFileName, LPCRECT lpBounds, LPCTSTR lpszDescription)
+ {
+ HDC hDC = pDCRef? pDCRef->GetHDC() : NULL;
+ ::CreateEnhMetaFile(hDC, lpszFileName, lpBounds, lpszDescription);
+ assert(GetHDC());
+ }
+ HMETAFILE Close() { return ::CloseMetaFile(GetHDC()); }
+ HENHMETAFILE CloseEnhanced() { return ::CloseEnhMetaFile(GetHDC()); }
+
+ private:
+ HMETAFILE m_hMF;
+ HENHMETAFILE m_hEMF;
+ };
+#endif
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CBitmapInfoPtr class
+ // The CBitmapInfoPtr class is a convienient wrapper for the BITMAPINFO structure.
+ class CBitmapInfoPtr
+ {
+ public:
+ CBitmapInfoPtr(CBitmap* pBitmap)
+ {
+ BITMAP bmSource = pBitmap->GetBitmapData();
+
+ // Convert the color format to a count of bits.
+ WORD cClrBits = (WORD)(bmSource.bmPlanes * bmSource.bmBitsPixel);
+ if (cClrBits == 1) cClrBits = 1;
+ else if (cClrBits <= 4) cClrBits = 4;
+ else if (cClrBits <= 8) cClrBits = 8;
+ else if (cClrBits <= 16) cClrBits = 16;
+ else if (cClrBits <= 24) cClrBits = 24;
+ else cClrBits = 32;
+
+ // Allocate memory for the BITMAPINFO structure.
+ UINT uQuadSize = (cClrBits == 24)? 0 : sizeof(RGBQUAD) * (int)(1 << cClrBits);
+ m_bmi.assign(sizeof(BITMAPINFOHEADER) + uQuadSize, 0);
+ m_pbmiArray = (LPBITMAPINFO) &m_bmi[0];
+
+ m_pbmiArray->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ m_pbmiArray->bmiHeader.biHeight = bmSource.bmHeight;
+ m_pbmiArray->bmiHeader.biWidth = bmSource.bmWidth;
+ m_pbmiArray->bmiHeader.biPlanes = bmSource.bmPlanes;
+ m_pbmiArray->bmiHeader.biBitCount = bmSource.bmBitsPixel;
+ m_pbmiArray->bmiHeader.biCompression = BI_RGB;
+ if (cClrBits < 24)
+ m_pbmiArray->bmiHeader.biClrUsed = (1<<cClrBits);
+ }
+ LPBITMAPINFO get() const { return m_pbmiArray; }
+ operator LPBITMAPINFO() const { return m_pbmiArray; }
+ LPBITMAPINFO operator->() const { return m_pbmiArray; }
+
+ private:
+ CBitmapInfoPtr(const CBitmapInfoPtr&); // Disable copy construction
+ CBitmapInfoPtr& operator = (const CBitmapInfoPtr&); // Disable assignment operator
+ LPBITMAPINFO m_pbmiArray;
+ std::vector<byte> m_bmi;
+ };
+
+
+ CBitmap* FromHandle(HBITMAP hBitmap);
+ CBrush* FromHandle(HBRUSH hBrush);
+ CFont* FromHandle(HFONT hFont);
+ CPalette* FromHandle(HPALETTE hPalette);
+ CPen* FromHandle(HPEN hPen);
+ CRgn* FromHandle(HRGN hRgn);
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ ///////////////////////////////////////////////
+ // Declarations for the CGDIObject class
+ //
+
+ inline CGDIObject::CGDIObject()
+ // Constructs the CGDIObject
+ {
+ m_pData = new DataMembers;
+ m_pData->hGDIObject = 0;
+ m_pData->Count = 1L;
+ m_pData->bRemoveObject = TRUE;
+ }
+
+ inline CGDIObject::CGDIObject(const CGDIObject& rhs)
+ // Note: A copy of a CGDIObject is a clone of the original.
+ // Both objects manipulate the one HGDIOBJ.
+ {
+ m_pData = rhs.m_pData;
+ InterlockedIncrement(&m_pData->Count);
+ }
+
+ inline CGDIObject::~CGDIObject()
+ // Deconstructs the CGDIObject
+ {
+ Release();
+ }
+
+ inline CGDIObject& CGDIObject::operator = ( const CGDIObject& rhs )
+ // Note: A copy of a CGDIObject is a clone of the original.
+ // Both objects manipulate the one HGDIOBJ.
+ {
+ if (this != &rhs)
+ {
+ InterlockedIncrement(&rhs.m_pData->Count);
+ Release();
+ m_pData = rhs.m_pData;
+ }
+
+ return *this;
+ }
+
+ inline void CGDIObject::operator = (HGDIOBJ hObject)
+ {
+ assert(m_pData);
+ assert (m_pData->hGDIObject == NULL);
+ m_pData->hGDIObject = hObject;
+ }
+
+ inline void CGDIObject::AddToMap()
+ // Store the HDC and CDC pointer in the HDC map
+ {
+ assert( GetApp() );
+ GetApp()->m_csMapLock.Lock();
+
+ assert(m_pData->hGDIObject);
+ assert(!GetApp()->GetCGDIObjectFromMap(m_pData->hGDIObject));
+
+ GetApp()->m_mapGDI.insert(std::make_pair(m_pData->hGDIObject, this));
+ GetApp()->m_csMapLock.Release();
+ }
+
+ inline void CGDIObject::Attach(HGDIOBJ hObject)
+ // Attaches a GDI HANDLE to the CGDIObject.
+ // The HGDIOBJ will be automatically deleted when the destructor is called unless it is detached.
+ {
+ assert(m_pData);
+
+ if (m_pData->hGDIObject != NULL && m_pData->hGDIObject != hObject)
+ {
+ ::DeleteObject(Detach());
+ }
+
+ CGDIObject* pObject = GetApp()->GetCGDIObjectFromMap(hObject);
+ if (pObject)
+ {
+ delete m_pData;
+ m_pData = pObject->m_pData;
+ InterlockedIncrement(&m_pData->Count);
+ }
+ else
+ {
+ m_pData->hGDIObject = hObject;
+ AddToMap();
+ }
+ }
+
+ inline HGDIOBJ CGDIObject::Detach()
+ // Detaches the HGDIOBJ from this object.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject);
+
+ GetApp()->m_csMapLock.Lock();
+ RemoveFromMap();
+ HGDIOBJ hObject = m_pData->hGDIObject;
+ m_pData->hGDIObject = 0;
+
+ if (m_pData->Count)
+ {
+ if (InterlockedDecrement(&m_pData->Count) == 0)
+ {
+ delete m_pData;
+ }
+ }
+
+ GetApp()->m_csMapLock.Release();
+
+ // Assign values to our data members
+ m_pData = new DataMembers;
+ m_pData->hGDIObject = 0;
+ m_pData->Count = 1L;
+ m_pData->bRemoveObject = TRUE;
+
+ return hObject;
+ }
+
+ inline HGDIOBJ CGDIObject::GetHandle() const
+ {
+ assert(m_pData);
+ return m_pData->hGDIObject;
+ }
+
+ inline int CGDIObject::GetObject(int nCount, LPVOID pObject) const
+ {
+ assert(m_pData);
+ return ::GetObject(m_pData->hGDIObject, nCount, pObject);
+ }
+
+ inline void CGDIObject::Release()
+ {
+ assert(m_pData);
+ BOOL bSucceeded = TRUE;
+
+ if (InterlockedDecrement(&m_pData->Count) == 0)
+ {
+ if (m_pData->hGDIObject != NULL)
+ {
+ if (m_pData->bRemoveObject)
+ bSucceeded = ::DeleteObject(m_pData->hGDIObject);
+ else
+ bSucceeded = TRUE;
+ }
+
+ RemoveFromMap();
+ delete m_pData;
+ m_pData = 0;
+ }
+
+ assert(bSucceeded);
+ }
+
+ inline BOOL CGDIObject::RemoveFromMap()
+ {
+ BOOL Success = FALSE;
+
+ if( GetApp() )
+ {
+ // Allocate an iterator for our HDC map
+ std::map<HGDIOBJ, CGDIObject*, CompareGDI>::iterator m;
+
+ CWinApp* pApp = GetApp();
+ if (pApp)
+ {
+ // Erase the CGDIObject pointer entry from the map
+ pApp->m_csMapLock.Lock();
+ m = pApp->m_mapGDI.find(m_pData->hGDIObject);
+ if (m != pApp->m_mapGDI.end())
+ {
+ pApp->m_mapGDI.erase(m);
+ Success = TRUE;
+ }
+
+ pApp->m_csMapLock.Release();
+ }
+ }
+
+ return Success;
+ }
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CBitmap class
+ //
+ inline CBitmap::CBitmap()
+ {
+ }
+
+ inline CBitmap::CBitmap(HBITMAP hBitmap)
+ {
+ assert(m_pData);
+ Attach(hBitmap);
+ }
+
+ inline CBitmap::CBitmap(LPCTSTR lpszName)
+ {
+ LoadBitmap(lpszName);
+ }
+
+ inline CBitmap::CBitmap(int nID)
+ {
+ LoadBitmap(nID);
+ }
+
+ inline CBitmap::operator HBITMAP() const
+ {
+ assert(m_pData);
+ return (HBITMAP)m_pData->hGDIObject;
+ }
+
+ inline CBitmap::~CBitmap()
+ {
+ }
+
+ inline BOOL CBitmap::LoadBitmap(int nID)
+ // Loads a bitmap from a resource using the resource ID.
+ {
+ return LoadBitmap(MAKEINTRESOURCE(nID));
+ }
+
+ inline BOOL CBitmap::LoadBitmap(LPCTSTR lpszName)
+ // Loads a bitmap from a resource using the resource string.
+ {
+ assert(GetApp());
+ assert(m_pData);
+
+ HBITMAP hBitmap = (HBITMAP)::LoadImage(GetApp()->GetResourceHandle(), lpszName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
+ if (hBitmap)
+ {
+ Attach(hBitmap);
+ }
+ return (0 != hBitmap); // boolean expression
+ }
+
+ inline BOOL CBitmap::LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad)
+ // Loads a bitmap from a resource using the resource ID.
+ {
+ return LoadImage(MAKEINTRESOURCE(nID), cxDesired, cyDesired, fuLoad);
+ }
+
+ inline BOOL CBitmap::LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad)
+ // Loads a bitmap from a resource using the resource string.
+ {
+ assert(GetApp());
+ assert(m_pData);
+
+ HBITMAP hBitmap = (HBITMAP)::LoadImage(GetApp()->GetResourceHandle(), lpszName, IMAGE_BITMAP, cxDesired, cyDesired, fuLoad);
+ if (hBitmap)
+ {
+ Attach(hBitmap);
+ }
+ return (0 != hBitmap); // boolean expression
+ }
+
+ inline BOOL CBitmap::LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_
+ // Loads a predefined bitmap
+ // Predefined bitmaps include: OBM_BTNCORNERS, OBM_BTSIZE, OBM_CHECK, OBM_CHECKBOXES, OBM_CLOSE, OBM_COMBO
+ // OBM_DNARROW, OBM_DNARROWD, OBM_DNARROWI, OBM_LFARROW, OBM_LFARROWD, OBM_LFARROWI, OBM_MNARROW,OBM_OLD_CLOSE
+ // OBM_OLD_DNARROW, OBM_OLD_LFARROW, OBM_OLD_REDUCE, OBM_OLD_RESTORE, OBM_OLD_RGARROW, OBM_OLD_UPARROW
+ // OBM_OLD_ZOOM, OBM_REDUCE, OBM_REDUCED, OBM_RESTORE, OBM_RESTORED, OBM_RGARROW, OBM_RGARROWD, OBM_RGARROWI
+ // OBM_SIZE, OBM_UPARROW, OBM_UPARROWD, OBM_UPARROWI, OBM_ZOOM, OBM_ZOOMD
+ {
+ assert(m_pData);
+
+ HBITMAP hBitmap = ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap));
+ if (hBitmap)
+ {
+ Attach( ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap)) );
+ }
+ return (0 != hBitmap); // boolean expression
+ }
+
+#ifndef _WIN32_WCE
+ inline HBITMAP CBitmap::CreateMappedBitmap(UINT nIDBitmap, UINT nFlags /*= 0*/, LPCOLORMAP lpColorMap /*= NULL*/, int nMapSize /*= 0*/)
+ // Creates a new bitmap using the bitmap data and colors specified by the bitmap resource and the color mapping information.
+ {
+ assert(GetApp());
+ assert(m_pData);
+ HBITMAP hBitmap = ::CreateMappedBitmap(GetApp()->GetResourceHandle(), nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize);
+ Attach(hBitmap);
+ return hBitmap;
+ }
+#endif // !_WIN32_WCE
+
+ inline HBITMAP CBitmap::CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, LPCVOID lpBits)
+ // Creates a bitmap with the specified width, height, and color format (color planes and bits-per-pixel).
+ {
+ assert(m_pData);
+ HBITMAP hBitmap = ::CreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpBits);
+ Attach(hBitmap);
+ return hBitmap;
+ }
+
+#ifndef _WIN32_WCE
+ inline HBITMAP CBitmap::CreateBitmapIndirect(LPBITMAP lpBitmap)
+ // Creates a bitmap with the width, height, and color format specified in the BITMAP structure.
+ {
+ assert(m_pData);
+ HBITMAP hBitmap = ::CreateBitmapIndirect(lpBitmap);
+ Attach(hBitmap);
+ return hBitmap;
+ }
+#endif // !_WIN32_WCE
+
+ inline HBITMAP CBitmap::CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight)
+ // Creates a bitmap compatible with the device that is associated with the specified device context.
+ {
+ assert(m_pData);
+ assert(pDC);
+ HBITMAP hBitmap = ::CreateCompatibleBitmap(pDC->GetHDC(), nWidth, nHeight);
+ Attach(hBitmap);
+ return hBitmap;
+ }
+
+ // Attributes
+ inline BITMAP CBitmap::GetBitmapData() const
+ // Retrieves the BITMAP structure
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ BITMAP bmp = {0};
+ ::GetObject(m_pData->hGDIObject, sizeof(BITMAP), &bmp);
+ return bmp;
+ }
+
+#ifndef _WIN32_WCE
+ inline CSize CBitmap::GetBitmapDimensionEx() const
+ // Retrieves the dimensions of a compatible bitmap.
+ // The retrieved dimensions must have been set by the SetBitmapDimensionEx function.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ CSize Size;
+ ::GetBitmapDimensionEx((HBITMAP)m_pData->hGDIObject, &Size);
+ return Size;
+ }
+
+ inline CSize CBitmap::SetBitmapDimensionEx(int nWidth, int nHeight)
+ // The SetBitmapDimensionEx function assigns preferred dimensions to a bitmap.
+ // These dimensions can be used by applications; however, they are not used by the system.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ CSize Size;
+ ::SetBitmapDimensionEx((HBITMAP)m_pData->hGDIObject, nWidth, nHeight, Size);
+ return Size;
+ }
+
+ // DIB support
+ inline HBITMAP CBitmap::CreateDIBitmap(CDC* pDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, CONST VOID* lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse)
+ // Creates a compatible bitmap (DDB) from a DIB and, optionally, sets the bitmap bits.
+ {
+ assert(m_pData);
+ assert(pDC);
+ HBITMAP hBitmap = ::CreateDIBitmap(pDC->GetHDC(), lpbmih, dwInit, lpbInit, lpbmi, uColorUse);
+ Attach(hBitmap);
+ return hBitmap;
+ }
+#endif // !_WIN32_WCE
+
+ inline HBITMAP CBitmap::CreateDIBSection(CDC* pDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, VOID** ppvBits, HANDLE hSection, DWORD dwOffset)
+ // Creates a DIB that applications can write to directly. The function gives you a pointer to the location of the bitmap bit values.
+ // You can supply a handle to a file-mapping object that the function will use to create the bitmap, or you can let the system allocate the memory for the bitmap.
+ {
+ assert(m_pData);
+ assert(pDC);
+ HBITMAP hBitmap = ::CreateDIBSection(pDC->GetHDC(), lpbmi, uColorUse, ppvBits, hSection, dwOffset);
+ Attach(hBitmap);
+ return hBitmap;
+ }
+
+#ifndef _WIN32_WCE
+ inline int CBitmap::GetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const
+ // Retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format.
+ {
+ assert(m_pData);
+ assert(pDC);
+ assert(m_pData->hGDIObject != NULL);
+ return ::GetDIBits(pDC->GetHDC(), (HBITMAP)m_pData->hGDIObject, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse);
+ }
+
+ inline int CBitmap::SetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse)
+ // Sets the pixels in a compatible bitmap (DDB) using the color data found in the specified DIB.
+ {
+ assert(m_pData);
+ assert(pDC);
+ assert(m_pData->hGDIObject != NULL);
+ return ::SetDIBits(pDC->GetHDC(), (HBITMAP)m_pData->hGDIObject, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse);
+ }
+#endif // !_WIN32_WCE
+
+
+ ///////////////////////////////////////////////
+ // Definitions of the CBrush class
+ //
+ inline CBrush::CBrush()
+ {
+ }
+
+ inline CBrush::CBrush(HBRUSH hBrush)
+ {
+ assert(m_pData);
+ Attach(hBrush);
+ }
+
+ inline CBrush::CBrush(COLORREF crColor)
+ {
+ Attach( ::CreateSolidBrush(crColor) );
+ assert (m_pData->hGDIObject);
+ }
+
+ inline CBrush::operator HBRUSH() const
+ {
+ assert(m_pData);
+ return (HBRUSH)m_pData->hGDIObject;
+ }
+
+ inline CBrush::~CBrush()
+ {
+ }
+
+ inline HBRUSH CBrush::CreateSolidBrush(COLORREF crColor)
+ // Creates a logical brush that has the specified solid color.
+ {
+ assert(m_pData);
+ HBRUSH hBrush = ::CreateSolidBrush(crColor);
+ Attach(hBrush);
+ return hBrush;
+ }
+
+#ifndef _WIN32_WCE
+ inline HBRUSH CBrush::CreateHatchBrush(int nIndex, COLORREF crColor)
+ // Creates a logical brush that has the specified hatch pattern and color.
+ {
+ assert(m_pData);
+ HBRUSH hBrush = ::CreateHatchBrush(nIndex, crColor);
+ Attach(hBrush);
+ return hBrush;
+ }
+
+ inline HBRUSH CBrush::CreateBrushIndirect(LPLOGBRUSH lpLogBrush)
+ // Creates a logical brush from style, color, and pattern specified in the LOGPRUSH struct.
+ {
+ assert(m_pData);
+ HBRUSH hBrush = ::CreateBrushIndirect(lpLogBrush);
+ Attach(hBrush);
+ return hBrush;
+ }
+
+ inline HBRUSH CBrush::CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec)
+ // Creates a logical brush that has the pattern specified by the specified device-independent bitmap (DIB).
+ {
+ assert(m_pData);
+ HBRUSH hBrush = ::CreateDIBPatternBrush(hglbDIBPacked, fuColorSpec);
+ Attach(hBrush);
+ return hBrush;
+ }
+
+ inline HBRUSH CBrush::CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT nUsage)
+ // Creates a logical brush that has the pattern specified by the device-independent bitmap (DIB).
+ {
+ assert(m_pData);
+ HBRUSH hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage);
+ Attach(hBrush);
+ return hBrush;
+ }
+
+#endif // !defined(_WIN32_WCE)
+
+ inline HBRUSH CBrush::CreatePatternBrush(CBitmap* pBitmap)
+ // Creates a logical brush with the specified bitmap pattern. The bitmap can be a DIB section bitmap,
+ // which is created by the CreateDIBSection function, or it can be a device-dependent bitmap.
+ {
+ assert(m_pData);
+ assert(pBitmap);
+ HBRUSH hBrush = ::CreatePatternBrush(*pBitmap);
+ Attach(hBrush);
+ return hBrush;
+ }
+
+ inline LOGBRUSH CBrush::GetLogBrush() const
+ // Retrieves the LOGBRUSH structure that defines the style, color, and pattern of a physical brush.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ LOGBRUSH LogBrush = {0};
+ ::GetObject (m_pData->hGDIObject, sizeof(LOGBRUSH), &LogBrush);
+ return LogBrush;
+ }
+
+
+ ///////////////////////////////////////////////
+ // Definitions of the CFont class
+ //
+ inline CFont::CFont()
+ {
+ }
+
+ inline CFont::CFont(HFONT hFont)
+ {
+ assert(m_pData);
+ Attach(hFont);
+ }
+
+ inline CFont::CFont(const LOGFONT* lpLogFont)
+ {
+ assert(m_pData);
+ Attach( ::CreateFontIndirect(lpLogFont) );
+ }
+
+ inline CFont::operator HFONT() const
+ {
+ assert(m_pData);
+ return (HFONT)m_pData->hGDIObject;
+ }
+
+ inline CFont::~CFont()
+ {
+ }
+
+ inline HFONT CFont::CreateFontIndirect(const LOGFONT* lpLogFont)
+ // Creates a logical font that has the specified characteristics.
+ {
+ assert(m_pData);
+ HFONT hFont = ::CreateFontIndirect(lpLogFont);
+ Attach(hFont);
+ return hFont;
+ }
+
+ inline HFONT CFont::CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, CDC* pDC /*= NULL*/, BOOL bBold /*= FALSE*/, BOOL bItalic /*= FALSE*/)
+ // Creates a font of a specified typeface and point size.
+ {
+ LOGFONT logFont = { 0 };
+ logFont.lfCharSet = DEFAULT_CHARSET;
+ logFont.lfHeight = nPointSize;
+
+ lstrcpy(logFont.lfFaceName, lpszFaceName);
+
+ if (bBold)
+ logFont.lfWeight = FW_BOLD;
+ if (bItalic)
+ logFont.lfItalic = (BYTE)TRUE;
+
+ return CreatePointFontIndirect(&logFont, pDC);
+ }
+
+ inline HFONT CFont::CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC /* = NULL*/)
+ // Creates a font of a specified typeface and point size.
+ // This function automatically converts the height in lfHeight to logical units using the specified device context.
+ {
+ HDC hDC = pDC? pDC->GetHDC() : NULL;
+ HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(HWND_DESKTOP);
+
+ // convert nPointSize to logical units based on hDC
+ LOGFONT logFont = *lpLogFont;
+
+#ifndef _WIN32_WCE
+ POINT pt = { 0, 0 };
+ pt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), logFont.lfHeight, 720); // 72 points/inch, 10 decipoints/point
+ ::DPtoLP(hDC1, &pt, 1);
+
+ POINT ptOrg = { 0, 0 };
+ ::DPtoLP(hDC1, &ptOrg, 1);
+
+ logFont.lfHeight = -abs(pt.y - ptOrg.y);
+#else // CE specific
+ // DP and LP are always the same on CE
+ logFont.lfHeight = -abs(((::GetDeviceCaps(hDC1, LOGPIXELSY)* logFont.lfHeight)/ 720));
+#endif // _WIN32_WCE
+
+ if (hDC == NULL)
+ ::ReleaseDC (NULL, hDC1);
+
+ return CreateFontIndirect (&logFont);
+ }
+
+#ifndef _WIN32_WCE
+
+ inline HFONT CFont::CreateFont(int nHeight, int nWidth, int nEscapement,
+ int nOrientation, int nWeight, DWORD dwItalic, DWORD dwUnderline,
+ DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision,
+ DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily,
+ LPCTSTR lpszFacename)
+ // Creates a logical font with the specified characteristics.
+ {
+ HFONT hFont = ::CreateFont(nHeight, nWidth, nEscapement,
+ nOrientation, nWeight, dwItalic, dwUnderline, dwStrikeOut,
+ dwCharSet, dwOutPrecision, dwClipPrecision, dwQuality,
+ dwPitchAndFamily, lpszFacename);
+
+ Attach(hFont);
+ return hFont;
+ }
+#endif // #ifndef _WIN32_WCE
+
+ inline LOGFONT CFont::GetLogFont() const
+ // Retrieves the Logfont structure that contains font attributes.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ LOGFONT LogFont = {0};
+ ::GetObject(m_pData->hGDIObject, sizeof(LOGFONT), &LogFont);
+ return LogFont;
+ }
+
+
+ ///////////////////////////////////////////////
+ // Definitions of the CPalette class
+ //
+ inline CPalette::CPalette()
+ {
+ }
+
+ inline CPalette::CPalette(HPALETTE hPalette)
+ {
+ Attach(hPalette);
+ }
+
+ inline CPalette::operator HPALETTE() const
+ {
+ assert(m_pData);
+ return (HPALETTE)m_pData->hGDIObject;
+ }
+
+ inline CPalette::~CPalette ()
+ {
+ }
+
+ inline HPALETTE CPalette::CreatePalette(LPLOGPALETTE lpLogPalette)
+ // Creates a logical palette from the information in the specified LOGPALETTE structure.
+ {
+ assert(m_pData);
+ HPALETTE hPalette = ::CreatePalette (lpLogPalette);
+ Attach(hPalette);
+ return hPalette;
+ }
+
+#ifndef _WIN32_WCE
+ inline HPALETTE CPalette::CreateHalftonePalette(CDC* pDC)
+ // Creates a halftone palette for the specified device context (DC).
+ {
+ assert(m_pData);
+ assert(pDC);
+ HPALETTE hPalette = ::CreateHalftonePalette(pDC->GetHDC());
+ Attach(hPalette);
+ return hPalette;
+ }
+#endif // !_WIN32_WCE
+
+ inline int CPalette::GetEntryCount() const
+ // Retrieve the number of entries in the palette.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ WORD nEntries = 0;
+ ::GetObject(m_pData->hGDIObject, sizeof(WORD), &nEntries);
+ return (int)nEntries;
+ }
+
+ inline UINT CPalette::GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const
+ // Retrieves a specified range of palette entries from the palette.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::GetPaletteEntries((HPALETTE)m_pData->hGDIObject, nStartIndex, nNumEntries, lpPaletteColors);
+ }
+
+ inline UINT CPalette::SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors)
+ // Sets RGB (red, green, blue) color values and flags in a range of entries in the palette.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::SetPaletteEntries((HPALETTE)m_pData->hGDIObject, nStartIndex, nNumEntries, lpPaletteColors);
+ }
+
+#ifndef _WIN32_WCE
+ inline void CPalette::AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors)
+ // Replaces entries in the palette.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ ::AnimatePalette((HPALETTE)m_pData->hGDIObject, nStartIndex, nNumEntries, lpPaletteColors);
+ }
+
+ inline BOOL CPalette::ResizePalette(UINT nNumEntries)
+ // Increases or decreases the size of the palette based on the specified value.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::ResizePalette((HPALETTE)m_pData->hGDIObject, nNumEntries);
+ }
+#endif // !_WIN32_WCE
+
+ inline UINT CPalette::GetNearestPaletteIndex(COLORREF crColor) const
+ // Retrieves the index for the entry in the palette most closely matching a specified color value.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::GetNearestPaletteIndex((HPALETTE)m_pData->hGDIObject, crColor);
+ }
+
+
+ ///////////////////////////////////////////////
+ // Declarations for the CPen class
+ //
+ inline CPen::CPen()
+ {
+ }
+
+ inline CPen::CPen(HPEN hPen)
+ {
+ Attach(hPen);
+ }
+
+ inline CPen::CPen(int nPenStyle, int nWidth, COLORREF crColor)
+ {
+ assert(m_pData);
+ Attach( ::CreatePen(nPenStyle, nWidth, crColor) );
+ }
+
+#ifndef _WIN32_WCE
+ inline CPen::CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount /*= 0*/, const DWORD* lpStyle /*= NULL*/)
+ {
+ assert(m_pData);
+ Attach( ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle) );
+ }
+#endif // !_WIN32_WCE
+
+ inline CPen::operator HPEN () const
+ {
+ assert(m_pData);
+ return (HPEN)m_pData->hGDIObject;
+ }
+
+ inline CPen::~CPen()
+ {
+ }
+
+ inline HPEN CPen::CreatePen(int nPenStyle, int nWidth, COLORREF crColor)
+ // Creates a logical pen that has the specified style, width, and color.
+ {
+ assert(m_pData);
+ HPEN hPen = ::CreatePen(nPenStyle, nWidth, crColor);
+ Attach(hPen);
+ return hPen;
+ }
+
+ inline HPEN CPen::CreatePenIndirect(LPLOGPEN lpLogPen)
+ // Creates a logical cosmetic pen that has the style, width, and color specified in a structure.
+ {
+ assert(m_pData);
+ HPEN hPen = ::CreatePenIndirect(lpLogPen);
+ Attach(hPen);
+ return hPen;
+ }
+
+ inline LOGPEN CPen::GetLogPen() const
+ {
+ // Retrieves the LOGPEN struct that specifies the pen's style, width, and color.
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+
+ LOGPEN LogPen = {0};
+ ::GetObject(m_pData->hGDIObject, sizeof(LOGPEN), &LogPen);
+ return LogPen;
+ }
+
+#ifndef _WIN32_WCE
+ inline HPEN CPen::ExtCreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount /* = 0*/, const DWORD* lpStyle /*= NULL*/)
+ // Creates a logical cosmetic or geometric pen that has the specified style, width, and brush attributes.
+ {
+ assert(m_pData);
+ HPEN hPen = ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle);
+ Attach(hPen);
+ return hPen;
+ }
+
+ inline EXTLOGPEN CPen::GetExtLogPen() const
+ // Retrieves the EXTLOGPEN struct that specifies the pen's style, width, color and brush attributes.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+
+ EXTLOGPEN ExLogPen = {0};
+ ::GetObject(m_pData->hGDIObject, sizeof(EXTLOGPEN), &ExLogPen);
+ return ExLogPen;
+ }
+#endif // !_WIN32_WCE
+
+
+ ///////////////////////////////////////////////
+ // Definitions of the CRgn class
+ //
+ inline CRgn::CRgn()
+ {
+ }
+
+ inline CRgn::CRgn(HRGN hRgn)
+ {
+ assert(m_pData);
+ Attach(hRgn);
+ }
+
+ inline CRgn::operator HRGN() const
+ {
+ assert(m_pData);
+ return (HRGN)m_pData->hGDIObject;
+ }
+
+ inline CRgn::~CRgn()
+ {
+ }
+
+ inline HRGN CRgn::CreateRectRgn(int x1, int y1, int x2, int y2)
+ // Creates a rectangular region.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreateRectRgn(x1, y1, x2, y2);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline HRGN CRgn::CreateRectRgnIndirect(const RECT& rc)
+ // Creates a rectangular region.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreateRectRgnIndirect(&rc);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+#ifndef _WIN32_WCE
+ inline HRGN CRgn::CreateEllipticRgn(int x1, int y1, int x2, int y2)
+ // Creates an elliptical region.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreateEllipticRgn(x1, y1, x2, y2);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline HRGN CRgn::CreateEllipticRgnIndirect(const RECT& rc)
+ // Creates an elliptical region.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreateEllipticRgnIndirect(&rc);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline HRGN CRgn::CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode)
+ // Creates a polygonal region.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreatePolygonRgn(lpPoints, nCount, nMode);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline HRGN CRgn::CreatePolyPolygonRgn(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount, int nPolyFillMode)
+ // Creates a region consisting of a series of polygons. The polygons can overlap.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreatePolyPolygonRgn(lpPoints, lpPolyCounts, nCount, nPolyFillMode);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline HRGN CRgn::CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3)
+ // Creates a rectangular region with rounded corners.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::CreateRoundRectRgn(x1, y1, x2, y2, x3, y3);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline HRGN CRgn::CreateFromPath(HDC hDC)
+ // Creates a region from the path that is selected into the specified device context.
+ // The resulting region uses device coordinates.
+ {
+ assert(m_pData);
+ assert(hDC != NULL);
+ HRGN hRgn = ::PathToRegion(hDC);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+#endif // !_WIN32_WCE
+
+ inline HRGN CRgn::CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData)
+ // Creates a region from the specified region and transformation data.
+ {
+ assert(m_pData);
+ HRGN hRgn = ::ExtCreateRegion(lpXForm, nCount, pRgnData);
+ Attach(hRgn);
+ return hRgn;
+ }
+
+ inline void CRgn::SetRectRgn(int x1, int y1, int x2, int y2)
+ // converts the region into a rectangular region with the specified coordinates.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ ::SetRectRgn((HRGN)m_pData->hGDIObject, x1, y1, x2, y2);
+ }
+
+ inline void CRgn::SetRectRgn(const RECT& rc)
+ // converts the region into a rectangular region with the specified coordinates.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ ::SetRectRgn((HRGN)m_pData->hGDIObject, rc.left, rc.top, rc.right, rc.bottom);
+ }
+
+ inline int CRgn::CombineRgn(CRgn* pRgnSrc1, CRgn* pRgnSrc2, int nCombineMode)
+ // Combines two sepcified regions and stores the result.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ assert(pRgnSrc1);
+ assert(pRgnSrc2);
+ return ::CombineRgn((HRGN)m_pData->hGDIObject, *pRgnSrc1, *pRgnSrc2, nCombineMode);
+ }
+
+ inline int CRgn::CombineRgn(CRgn* pRgnSrc, int nCombineMode)
+ // Combines the sepcified region with the current region.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ assert(pRgnSrc);
+ return ::CombineRgn((HRGN)m_pData->hGDIObject, (HRGN)m_pData->hGDIObject, *pRgnSrc, nCombineMode);
+ }
+
+ inline int CRgn::CopyRgn(CRgn* pRgnSrc)
+ // Assigns the specified region to the current region.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject == NULL);
+ assert(pRgnSrc);
+ return ::CombineRgn((HRGN)m_pData->hGDIObject, *pRgnSrc, NULL, RGN_COPY);
+ }
+
+ inline BOOL CRgn::EqualRgn(CRgn* pRgn) const
+ // Checks the two specified regions to determine whether they are identical.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ assert(pRgn);
+ return ::EqualRgn((HRGN)m_pData->hGDIObject, *pRgn);
+ }
+
+ inline int CRgn::OffsetRgn(int x, int y)
+ // Moves a region by the specified offsets.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::OffsetRgn((HRGN)m_pData->hGDIObject, x, y);
+ }
+
+ inline int CRgn::OffsetRgn(POINT& pt)
+ // Moves a region by the specified offsets.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::OffsetRgn((HRGN)m_pData->hGDIObject, pt.x, pt.y);
+ }
+
+ inline int CRgn::GetRgnBox(RECT& rc) const
+ // Retrieves the bounding rectangle of the region, and stores it in the specified RECT.
+ // The return value indicates the region's complexity: NULLREGION;SIMPLEREGION; or COMPLEXREGION.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::GetRgnBox((HRGN)m_pData->hGDIObject, &rc);
+ }
+
+ inline int CRgn::GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const
+ // Fills the specified buffer with data describing a region.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return (int)::GetRegionData((HRGN)m_pData->hGDIObject, nDataSize, lpRgnData);
+ }
+
+ inline BOOL CRgn::PtInRegion(int x, int y) const
+ // Determines whether the specified point is inside the specified region.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::PtInRegion((HRGN)m_pData->hGDIObject, x, y);
+ }
+
+ inline BOOL CRgn::PtInRegion(POINT& pt) const
+ // Determines whether the specified point is inside the specified region.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::PtInRegion((HRGN)m_pData->hGDIObject, pt.x, pt.y);
+ }
+
+ inline BOOL CRgn::RectInRegion(const RECT& rc) const
+ // Determines whether the specified rect is inside the specified region.
+ {
+ assert(m_pData);
+ assert(m_pData->hGDIObject != NULL);
+ return ::RectInRegion((HRGN)m_pData->hGDIObject, &rc);
+ }
+
+
+ ///////////////////////////////////////////////
+ // Definitions of the CDC class
+ //
+ inline CDC::CDC()
+ {
+ // Allocate memory for our data members
+ m_pData = new DataMembers;
+
+ // Assign values to our data members
+ m_pData->hDC = 0;
+ m_pData->Count = 1L;
+ m_pData->bRemoveHDC = TRUE;
+ m_pData->hWnd = 0;
+ }
+
+ inline CDC::CDC(HDC hDC, HWND hWnd /*= 0*/)
+ // This constructor assigns an existing HDC to the CDC
+ // The HDC WILL be released or deleted when the CDC object is destroyed
+ // The hWnd paramter is only used in WindowsCE. It specifies the HWND of a Window or
+ // Window Client DC
+
+ // Note: this constructor permits a call like this:
+ // CDC MyCDC = SomeHDC;
+ // or
+ // CDC MyCDC = ::CreateCompatibleDC(SomeHDC);
+ // or
+ // CDC MyCDC = ::GetDC(SomeHWND);
+ {
+ UNREFERENCED_PARAMETER(hWnd);
+ assert(hDC);
+
+ CDC* pDC = GetApp()->GetCDCFromMap(hDC);
+ if (pDC)
+ {
+ m_pData = pDC->m_pData;
+ InterlockedIncrement(&m_pData->Count);
+ }
+ else
+ {
+ // Allocate memory for our data members
+ m_pData = new DataMembers;
+
+ // Assign values to our data members
+ m_pData->hDC = hDC;
+ m_pData->Count = 1L;
+ m_pData->bRemoveHDC = TRUE;
+ m_pData->nSavedDCState = ::SaveDC(hDC);
+#ifndef _WIN32_WCE
+ m_pData->hWnd = ::WindowFromDC(hDC);
+#else
+ m_pData->hWnd = hWnd;
+#endif
+ if (m_pData->hWnd)
+ AddToMap();
+ }
+ }
+
+#ifndef _WIN32_WCE
+ inline void CDC::operator = (const HDC hDC)
+ // Note: this assignment operater permits a call like this:
+ // CDC MyCDC;
+ // MyCDC = SomeHDC;
+ {
+ Attach(hDC);
+ }
+#endif
+
+ inline CDC::CDC(const CDC& rhs) // Copy constructor
+ // The copy constructor is called when a temporary copy of the CDC needs to be created.
+ // This can happen when a CDC is passed by value in a function call. Each CDC copy manages
+ // the same Device Context and GDI objects.
+ {
+ m_pData = rhs.m_pData;
+ InterlockedIncrement(&m_pData->Count);
+ }
+
+ inline CDC& CDC::operator = (const CDC& rhs)
+ // Note: A copy of a CDC is a clone of the original.
+ // Both objects manipulate the one HDC
+ {
+ if (this != &rhs)
+ {
+ InterlockedIncrement(&rhs.m_pData->Count);
+ Release();
+ m_pData = rhs.m_pData;
+ }
+
+ return *this;
+ }
+
+ inline CDC::~CDC ()
+ {
+ Release();
+ }
+
+ inline void CDC::AddToMap()
+ // Store the HDC and CDC pointer in the HDC map
+ {
+ assert( GetApp() );
+ assert(m_pData->hDC);
+ GetApp()->m_csMapLock.Lock();
+
+ assert(m_pData->hDC);
+ assert(!GetApp()->GetCDCFromMap(m_pData->hDC));
+
+ GetApp()->m_mapHDC.insert(std::make_pair(m_pData->hDC, this));
+ GetApp()->m_csMapLock.Release();
+ }
+
+ inline void CDC::Attach(HDC hDC, HWND hWnd /* = 0*/)
+ // Attaches a HDC to the CDC object.
+ // The HDC will be automatically deleted or released when the destructor is called.
+ // The hWnd parameter is only used on WindowsCE. It specifies the HWND of a Window or
+ // Window Client DC
+ {
+ UNREFERENCED_PARAMETER(hWnd);
+ assert(m_pData);
+ assert(0 == m_pData->hDC);
+ assert(hDC);
+
+ CDC* pDC = GetApp()->GetCDCFromMap(hDC);
+ if (pDC)
+ {
+ delete m_pData;
+ m_pData = pDC->m_pData;
+ InterlockedIncrement(&m_pData->Count);
+ }
+ else
+ {
+ m_pData->hDC = hDC;
+
+#ifndef _WIN32_WCE
+ m_pData->hWnd = ::WindowFromDC(hDC);
+#else
+ m_pData->hWnd = hWnd;
+#endif
+
+ if (m_pData->hWnd == 0)
+ AddToMap();
+ m_pData->nSavedDCState = ::SaveDC(hDC);
+ }
+ }
+
+ inline HDC CDC::Detach()
+ // Detaches the HDC from this object.
+ {
+ assert(m_pData);
+ assert(m_pData->hDC);
+
+ GetApp()->m_csMapLock.Lock();
+ RemoveFromMap();
+ HDC hDC = m_pData->hDC;
+ m_pData->hDC = 0;
+
+ if (m_pData->Count)
+ {
+ if (InterlockedDecrement(&m_pData->Count) == 0)
+ {
+ delete m_pData;
+ }
+ }
+
+ GetApp()->m_csMapLock.Release();
+
+ // Assign values to our data members
+ m_pData = new DataMembers;
+ m_pData->hDC = 0;
+ m_pData->Count = 1L;
+ m_pData->bRemoveHDC = TRUE;
+ m_pData->hWnd = 0;
+
+ return hDC;
+ }
+
+ // Initialization
+ inline BOOL CDC::CreateCompatibleDC(CDC* pDC)
+ // Returns a memory device context (DC) compatible with the specified device.
+ {
+ assert(m_pData->hDC == NULL);
+ HDC hdcSource = (pDC == NULL)? NULL : pDC->GetHDC();
+ HDC hDC = ::CreateCompatibleDC(hdcSource);
+ if (hDC)
+ {
+ m_pData->hDC = hDC;
+ AddToMap();
+ }
+ return (hDC != NULL); // boolean expression
+ }
+
+ inline BOOL CDC::CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData)
+ // Returns a device context (DC) for a device using the specified name.
+ {
+ assert(m_pData->hDC == NULL);
+ HDC hDC = ::CreateDC(lpszDriver, lpszDevice, lpszOutput, pInitData);
+ if (hDC)
+ {
+ m_pData->hDC = hDC;
+ AddToMap();
+ }
+ return (hDC != NULL); // boolean expression
+ }
+
+#ifndef _WIN32_WCE
+ inline BOOL CDC::CreateIC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData)
+ {
+ assert(m_pData->hDC == NULL);
+ HDC hDC = ::CreateIC(lpszDriver, lpszDevice, lpszOutput, pInitData);
+ if (hDC)
+ {
+ m_pData->hDC = hDC;
+ AddToMap();
+ }
+ return (hDC != NULL); // boolean expression
+ }
+#endif
+
+ inline void CDC::DrawBitmap(int x, int y, int cx, int cy, CBitmap& Bitmap, COLORREF clrMask)
+ // Draws the specified bitmap to the specified DC using the mask colour provided as the transparent colour
+ // Suitable for use with a Window DC or a memory DC
+ {
+ // Create the Image memory DC
+ CMemDC dcImage(this);
+ dcImage.SetBkColor(clrMask);
+ dcImage.SelectObject(&Bitmap);
+
+ // Create the Mask memory DC
+ CMemDC dcMask(this);
+ dcMask.CreateBitmap(cx, cy, 1, 1, NULL);
+ dcMask.BitBlt(0, 0, cx, cy, &dcImage, 0, 0, SRCCOPY);
+
+ // Mask the image to 'this' DC
+ BitBlt(x, y, cx, cy, &dcImage, 0, 0, SRCINVERT);
+ BitBlt(x, y, cx, cy, &dcMask, 0, 0, SRCAND);
+ BitBlt(x, y, cx, cy, &dcImage, 0, 0, SRCINVERT);
+ }
+
+ inline CDC* CDC::AddTempHDC(HDC hDC, HWND hWnd)
+ // Returns the CDC object associated with the device context handle
+ // The HDC is removed when the CDC is destroyed
+ {
+ assert( GetApp() );
+ CDC* pDC = new CDC;
+ pDC->m_pData->hDC = hDC;
+ GetApp()->AddTmpDC(pDC);
+ pDC->m_pData->bRemoveHDC = TRUE;
+ pDC->m_pData->hWnd = hWnd;
+ return pDC;
+ }
+
+ inline void CDC::GradientFill(COLORREF Color1, COLORREF Color2, const RECT& rc, BOOL bVertical)
+ // An efficient color gradient filler compatible with all Windows operating systems
+ {
+ int Width = rc.right - rc.left;
+ int Height = rc.bottom - rc.top;
+
+ int r1 = GetRValue(Color1);
+ int g1 = GetGValue(Color1);
+ int b1 = GetBValue(Color1);
+
+ int r2 = GetRValue(Color2);
+ int g2 = GetGValue(Color2);
+ int b2 = GetBValue(Color2);
+
+ COLORREF OldBkColor = GetBkColor();
+
+ if (bVertical)
+ {
+ for(int i=0; i < Width; ++i)
+ {
+ int r = r1 + (i * (r2-r1) / Width);
+ int g = g1 + (i * (g2-g1) / Width);
+ int b = b1 + (i * (b2-b1) / Width);
+ SetBkColor(RGB(r, g, b));
+ CRect line( i + rc.left, rc.top, i + 1 + rc.left, rc.top+Height);
+ ExtTextOut(0, 0, ETO_OPAQUE, line, NULL, 0, 0);
+ }
+ }
+ else
+ {
+ for(int i=0; i < Height; ++i)
+ {
+ int r = r1 + (i * (r2-r1) / Height);
+ int g = g1 + (i * (g2-g1) / Height);
+ int b = b1 + (i * (b2-b1) / Height);
+ SetBkColor(RGB(r, g, b));
+ CRect line(rc.left, i + rc.top, rc.left+Width, i + 1 + rc.top);
+ ExtTextOut(0, 0, ETO_OPAQUE, line, NULL, 0, 0);
+ }
+ }
+
+ SetBkColor(OldBkColor);
+ }
+
+ inline void CDC::Release()
+ {
+ GetApp()->m_csMapLock.Lock();
+
+ if (m_pData->Count)
+ {
+ if (InterlockedDecrement(&m_pData->Count) == 0)
+ {
+ Destroy();
+ delete m_pData;
+ m_pData = 0;
+ }
+ }
+
+ GetApp()->m_csMapLock.Release();
+ }
+
+ inline BOOL CDC::RemoveFromMap()
+ {
+ BOOL Success = FALSE;
+
+ if( GetApp() )
+ {
+ // Allocate an iterator for our HDC map
+ std::map<HDC, CDC*, CompareHDC>::iterator m;
+
+ CWinApp* pApp = GetApp();
+ if (pApp)
+ {
+ // Erase the CDC pointer entry from the map
+ pApp->m_csMapLock.Lock();
+ m = pApp->m_mapHDC.find(m_pData->hDC);
+ if (m != pApp->m_mapHDC.end())
+ {
+ pApp->m_mapHDC.erase(m);
+ Success = TRUE;
+ }
+
+ pApp->m_csMapLock.Release();
+ }
+ }
+ return Success;
+ }
+
+ inline void CDC::SolidFill(COLORREF Color, const RECT& rc)
+ // Fills a rectangle with a solid color
+ {
+ COLORREF OldColor = SetBkColor(Color);
+ ExtTextOut(0, 0, ETO_OPAQUE, &rc, NULL, 0, 0);
+ SetBkColor(OldColor);
+ }
+
+ // Bitmap functions
+ inline CBitmap* CDC::CreateCompatibleBitmap(CDC* pDC, int cx, int cy)
+ // Creates a compatible bitmap and selects it into the device context.
+ {
+ assert(m_pData->hDC);
+ assert(pDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ pBitmap->CreateCompatibleBitmap(pDC, cx, cy);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return SelectObject(pBitmap);
+ }
+
+ inline CBitmap* CDC::CreateBitmap(int cx, int cy, UINT Planes, UINT BitsPerPixel, LPCVOID pvColors)
+ // Creates a bitmap and selects it into the device context.
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ pBitmap->CreateBitmap(cx, cy, Planes, BitsPerPixel, pvColors);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return SelectObject(pBitmap);
+ }
+
+#ifndef _WIN32_WCE
+ inline CBitmap* CDC::CreateBitmapIndirect (LPBITMAP lpBitmap)
+ // Creates a bitmap and selects it into the device context.
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ pBitmap->CreateBitmapIndirect(lpBitmap);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return SelectObject(pBitmap);
+ }
+
+ inline CBitmap* CDC::CreateDIBitmap(CDC* pDC, const BITMAPINFOHEADER& bmih, DWORD fdwInit, LPCVOID lpbInit,
+ BITMAPINFO& bmi, UINT fuUsage)
+ // Creates a bitmap and selects it into the device context.
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+ assert(pDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ pBitmap->CreateDIBitmap(pDC, &bmih, fdwInit, lpbInit, &bmi, fuUsage);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return SelectObject(pBitmap);
+ }
+#endif
+
+ inline CBitmap* CDC::CreateDIBSection(CDC* pDC, const BITMAPINFO& bmi, UINT iUsage, LPVOID *ppvBits,
+ HANDLE hSection, DWORD dwOffset)
+ // Creates a bitmap and selects it into the device context.
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+ assert(pDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ pBitmap->CreateDIBSection(pDC, &bmi, iUsage, ppvBits, hSection, dwOffset);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return SelectObject(pBitmap);
+ }
+
+ inline void CDC::Destroy()
+ // Deletes or releases the device context and returns the CDC object to its
+ // default state, ready for reuse.
+ {
+ if (m_pData->hDC)
+ {
+ RemoveFromMap();
+ if (m_pData->bRemoveHDC)
+ {
+ // Return the DC back to its initial state
+ ::RestoreDC(m_pData->hDC, m_pData->nSavedDCState);
+
+ // We need to release a Window DC, and delete a memory DC
+ if (m_pData->hWnd)
+ ::ReleaseDC(m_pData->hWnd, m_pData->hDC);
+ else
+ if (!::DeleteDC(m_pData->hDC))
+ ::ReleaseDC(NULL, m_pData->hDC);
+
+ m_pData->hDC = 0;
+ m_pData->hWnd = 0;
+ m_pData->bRemoveHDC = TRUE;
+ }
+ }
+
+ // RemoveFromMap();
+ }
+
+ inline BITMAP CDC::GetBitmapData() const
+ // Retrieves the BITMAP information for the current HBITMAP.
+ {
+ assert(m_pData->hDC);
+
+ HBITMAP hbm = (HBITMAP)::GetCurrentObject(m_pData->hDC, OBJ_BITMAP);
+ BITMAP bm = {0};
+ ::GetObject(hbm, sizeof(bm), &bm);
+ return bm;
+ }
+
+ inline CBitmap* CDC::LoadBitmap(UINT nID)
+ // Loads a bitmap from the resource and selects it into the device context
+ {
+ return LoadBitmap(MAKEINTRESOURCE(nID));
+ }
+
+ inline CBitmap* CDC::LoadBitmap(LPCTSTR lpszName)
+ // Loads a bitmap from the resource and selects it into the device context
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ BOOL bResult = pBitmap->LoadBitmap(lpszName);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+
+ return bResult? SelectObject(pBitmap) : NULL;
+ }
+
+ inline CBitmap* CDC::LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad)
+ // Loads a bitmap from the resource and selects it into the device context
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ return LoadImage(nID, cxDesired, cyDesired, fuLoad);
+ }
+
+ inline CBitmap* CDC::LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad)
+ // Loads a bitmap from the resource and selects it into the device context
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ BOOL bResult = pBitmap->LoadImage(lpszName, cxDesired, cyDesired, fuLoad);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return bResult? SelectObject(pBitmap) : NULL;
+ }
+
+ inline CBitmap* CDC::LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_
+ // Loads a predefined system bitmap and selects it into the device context
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ BOOL bResult = pBitmap->LoadOEMBitmap(nIDBitmap);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return bResult? SelectObject(pBitmap) : NULL;
+ }
+
+#ifndef _WIN32_WCE
+ inline CBitmap* CDC::CreateMappedBitmap(UINT nIDBitmap, UINT nFlags /*= 0*/, LPCOLORMAP lpColorMap /*= NULL*/, int nMapSize /*= 0*/)
+ // creates and selects a new bitmap using the bitmap data and colors specified by the bitmap resource and the color mapping information.
+ // Returns a pointer to the old bitmap selected out of the device context
+ {
+ assert(m_pData->hDC);
+
+ CBitmap* pBitmap = new CBitmap;
+ pBitmap->CreateMappedBitmap(nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize);
+ m_pData->m_vGDIObjects.push_back(pBitmap);
+ return SelectObject(pBitmap);
+ }
+#endif // !_WIN32_WCE
+
+
+ // Brush functions
+#ifndef _WIN32_WCE
+ inline CBrush* CDC::CreateBrushIndirect(LPLOGBRUSH pLogBrush)
+ // Creates the brush and selects it into the device context.
+ // Returns a pointer to the old brush selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CBrush* pBrush = new CBrush;
+ pBrush->CreateBrushIndirect(pLogBrush);
+ m_pData->m_vGDIObjects.push_back(pBrush);
+ return SelectObject(pBrush);
+ }
+
+ inline CBrush* CDC::CreateHatchBrush(int fnStyle, COLORREF rgb)
+ // Creates a brush with the specified hatch pattern and color, and selects it into the device context.
+ // Returns a pointer to the old brush selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CBrush* pBrush = new CBrush;
+ pBrush->CreateHatchBrush(fnStyle, rgb);
+ m_pData->m_vGDIObjects.push_back(pBrush);
+ return SelectObject(pBrush);
+ }
+
+ inline CBrush* CDC::CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec)
+ // Creates a logical from the specified device-independent bitmap (DIB), and selects it into the device context.
+ // Returns a pointer to the old brush selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CBrush* pBrush = new CBrush;
+ pBrush->CreateDIBPatternBrush(hglbDIBPacked, fuColorSpec);
+ m_pData->m_vGDIObjects.push_back(pBrush);
+ return SelectObject(pBrush);
+ }
+
+ inline CBrush* CDC::CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT iUsage)
+ // Creates a logical from the specified device-independent bitmap (DIB), and selects it into the device context.
+ // Returns a pointer to the old brush selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CBrush* pBrush = new CBrush;
+ pBrush->CreateDIBPatternBrushPt(lpPackedDIB, iUsage);
+ m_pData->m_vGDIObjects.push_back(pBrush);
+ return SelectObject(pBrush);
+ }
+#endif
+
+ inline CBrush* CDC::CreatePatternBrush(CBitmap* pBitmap)
+ // Creates the brush with the specified pattern, and selects it into the device context.
+ // Returns a pointer to the old brush selected out of the device context.
+ {
+ assert(m_pData->hDC);
+ assert(pBitmap);
+
+ CBrush* pBrush = new CBrush;
+ pBrush->CreatePatternBrush(pBitmap);
+ m_pData->m_vGDIObjects.push_back(pBrush);
+ return SelectObject(pBrush);
+ }
+
+ inline CBrush* CDC::CreateSolidBrush(COLORREF rgb)
+ // Creates the brush with the specified color, and selects it into the device context.
+ // Returns a pointer to the old brush selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CBrush* pBrush = new CBrush;
+ pBrush->CreateSolidBrush(rgb);
+ m_pData->m_vGDIObjects.push_back(pBrush);
+ return SelectObject(pBrush);
+ }
+
+ inline LOGBRUSH CDC::GetLogBrush() const
+ // Retrieves the current brush information
+ {
+ assert(m_pData->hDC);
+
+ HBRUSH hBrush = (HBRUSH)::GetCurrentObject(m_pData->hDC, OBJ_BRUSH);
+ LOGBRUSH lBrush = {0};
+ ::GetObject(hBrush, sizeof(lBrush), &lBrush);
+ return lBrush;
+ }
+
+
+ // Font functions
+#ifndef _WIN32_WCE
+ inline CFont* CDC::CreateFont (
+ int nHeight, // height of font
+ int nWidth, // average character width
+ int nEscapement, // angle of escapement
+ int nOrientation, // base-line orientation angle
+ int fnWeight, // font weight
+ DWORD fdwItalic, // italic attribute option
+ DWORD fdwUnderline, // underline attribute option
+ DWORD fdwStrikeOut, // strikeout attribute option
+ DWORD fdwCharSet, // character set identifier
+ DWORD fdwOutputPrecision, // output precision
+ DWORD fdwClipPrecision, // clipping precision
+ DWORD fdwQuality, // output quality
+ DWORD fdwPitchAndFamily, // pitch and family
+ LPCTSTR lpszFace // typeface name
+ )
+
+ // Creates a logical font with the specified characteristics.
+ // Returns a pointer to the old font selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CFont* pFont = new CFont;
+ pFont->CreateFont (nHeight, nWidth, nEscapement, nOrientation, fnWeight,
+ fdwItalic, fdwUnderline, fdwStrikeOut, fdwCharSet,
+ fdwOutputPrecision, fdwClipPrecision, fdwQuality,
+ fdwPitchAndFamily, lpszFace);
+ m_pData->m_vGDIObjects.push_back(pFont);
+ return SelectObject(pFont);
+ }
+#endif
+
+ inline CFont* CDC::CreateFontIndirect(LPLOGFONT plf)
+ // Creates a logical font and selects it into the device context.
+ // Returns a pointer to the old font selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CFont* pFont = new CFont;
+ pFont->CreateFontIndirect(plf);
+ m_pData->m_vGDIObjects.push_back(pFont);
+ return SelectObject(pFont);
+ }
+
+ inline LOGFONT CDC::GetLogFont() const
+ // Retrieves the current font information.
+ {
+ assert(m_pData->hDC);
+
+ HFONT hFont = (HFONT)::GetCurrentObject(m_pData->hDC, OBJ_FONT);
+ LOGFONT lFont = {0};
+ ::GetObject(hFont, sizeof(lFont), &lFont);
+ return lFont;
+ }
+
+ // Pen functions
+ inline CPen* CDC::CreatePen (int nStyle, int nWidth, COLORREF rgb)
+ // Creates the pen and selects it into the device context.
+ // Returns a pointer to the old pen selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CPen* pPen = new CPen;
+ pPen->CreatePen(nStyle, nWidth, rgb);
+ m_pData->m_vGDIObjects.push_back(pPen);
+ return SelectObject(pPen);
+ }
+
+ inline CPen* CDC::CreatePenIndirect (LPLOGPEN pLogPen)
+ // Creates the pen and selects it into the device context.
+ // Returns a pointer to the old pen selected out of the device context.
+ {
+ assert(m_pData->hDC);
+
+ CPen* pPen = new CPen;
+ pPen->CreatePenIndirect(pLogPen);
+ m_pData->m_vGDIObjects.push_back(pPen);
+ return SelectObject(pPen);
+ }
+
+ inline LOGPEN CDC::GetLogPen() const
+ // Retrieves the current pen information as a LOGPEN
+ {
+ assert(m_pData->hDC);
+
+ HPEN hPen = (HPEN)::GetCurrentObject(m_pData->hDC, OBJ_PEN);
+ LOGPEN lPen = {0};
+ ::GetObject(hPen, sizeof(lPen), &lPen);
+ return lPen;
+ }
+
+ // Region functions
+ inline int CDC::CreateRectRgn(int left, int top, int right, int bottom)
+ // Creates a rectangular region from the rectangle co-ordinates.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreateRectRgn(left, top, right, bottom);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+
+ inline int CDC::CreateRectRgnIndirect(const RECT& rc)
+ // Creates a rectangular region from the rectangle co-ordinates.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreateRectRgnIndirect(rc);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+
+ inline int CDC::CreateFromData(const XFORM* Xform, DWORD nCount, const RGNDATA *pRgnData)
+ // Creates a region from the specified region data and tranformation data.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ // Notes: GetRegionData can be used to get a region's data
+ // If the XFROM pointer is NULL, the identity transformation is used.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreateFromData(Xform, nCount, pRgnData);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+
+
+#ifndef _WIN32_WCE
+ inline int CDC::CreateEllipticRgn(int left, int top, int right, int bottom)
+ // Creates the ellyiptical region from the bounding rectangle co-ordinates
+ // and selects it into the device context.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreateEllipticRgn(left, top, right, bottom);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+
+ inline int CDC::CreateEllipticRgnIndirect(const RECT& rc)
+ // Creates the ellyiptical region from the bounding rectangle co-ordinates
+ // and selects it into the device context.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreateEllipticRgnIndirect(rc);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+
+ inline int CDC::CreatePolygonRgn(LPPOINT ppt, int cPoints, int fnPolyFillMode)
+ // Creates the polygon region from the array of points and selects it into
+ // the device context. The polygon is presumed closed.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreatePolygonRgn(ppt, cPoints, fnPolyFillMode);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+
+ inline int CDC::CreatePolyPolygonRgn(LPPOINT ppt, LPINT pPolyCounts, int nCount, int fnPolyFillMode)
+ // Creates the polygon region from a series of polygons.The polygons can overlap.
+ // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR.
+ {
+ assert(m_pData->hDC);
+
+ CRgn* pRgn = new CRgn;
+ pRgn->CreatePolyPolygonRgn(ppt, pPolyCounts, nCount, fnPolyFillMode);
+ m_pData->m_vGDIObjects.push_back(pRgn);
+ return SelectClipRgn(pRgn);
+ }
+#endif
+
+
+ // Wrappers for WinAPI functions
+
+ inline int CDC::GetDeviceCaps (int nIndex) const
+ // Retrieves device-specific information for the specified device.
+ {
+ assert(m_pData->hDC);
+ return ::GetDeviceCaps(m_pData->hDC, nIndex);
+ }
+
+ // Brush Functions
+#ifdef GetDCBrushColor
+ inline COLORREF CDC::GetDCBrushColor() const
+ {
+ assert(m_pData->hDC);
+ return ::GetDCBrushColor(m_pData->hDC);
+ }
+
+ inline COLORREF CDC::SetDCBrushColor(COLORREF crColor) const
+ {
+ assert(m_pData->hDC);
+ return ::SetDCBrushColor(m_pData->hDC, crColor);
+ }
+#endif
+
+ // Clipping functions
+ inline int CDC::ExcludeClipRect(int Left, int Top, int Right, int BottomRect)
+ // Creates a new clipping region that consists of the existing clipping region minus the specified rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::ExcludeClipRect(m_pData->hDC, Left, Top, Right, BottomRect);
+ }
+
+ inline int CDC::ExcludeClipRect(const RECT& rc)
+ // Creates a new clipping region that consists of the existing clipping region minus the specified rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::ExcludeClipRect(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom);
+ }
+
+ inline int CDC::GetClipBox (RECT& rc)
+ // Retrieves the dimensions of the tightest bounding rectangle that can be drawn around the current visible area on the device.
+ {
+ assert(m_pData->hDC);
+ return ::GetClipBox(m_pData->hDC, &rc);
+ }
+
+ inline int CDC::GetClipRgn(HRGN hrgn)
+ // Retrieves a handle identifying the current application-defined clipping region for the specified device context.
+ // hrgn: A handle to an existing region before the function is called.
+ // After the function returns, this parameter is a handle to a copy of the current clipping region.
+ {
+ assert(m_pData->hDC);
+ return ::GetClipRgn(m_pData->hDC, hrgn);
+ }
+
+ inline int CDC::IntersectClipRect(int Left, int Top, int Right, int Bottom)
+ // Creates a new clipping region from the intersection of the current clipping region and the specified rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::IntersectClipRect(m_pData->hDC, Left, Top, Right, Bottom);
+ }
+
+ inline int CDC::IntersectClipRect(const RECT& rc)
+ // Creates a new clipping region from the intersection of the current clipping region and the specified rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::IntersectClipRect(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom);
+ }
+
+ inline BOOL CDC::RectVisible(const RECT& rc)
+ // Determines whether any part of the specified rectangle lies within the clipping region of a device context.
+ {
+ assert(m_pData->hDC);
+ return ::RectVisible (m_pData->hDC, &rc);
+ }
+
+ inline int CDC::SelectClipRgn(CRgn* pRgn)
+ // Selects a region as the current clipping region for the specified device context.
+ // Note: Only a copy of the selected region is used.
+ // To remove a device-context's clipping region, specify a NULL region handle.
+ {
+ assert(m_pData->hDC);
+ return ::SelectClipRgn(m_pData->hDC, pRgn? (HRGN)pRgn->GetHandle() : 0);
+ }
+
+#ifndef _WIN32_WCE
+ inline int CDC::ExtSelectClipRgn(CRgn* pRgn, int fnMode)
+ // Combines the specified region with the current clipping region using the specified mode.
+ {
+ assert(m_pData->hDC);
+ assert(pRgn);
+ return ::ExtSelectClipRgn(m_pData->hDC, *pRgn, fnMode);
+ }
+#endif
+
+ inline CBitmap* CDC::SelectObject(const CBitmap* pBitmap)
+ // Use this to attach an existing bitmap.
+ {
+ assert(m_pData->hDC);
+ assert(pBitmap);
+
+ return FromHandle( (HBITMAP)::SelectObject(m_pData->hDC, *pBitmap) );
+ }
+
+ inline CBrush* CDC::SelectObject(const CBrush* pBrush)
+ // Use this to attach an existing brush.
+ {
+ assert(m_pData->hDC);
+ assert(pBrush);
+
+ return FromHandle( (HBRUSH)::SelectObject(m_pData->hDC, *pBrush) );
+ }
+
+ inline CFont* CDC::SelectObject(const CFont* pFont)
+ // Use this to attach an existing font.
+ {
+ assert(m_pData->hDC);
+ assert(pFont);
+
+ return FromHandle( (HFONT)::SelectObject(m_pData->hDC, *pFont) );
+ }
+
+ inline CPalette* CDC::SelectObject(const CPalette* pPalette)
+ // Use this to attach an existing Palette.
+ {
+ assert(m_pData->hDC);
+ assert(pPalette);
+
+ return FromHandle( (HPALETTE)::SelectObject(m_pData->hDC, *pPalette) );
+ }
+
+ inline CPen* CDC::SelectObject(const CPen* pPen)
+ // Use this to attach an existing pen.
+ {
+ assert(m_pData->hDC);
+ assert(pPen);
+
+ return FromHandle( (HPEN)::SelectObject(m_pData->hDC, *pPen) );
+ }
+
+ inline CPalette* CDC::SelectPalette(const CPalette* pPalette, BOOL bForceBkgnd)
+ // Use this to attach an existing palette.
+ {
+ assert(m_pData->hDC);
+ assert(pPalette);
+
+ return FromHandle( (HPALETTE)::SelectPalette(m_pData->hDC, *pPalette, bForceBkgnd) );
+ }
+#ifndef _WIN32_WCE
+ inline BOOL CDC::PtVisible(int X, int Y)
+ // Determines whether the specified point is within the clipping region of a device context.
+ {
+ assert(m_pData->hDC);
+ return ::PtVisible (m_pData->hDC, X, Y);
+ }
+
+ inline int CDC::OffsetClipRgn(int nXOffset, int nYOffset)
+ // Moves the clipping region of a device context by the specified offsets.
+ {
+ assert(m_pData->hDC);
+ return ::OffsetClipRgn (m_pData->hDC, nXOffset, nYOffset);
+ }
+#endif
+
+ // Point and Line Drawing Functions
+ inline CPoint CDC::GetCurrentPosition() const
+ // Returns the current "MoveToEx" position.
+ {
+ assert(m_pData->hDC);
+ CPoint pt;
+ ::MoveToEx(m_pData->hDC, 0, 0, &pt);
+ ::MoveToEx(m_pData->hDC, pt.x, pt.y, NULL);
+ return pt;
+ }
+
+ inline CPoint CDC::MoveTo(int x, int y) const
+ // Updates the current position to the specified point.
+ {
+ assert(m_pData->hDC);
+ return ::MoveToEx(m_pData->hDC, x, y, NULL);
+ }
+
+ inline CPoint CDC::MoveTo(POINT pt) const
+ // Updates the current position to the specified point
+ {
+ assert(m_pData->hDC);
+ return ::MoveToEx(m_pData->hDC, pt.x, pt.y, NULL);
+ }
+
+ inline BOOL CDC::LineTo(int x, int y) const
+ // Draws a line from the current position up to, but not including, the specified point.
+ {
+ assert(m_pData->hDC);
+ return ::LineTo(m_pData->hDC, x, y);
+ }
+
+ inline BOOL CDC::LineTo(POINT pt) const
+ // Draws a line from the current position up to, but not including, the specified point.
+ {
+ assert(m_pData->hDC);
+ return ::LineTo(m_pData->hDC, pt.x, pt.y);
+ }
+
+#ifndef _WIN32_WCE
+ inline BOOL CDC::Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const
+ // Draws an elliptical arc.
+ {
+ assert(m_pData->hDC);
+ return ::Arc(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4);
+ }
+
+ inline BOOL CDC::Arc(RECT& rc, POINT ptStart, POINT ptEnd) const
+ // Draws an elliptical arc.
+ {
+ assert(m_pData->hDC);
+ return ::Arc(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom,
+ ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
+ }
+
+ inline BOOL CDC::ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const
+ // Draws an elliptical arc.
+ {
+ assert(m_pData->hDC);
+ return ::ArcTo(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4);
+ }
+
+ inline BOOL CDC::ArcTo(RECT& rc, POINT ptStart, POINT ptEnd) const
+ // Draws an elliptical arc.
+ {
+ assert(m_pData->hDC);
+ return ::ArcTo (m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom,
+ ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
+ }
+
+ inline BOOL CDC::AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle) const
+ // Draws a line segment and an arc.
+ {
+ assert(m_pData->hDC);
+ return ::AngleArc(m_pData->hDC, x, y, nRadius, fStartAngle, fSweepAngle);
+ }
+
+ inline int CDC::GetArcDirection() const
+ // Retrieves the current arc direction ( AD_COUNTERCLOCKWISE or AD_CLOCKWISE ).
+ {
+ assert(m_pData->hDC);
+ return ::GetArcDirection(m_pData->hDC);
+ }
+
+ inline int CDC::SetArcDirection(int nArcDirection) const
+ // Sets the current arc direction ( AD_COUNTERCLOCKWISE or AD_CLOCKWISE ).
+ {
+ assert(m_pData->hDC);
+ return ::SetArcDirection(m_pData->hDC, nArcDirection);
+ }
+
+ inline BOOL CDC::PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount) const
+ // Draws a set of line segments and Bzier curves.
+ {
+ assert(m_pData->hDC);
+ return ::PolyDraw(m_pData->hDC, lpPoints, lpTypes, nCount);
+ }
+
+ inline BOOL CDC::Polyline(LPPOINT lpPoints, int nCount) const
+ // Draws a series of line segments by connecting the points in the specified array.
+ {
+ assert(m_pData->hDC);
+ return ::Polyline(m_pData->hDC, lpPoints, nCount);
+ }
+
+ inline BOOL CDC::PolyPolyline(const POINT* lpPoints, const DWORD* lpPolyPoints, int nCount) const
+ // Draws multiple series of connected line segments.
+ {
+ assert(m_pData->hDC);
+ return ::PolyPolyline(m_pData->hDC, lpPoints, lpPolyPoints, nCount);
+ }
+
+ inline BOOL CDC::PolylineTo(const POINT* lpPoints, int nCount) const
+ // Draws one or more straight lines.
+ {
+ assert(m_pData->hDC);
+ return ::PolylineTo(m_pData->hDC, lpPoints, nCount);
+ }
+ inline BOOL CDC::PolyBezier(const POINT* lpPoints, int nCount) const
+ // Draws one or more Bzier curves.
+ {
+ assert(m_pData->hDC);
+ return ::PolyBezier(m_pData->hDC, lpPoints, nCount);
+ }
+
+ inline BOOL CDC::PolyBezierTo(const POINT* lpPoints, int nCount) const
+ // Draws one or more Bzier curves.
+ {
+ assert(m_pData->hDC);
+ return ::PolyBezierTo(m_pData->hDC, lpPoints, nCount );
+ }
+
+ inline COLORREF CDC::GetPixel(int x, int y) const
+ // Retrieves the red, green, blue (RGB) color value of the pixel at the specified coordinates.
+ {
+ assert(m_pData->hDC);
+ return ::GetPixel(m_pData->hDC, x, y);
+ }
+
+ inline COLORREF CDC::GetPixel(POINT pt) const
+ // Retrieves the red, green, blue (RGB) color value of the pixel at the specified coordinates.
+ {
+ assert(m_pData->hDC);
+ return ::GetPixel(m_pData->hDC, pt.x, pt.y);
+ }
+
+ inline COLORREF CDC::SetPixel (int x, int y, COLORREF crColor) const
+ // Sets the pixel at the specified coordinates to the specified color.
+ {
+ assert(m_pData->hDC);
+ return ::SetPixel(m_pData->hDC, x, y, crColor);
+ }
+
+ inline COLORREF CDC::SetPixel(POINT pt, COLORREF crColor) const
+ // Sets the pixel at the specified coordinates to the specified color.
+ {
+ assert(m_pData->hDC);
+ return ::SetPixel(m_pData->hDC, pt.x, pt.y, crColor);
+ }
+
+ inline BOOL CDC::SetPixelV(int x, int y, COLORREF crColor) const
+ // Sets the pixel at the specified coordinates to the closest approximation of the specified color.
+ {
+ assert(m_pData->hDC);
+ return ::SetPixelV(m_pData->hDC, x, y, crColor);
+ }
+
+ inline BOOL CDC::SetPixelV(POINT pt, COLORREF crColor) const
+ // Sets the pixel at the specified coordinates to the closest approximation of the specified color.
+ {
+ assert(m_pData->hDC);
+ return ::SetPixelV(m_pData->hDC, pt.x, pt.y, crColor);
+ }
+#endif
+
+ // Shape Drawing Functions
+ inline void CDC::DrawFocusRect(const RECT& rc) const
+ // Draws a rectangle in the style used to indicate that the rectangle has the focus.
+ {
+ assert(m_pData->hDC);
+ ::DrawFocusRect(m_pData->hDC, &rc);
+ }
+
+ inline BOOL CDC::Ellipse(int x1, int y1, int x2, int y2) const
+ // Draws an ellipse. The center of the ellipse is the center of the specified bounding rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::Ellipse(m_pData->hDC, x1, y1, x2, y2);
+ }
+
+ inline BOOL CDC::Ellipse(const RECT& rc) const
+ // Draws an ellipse. The center of the ellipse is the center of the specified bounding rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::Ellipse(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom);
+ }
+
+ inline BOOL CDC::Polygon(LPPOINT lpPoints, int nCount) const
+ // Draws a polygon consisting of two or more vertices connected by straight lines.
+ {
+ assert(m_pData->hDC);
+ return ::Polygon(m_pData->hDC, lpPoints, nCount);
+ }
+
+ inline BOOL CDC::Rectangle(int x1, int y1, int x2, int y2) const
+ // Draws a rectangle. The rectangle is outlined by using the current pen and filled by using the current brush.
+ {
+ assert(m_pData->hDC);
+ return ::Rectangle(m_pData->hDC, x1, y1, x2, y2);
+ }
+
+ inline BOOL CDC::Rectangle(const RECT& rc) const
+ // Draws a rectangle. The rectangle is outlined by using the current pen and filled by using the current brush.
+ {
+ assert(m_pData->hDC);
+ return ::Rectangle(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom);
+ }
+
+ inline BOOL CDC::RoundRect(int x1, int y1, int x2, int y2, int nWidth, int nHeight) const
+ // Draws a rectangle with rounded corners.
+ {
+ assert(m_pData->hDC);
+ return ::RoundRect(m_pData->hDC, x1, y1, x2, y2, nWidth, nHeight);
+ }
+ inline BOOL CDC::RoundRect(const RECT& rc, int nWidth, int nHeight) const
+ // Draws a rectangle with rounded corners.
+ {
+ assert(m_pData->hDC);
+ return ::RoundRect(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom, nWidth, nHeight );
+ }
+
+#ifndef _WIN32_WCE
+ inline BOOL CDC::Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const
+ // Draws a chord (a region bounded by the intersection of an ellipse and a line segment, called a secant).
+ {
+ assert(m_pData->hDC);
+ return ::Chord(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4);
+ }
+
+ inline BOOL CDC::Chord(const RECT& rc, POINT ptStart, POINT ptEnd) const
+ // Draws a chord (a region bounded by the intersection of an ellipse and a line segment, called a secant).
+ {
+ assert(m_pData->hDC);
+ return ::Chord(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom,
+ ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
+ }
+
+ inline BOOL CDC::Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const
+ // Draws a pie-shaped wedge bounded by the intersection of an ellipse and two radials.
+ {
+ assert(m_pData->hDC);
+ return ::Pie(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4);
+ }
+
+ inline BOOL CDC::Pie(const RECT& rc, POINT ptStart, POINT ptEnd) const
+ // Draws a pie-shaped wedge bounded by the intersection of an ellipse and two radials.
+ {
+ assert(m_pData->hDC);
+ return ::Pie(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom,
+ ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
+ }
+
+ inline BOOL CDC::PolyPolygon(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount) const
+ // Draws a series of closed polygons.
+ {
+ assert(m_pData->hDC);
+ return ::PolyPolygon(m_pData->hDC, lpPoints, lpPolyCounts, nCount);
+ }
+#endif
+
+ // Fill and 3D Drawing functions
+ inline BOOL CDC::FillRect(const RECT& rc, CBrush* pBrush) const
+ // Fills a rectangle by using the specified brush.
+ {
+ assert(m_pData->hDC);
+ assert(pBrush);
+ return (BOOL)::FillRect(m_pData->hDC, &rc, *pBrush);
+ }
+
+ inline BOOL CDC::InvertRect(const RECT& rc) const
+ // Inverts a rectangle in a window by performing a logical NOT operation on the color values for each pixel in the rectangle's interior.
+ {
+ assert(m_pData->hDC);
+ return ::InvertRect( m_pData->hDC, &rc);
+ }
+
+ inline BOOL CDC::DrawIconEx(int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, CBrush* pFlickerFreeDraw, UINT diFlags) const
+ // draws an icon or cursor, performing the specified raster operations, and stretching or compressing the icon or cursor as specified.
+ {
+ assert(m_pData->hDC);
+ HBRUSH hFlickerFreeDraw = pFlickerFreeDraw? (HBRUSH)pFlickerFreeDraw->GetHandle() : NULL;
+ return ::DrawIconEx(m_pData->hDC, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hFlickerFreeDraw, diFlags);
+ }
+
+ inline BOOL CDC::DrawEdge(const RECT& rc, UINT nEdge, UINT nFlags) const
+ // Draws one or more edges of rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::DrawEdge(m_pData->hDC, (LPRECT)&rc, nEdge, nFlags);
+ }
+
+ inline BOOL CDC::DrawFrameControl(const RECT& rc, UINT nType, UINT nState) const
+ // Draws a frame control of the specified type and style.
+ {
+ assert(m_pData->hDC);
+ return ::DrawFrameControl(m_pData->hDC, (LPRECT)&rc, nType, nState);
+ }
+
+ inline BOOL CDC::FillRgn(CRgn* pRgn, CBrush* pBrush) const
+ // Fills a region by using the specified brush.
+ {
+ assert(m_pData->hDC);
+ assert(pRgn);
+ assert(pBrush);
+ return ::FillRgn(m_pData->hDC, *pRgn, *pBrush);
+ }
+
+#ifndef _WIN32_WCE
+ inline BOOL CDC::DrawIcon(int x, int y, HICON hIcon) const
+ // Draws an icon or cursor.
+ {
+ assert(m_pData->hDC);
+ return ::DrawIcon(m_pData->hDC, x, y, hIcon);
+ }
+
+ inline BOOL CDC::DrawIcon(POINT pt, HICON hIcon) const
+ // Draws an icon or cursor.
+ {
+ assert(m_pData->hDC);
+ return ::DrawIcon(m_pData->hDC, pt.x, pt.y, hIcon);
+ }
+
+ inline BOOL CDC::FrameRect(const RECT& rc, CBrush* pBrush) const
+ // Draws a border around the specified rectangle by using the specified brush.
+ {
+ assert(m_pData->hDC);
+ assert(pBrush);
+ return (BOOL)::FrameRect(m_pData->hDC, &rc, *pBrush);
+ }
+
+ inline BOOL CDC::PaintRgn(CRgn* pRgn) const
+ // Paints the specified region by using the brush currently selected into the device context.
+ {
+ assert(m_pData->hDC);
+ assert(pRgn);
+ return ::PaintRgn(m_pData->hDC, *pRgn);
+ }
+#endif
+
+ // Bitmap Functions
+ inline int CDC::StretchDIBits(int XDest, int YDest, int nDestWidth, int nDestHeight, int XSrc, int YSrc, int nSrcWidth,
+ int nSrcHeight, CONST VOID *lpBits, BITMAPINFO& bi, UINT iUsage, DWORD dwRop) const
+ // Copies the color data for a rectangle of pixels in a DIB to the specified destination rectangle.
+ {
+ assert(m_pData->hDC);
+ return ::StretchDIBits(m_pData->hDC, XDest, YDest, nDestWidth, nDestHeight, XSrc, YSrc, nSrcWidth, nSrcHeight, lpBits, &bi, iUsage, dwRop);
+ }
+
+ inline BOOL CDC::PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) const
+ // Paints the specified rectangle using the brush that is currently selected into the device context.
+ {
+ assert(m_pData->hDC);
+ return ::PatBlt(m_pData->hDC, x, y, nWidth, nHeight, dwRop);
+ }
+
+ inline BOOL CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop) const
+ // Performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.
+ {
+ assert(m_pData->hDC);
+ assert(pSrcDC);
+ return ::BitBlt(m_pData->hDC, x, y, nWidth, nHeight, pSrcDC->GetHDC(), xSrc, ySrc, dwRop);
+ }
+
+ inline BOOL CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop) const
+ // Copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary.
+ {
+ assert(m_pData->hDC);
+ assert(pSrcDC);
+ return ::StretchBlt(m_pData->hDC, x, y, nWidth, nHeight, pSrcDC->GetHDC(), xSrc, ySrc, nSrcWidth, nSrcHeight, dwRop);
+ }
+
+#ifndef _WIN32_WCE
+ inline int CDC::GetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbi, UINT uUsage) const
+ // Retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format.
+ {
+ assert(m_pData->hDC);
+ assert(pBitmap);
+ return ::GetDIBits(m_pData->hDC, *pBitmap, uStartScan, cScanLines, lpvBits, lpbi, uUsage);
+ }
+
+ inline int CDC::SetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, CONST VOID *lpvBits, LPBITMAPINFO lpbi, UINT fuColorUse) const
+ // Sets the pixels in a compatible bitmap (DDB) using the color data found in the specified DIB.
+ {
+ assert(m_pData->hDC);
+ return ::SetDIBits(m_pData->hDC, *pBitmap, uStartScan, cScanLines, lpvBits, lpbi, fuColorUse);
+ }
+
+ inline int CDC::GetStretchBltMode() const
+ // Retrieves the current stretching mode.
+ // Possible modes: BLACKONWHITE, COLORONCOLOR, HALFTONE, STRETCH_ANDSCANS, STRETCH_DELETESCANS, STRETCH_HALFTONE, STRETCH_ORSCANS, WHITEONBLACK
+ {
+ assert(m_pData->hDC);
+ return ::GetStretchBltMode(m_pData->hDC);
+ }
+
+ inline int CDC::SetStretchBltMode(int iStretchMode) const
+ // Sets the stretching mode.
+ // Possible modes: BLACKONWHITE, COLORONCOLOR, HALFTONE, STRETCH_ANDSCANS, STRETCH_DELETESCANS, STRETCH_HALFTONE, STRETCH_ORSCANS, WHITEONBLACK
+ {
+ assert(m_pData->hDC);
+ return ::SetStretchBltMode(m_pData->hDC, iStretchMode);
+ }
+
+ inline BOOL CDC::FloodFill(int x, int y, COLORREF crColor) const
+ // Fills an area of the display surface with the current brush.
+ {
+ assert(m_pData->hDC);
+ return ::FloodFill(m_pData->hDC, x, y, crColor);
+ }
+
+ inline BOOL CDC::ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) const
+ // Fills an area of the display surface with the current brush.
+ // Fill type: FLOODFILLBORDER or FLOODFILLSURFACE
+ {
+ assert(m_pData->hDC);
+ return ::ExtFloodFill(m_pData->hDC, x, y, crColor, nFillType );
+ }
+
+ // co-ordinate functions
+ inline BOOL CDC::DPtoLP(LPPOINT lpPoints, int nCount) const
+ // Converts device coordinates into logical coordinates.
+ {
+ assert(m_pData->hDC);
+ return ::DPtoLP(m_pData->hDC, lpPoints, nCount);
+ }
+
+ inline BOOL CDC::DPtoLP(RECT& rc) const
+ // Converts device coordinates into logical coordinates.
+ {
+ assert(m_pData->hDC);
+ return ::DPtoLP(m_pData->hDC, (LPPOINT)&rc, 2);
+ }
+
+ inline BOOL CDC::LPtoDP(LPPOINT lpPoints, int nCount) const
+ // Converts logical coordinates into device coordinates.
+ {
+ assert(m_pData->hDC);
+ return ::LPtoDP(m_pData->hDC, lpPoints, nCount);
+ }
+
+ inline BOOL CDC::LPtoDP(RECT& rc) const
+ // Converts logical coordinates into device coordinates.
+ {
+ assert(m_pData->hDC);
+ return ::LPtoDP(m_pData->hDC, (LPPOINT)&rc, 2);
+ }
+
+#endif
+
+ // Layout Functions
+ inline DWORD CDC::GetLayout() const
+ // Returns the layout of a device context (LAYOUT_RTL and LAYOUT_BITMAPORIENTATIONPRESERVED).
+ {
+#if defined(WINVER) && defined(GetLayout) && (WINVER >= 0x0500)
+ return ::GetLayout(m_pData->hDC);
+#else
+ return 0;
+#endif
+ }
+
+ inline DWORD CDC::SetLayout(DWORD dwLayout) const
+ // changes the layout of a device context (DC).
+ // dwLayout values: LAYOUT_RTL or LAYOUT_BITMAPORIENTATIONPRESERVED
+ {
+#if defined(WINVER) && defined (SetLayout) && (WINVER >= 0x0500)
+ // Sets the layout of a device context
+ return ::SetLayout(m_pData->hDC, dwLayout);
+#else
+ UNREFERENCED_PARAMETER(dwLayout); // no-op
+ return 0;
+#endif
+ }
+
+ // Mapping Functions
+#ifndef _WIN32_WCE
+ inline int CDC::GetMapMode() const
+ // Rretrieves the current mapping mode.
+ // Possible modes: MM_ANISOTROPIC, MM_HIENGLISH, MM_HIMETRIC, MM_ISOTROPIC, MM_LOENGLISH, MM_LOMETRIC, MM_TEXT, and MM_TWIPS.
+ {
+ assert(m_pData->hDC);
+ return ::GetMapMode(m_pData->hDC);
+ }
+
+ inline BOOL CDC::GetViewportOrgEx(LPPOINT lpPoint) const
+ // Retrieves the x-coordinates and y-coordinates of the viewport origin for the device context.
+ {
+ assert(m_pData->hDC);
+ return ::GetViewportOrgEx(m_pData->hDC, lpPoint);
+ }
+
+ inline int CDC::SetMapMode(int nMapMode) const
+ // Sets the mapping mode of the specified device context.
+ {
+ assert(m_pData->hDC);
+ return ::SetMapMode(m_pData->hDC, nMapMode);
+ }
+
+ inline BOOL CDC::SetViewportOrgEx(int x, int y, LPPOINT lpPoint /* = NULL */) const
+ // Specifies which device point maps to the window origin (0,0).
+ {
+ assert(m_pData->hDC);
+ return ::SetViewportOrgEx(m_pData->hDC, x, y, lpPoint);
+ }
+
+ inline BOOL CDC::SetViewportOrgEx(POINT point, LPPOINT lpPointRet /* = NULL */) const
+ // Specifies which device point maps to the window origin (0,0).
+ {
+ assert(m_pData->hDC);
+ return SetViewportOrgEx(point.x, point.y, lpPointRet);
+ }
+
+ inline BOOL CDC::OffsetViewportOrgEx(int nWidth, int nHeight, LPPOINT lpPoint /* = NULL */) const
+ // Modifies the viewport origin for the device context using the specified horizontal and vertical offsets.
+ {
+ assert(m_pData->hDC);
+ return ::OffsetViewportOrgEx(m_pData->hDC, nWidth, nHeight, lpPoint);
+ }
+
+ inline BOOL CDC::GetViewportExtEx(LPSIZE lpSize) const
+ // Retrieves the x-extent and y-extent of the current viewport for the device context.
+ {
+ assert(m_pData->hDC);
+ return ::GetViewportExtEx(m_pData->hDC, lpSize);
+ }
+
+ inline BOOL CDC::SetViewportExtEx(int x, int y, LPSIZE lpSize ) const
+ // Sets the horizontal and vertical extents of the viewport for the device context by using the specified values.
+ {
+ assert(m_pData->hDC);
+ return ::SetViewportExtEx(m_pData->hDC, x, y, lpSize);
+ }
+
+ inline BOOL CDC::SetViewportExtEx(SIZE size, LPSIZE lpSizeRet ) const
+ // Sets the horizontal and vertical extents of the viewport for the device context by using the specified values.
+ {
+ assert(m_pData->hDC);
+ return SetViewportExtEx(size.cx, size.cy, lpSizeRet);
+ }
+
+ inline BOOL CDC::ScaleViewportExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize ) const
+ // Modifies the viewport for the device context using the ratios formed by the specified multiplicands and divisors.
+ {
+ assert(m_pData->hDC);
+ return ::ScaleViewportExtEx(m_pData->hDC, xNum, xDenom, yNum, yDenom, lpSize);
+ }
+
+ inline BOOL CDC::GetWindowOrgEx(LPPOINT lpPoint) const
+ // Retrieves the x-coordinates and y-coordinates of the window origin for the device context.
+ {
+ assert(m_pData->hDC);
+ return ::GetWindowOrgEx(m_pData->hDC, lpPoint);
+ }
+
+ inline BOOL CDC::SetWindowOrgEx(int x, int y, LPPOINT lpPoint ) const
+ // Specifies which window point maps to the viewport origin (0,0).
+ {
+ assert(m_pData->hDC);
+ return ::SetWindowOrgEx(m_pData->hDC, x, y, lpPoint);
+ }
+
+ inline BOOL CDC::SetWindowOrgEx(POINT point, LPPOINT lpPointRet ) const
+ // Specifies which window point maps to the viewport origin (0,0).
+ {
+ assert(m_pData->hDC);
+ return SetWindowOrgEx(point.x, point.y, lpPointRet);
+ }
+
+ inline BOOL CDC::OffsetWindowOrgEx(int nWidth, int nHeight, LPPOINT lpPoint ) const
+ // Modifies the window origin for the device context using the specified horizontal and vertical offsets.
+ {
+ assert(m_pData->hDC);
+ return ::OffsetWindowOrgEx(m_pData->hDC, nWidth, nHeight, lpPoint);
+ }
+
+ inline BOOL CDC::GetWindowExtEx(LPSIZE lpSize) const
+ // Retrieves the x-extent and y-extent of the window for the device context.
+ {
+ assert(m_pData->hDC);
+ return ::GetWindowExtEx(m_pData->hDC, lpSize);
+ }
+
+ inline BOOL CDC::SetWindowExtEx(int x, int y, LPSIZE lpSize ) const
+ // Sets the horizontal and vertical extents of the window for the device context by using the specified values.
+ {
+ assert(m_pData->hDC);
+ return ::SetWindowExtEx(m_pData->hDC, x, y, lpSize);
+ }
+
+ inline BOOL CDC::SetWindowExtEx(SIZE size, LPSIZE lpSizeRet) const
+ // Sets the horizontal and vertical extents of the window for the device context by using the specified values.
+ {
+ assert(m_pData->hDC);
+ return SetWindowExtEx(size.cx, size.cy, lpSizeRet);
+ }
+
+ inline BOOL CDC::ScaleWindowExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize) const
+ // Modifies the window for the device context using the ratios formed by the specified multiplicands and divisors.
+ {
+ assert(m_pData->hDC);
+ return ::ScaleWindowExtEx(m_pData->hDC, xNum, xDenom, yNum, yDenom, lpSize);
+ }
+#endif
+
+ // Printer Functions
+ inline int CDC::StartDoc(LPDOCINFO lpDocInfo) const
+ // Starts a print job.
+ {
+ assert(m_pData->hDC);
+ return ::StartDoc(m_pData->hDC, lpDocInfo);
+ }
+
+ inline int CDC::EndDoc() const
+ // Ends a print job.
+ {
+ assert(m_pData->hDC);
+ return ::EndDoc(m_pData->hDC);
+ }
+
+ inline int CDC::StartPage() const
+ // Prepares the printer driver to accept data.
+ {
+ assert(m_pData->hDC);
+ return ::StartPage(m_pData->hDC);
+ }
+
+ inline int CDC::EndPage() const
+ // Notifies the device that the application has finished writing to a page.
+ {
+ assert(m_pData->hDC);
+ return ::EndPage(m_pData->hDC);
+ }
+
+ inline int CDC::AbortDoc() const
+ // Stops the current print job and erases everything drawn since the last call to the StartDoc function.
+ {
+ assert(m_pData->hDC);
+ return ::AbortDoc(m_pData->hDC);
+ }
+
+ inline int CDC::SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int)) const
+ // Sets the application-defined abort function that allows a print job to be canceled during spooling.
+ {
+ assert(m_pData->hDC);
+ return ::SetAbortProc(m_pData->hDC, lpfn);
+ }
+
+ // Text Functions
+ inline BOOL CDC::ExtTextOut(int x, int y, UINT nOptions, LPCRECT lprc, LPCTSTR lpszString, int nCount /*= -1*/, LPINT lpDxWidths /*=NULL*/) const
+ // Draws text using the currently selected font, background color, and text color
+ {
+ assert(m_pData->hDC);
+
+ if (nCount == -1)
+ nCount = lstrlen (lpszString);
+
+ return ::ExtTextOut(m_pData->hDC, x, y, nOptions, lprc, lpszString, nCount, lpDxWidths );
+ }
+
+ inline int CDC::DrawText(LPCTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat) const
+ // Draws formatted text in the specified rectangle
+ {
+ assert(m_pData->hDC);
+ return ::DrawText(m_pData->hDC, lpszString, nCount, lprc, nFormat );
+ }
+
+ inline UINT CDC::GetTextAlign() const
+ // Retrieves the text-alignment setting
+ // Values: TA_BASELINE, TA_BOTTOM, TA_TOP, TA_CENTER, TA_LEFT, TA_RIGHT, TA_RTLREADING, TA_NOUPDATECP, TA_UPDATECP
+ {
+ assert(m_pData->hDC);
+ return ::GetTextAlign(m_pData->hDC);
+ }
+
+ inline UINT CDC::SetTextAlign(UINT nFlags) const
+ // Sets the text-alignment setting
+ // Values: TA_BASELINE, TA_BOTTOM, TA_TOP, TA_CENTER, TA_LEFT, TA_RIGHT, TA_RTLREADING, TA_NOUPDATECP, TA_UPDATECP
+ {
+ assert(m_pData->hDC);
+ return ::SetTextAlign(m_pData->hDC, nFlags);
+ }
+
+ inline int CDC::GetTextFace(int nCount, LPTSTR lpszFacename) const
+ // Retrieves the typeface name of the font that is selected into the device context
+ {
+ assert(m_pData->hDC);
+ return ::GetTextFace(m_pData->hDC, nCount, lpszFacename);
+ }
+
+ inline BOOL CDC::GetTextMetrics(TEXTMETRIC& Metrics) const
+ // Fills the specified buffer with the metrics for the currently selected font
+ {
+ assert(m_pData->hDC);
+ return ::GetTextMetrics(m_pData->hDC, &Metrics);
+ }
+
+ inline COLORREF CDC::GetBkColor() const
+ // Returns the current background color
+ {
+ assert(m_pData->hDC);
+ return ::GetBkColor(m_pData->hDC);
+ }
+
+ inline COLORREF CDC::SetBkColor(COLORREF crColor) const
+ // Sets the current background color to the specified color value
+ {
+ assert(m_pData->hDC);
+ return ::SetBkColor(m_pData->hDC, crColor);
+ }
+
+ inline COLORREF CDC::GetTextColor() const
+ // Retrieves the current text color
+ {
+ assert(m_pData->hDC);
+ return ::GetTextColor(m_pData->hDC);
+ }
+
+ inline COLORREF CDC::SetTextColor(COLORREF crColor) const
+ // Sets the current text color
+ {
+ assert(m_pData->hDC);
+ return ::SetTextColor(m_pData->hDC, crColor);
+ }
+
+ inline int CDC::GetBkMode() const
+ // returns the current background mix mode (OPAQUE or TRANSPARENT)
+ {
+ assert(m_pData->hDC);
+ return ::GetBkMode(m_pData->hDC);
+ }
+
+ inline int CDC::SetBkMode(int iBkMode) const
+ // Sets the current background mix mode (OPAQUE or TRANSPARENT)
+ {
+ assert(m_pData->hDC);
+ return ::SetBkMode(m_pData->hDC, iBkMode);
+ }
+
+#ifndef _WIN32_WCE
+ inline int CDC::DrawTextEx(LPTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat, LPDRAWTEXTPARAMS lpDTParams) const
+ // Draws formatted text in the specified rectangle with more formatting options
+ {
+ assert(m_pData->hDC);
+ return ::DrawTextEx(m_pData->hDC, lpszString, nCount, lprc, nFormat, lpDTParams);
+ }
+
+ inline CSize CDC::GetTextExtentPoint32(LPCTSTR lpszString, int nCount) const
+ // Computes the width and height of the specified string of text
+ {
+ assert(m_pData->hDC);
+ CSize sz;
+ ::GetTextExtentPoint32(m_pData->hDC, lpszString, nCount, &sz);
+ return sz;
+ }
+
+ inline CSize CDC::GetTabbedTextExtent(LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const
+ // Computes the width and height of a character string
+ {
+ assert(m_pData->hDC);
+ DWORD dwSize = ::GetTabbedTextExtent(m_pData->hDC, lpszString, nCount, nTabPositions, lpnTabStopPositions );
+ CSize sz(dwSize);
+ return sz;
+ }
+
+ inline BOOL CDC::GrayString(CBrush* pBrush, GRAYSTRINGPROC lpOutputFunc, LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight) const
+ // Draws gray text at the specified location
+ {
+ assert(m_pData->hDC);
+ assert(pBrush);
+ return ::GrayString(m_pData->hDC, *pBrush, lpOutputFunc, lpData, nCount, x, y, nWidth, nHeight);
+ }
+
+ inline int CDC::SetTextJustification(int nBreakExtra, int nBreakCount) const
+ // Specifies the amount of space the system should add to the break characters in a string of text
+ {
+ assert(m_pData->hDC);
+ return ::SetTextJustification(m_pData->hDC, nBreakExtra, nBreakCount);
+ }
+
+ inline int CDC::GetTextCharacterExtra() const
+ // Retrieves the current intercharacter spacing for the device context
+ {
+ assert(m_pData->hDC);
+ return ::GetTextCharacterExtra(m_pData->hDC);
+ }
+
+ inline int CDC::SetTextCharacterExtra(int nCharExtra) const
+ // Sets the intercharacter spacing
+ {
+ assert(m_pData->hDC);
+ return ::SetTextCharacterExtra(m_pData->hDC, nCharExtra);
+ }
+
+ inline CSize CDC::TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin) const
+ // Writes a character string at a specified location, expanding tabs to the values specified in an array of tab-stop positions
+ {
+ assert(m_pData->hDC);
+ DWORD dwSize = ::TabbedTextOut(m_pData->hDC, x, y, lpszString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin );
+ CSize sz(dwSize);
+ return sz;
+ }
+
+ inline BOOL CDC::TextOut(int x, int y, LPCTSTR lpszString, int nCount/* = -1*/) const
+ // Writes a character string at the specified location
+ {
+ assert(m_pData->hDC);
+ if (nCount == -1)
+ nCount = lstrlen (lpszString);
+
+ return ::TextOut(m_pData->hDC, x, y, lpszString, nCount);
+ }
+
+#endif
+
+
+
+ /////////////////////////////////////////////////////////////////
+ // Definitions for some global functions in the Win32xx namespace
+ //
+
+#ifndef _WIN32_WCE
+ inline void TintBitmap (CBitmap* pbmSource, int cRed, int cGreen, int cBlue)
+ // Modifies the colour of the supplied Device Dependant Bitmap, by the colour
+ // correction values specified. The correction values can range from -255 to +255.
+ // This function gains its speed by accessing the bitmap colour information
+ // directly, rather than using GetPixel/SetPixel.
+ {
+ // Create our LPBITMAPINFO object
+ CBitmapInfoPtr pbmi(pbmSource);
+ pbmi->bmiHeader.biBitCount = 24;
+
+ // Create the reference DC for GetDIBits to use
+ CMemDC MemDC(NULL);
+
+ // Use GetDIBits to create a DIB from our DDB, and extract the colour data
+ MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS);
+ std::vector<byte> vBits(pbmi->bmiHeader.biSizeImage, 0);
+ byte* pByteArray = &vBits[0];
+
+ MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS);
+ UINT nWidthBytes = pbmi->bmiHeader.biSizeImage/pbmi->bmiHeader.biHeight;
+
+ // Ensure sane colour correction values
+ cBlue = MIN(cBlue, 255);
+ cBlue = MAX(cBlue, -255);
+ cRed = MIN(cRed, 255);
+ cRed = MAX(cRed, -255);
+ cGreen = MIN(cGreen, 255);
+ cGreen = MAX(cGreen, -255);
+
+ // Pre-calculate the RGB modification values
+ int b1 = 256 - cBlue;
+ int g1 = 256 - cGreen;
+ int r1 = 256 - cRed;
+
+ int b2 = 256 + cBlue;
+ int g2 = 256 + cGreen;
+ int r2 = 256 + cRed;
+
+ // Modify the colour
+ int yOffset = 0;
+ int xOffset;
+ int Index;
+ for (int Row=0; Row < pbmi->bmiHeader.biHeight; Row++)
+ {
+ xOffset = 0;
+
+ for (int Column=0; Column < pbmi->bmiHeader.biWidth; Column++)
+ {
+ // Calculate Index
+ Index = yOffset + xOffset;
+
+ // Adjust the colour values
+ if (cBlue > 0)
+ pByteArray[Index] = (BYTE)(cBlue + (((pByteArray[Index] *b1)) >>8));
+ else if (cBlue < 0)
+ pByteArray[Index] = (BYTE)((pByteArray[Index] *b2) >>8);
+
+ if (cGreen > 0)
+ pByteArray[Index+1] = (BYTE)(cGreen + (((pByteArray[Index+1] *g1)) >>8));
+ else if (cGreen < 0)
+ pByteArray[Index+1] = (BYTE)((pByteArray[Index+1] *g2) >>8);
+
+ if (cRed > 0)
+ pByteArray[Index+2] = (BYTE)(cRed + (((pByteArray[Index+2] *r1)) >>8));
+ else if (cRed < 0)
+ pByteArray[Index+2] = (BYTE)((pByteArray[Index+2] *r2) >>8);
+
+ // Increment the horizontal offset
+ xOffset += pbmi->bmiHeader.biBitCount >> 3;
+ }
+
+ // Increment vertical offset
+ yOffset += nWidthBytes;
+ }
+
+ // Save the modified colour back into our source DDB
+ MemDC.SetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS);
+ }
+
+ inline void GrayScaleBitmap(CBitmap* pbmSource)
+ {
+ // Create our LPBITMAPINFO object
+ CBitmapInfoPtr pbmi(pbmSource);
+
+ // Create the reference DC for GetDIBits to use
+ CMemDC MemDC(NULL);
+
+ // Use GetDIBits to create a DIB from our DDB, and extract the colour data
+ MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS);
+ std::vector<byte> vBits(pbmi->bmiHeader.biSizeImage, 0);
+ byte* pByteArray = &vBits[0];
+
+ MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS);
+ UINT nWidthBytes = pbmi->bmiHeader.biSizeImage/pbmi->bmiHeader.biHeight;
+
+ int yOffset = 0;
+ int xOffset;
+ int Index;
+
+ for (int Row=0; Row < pbmi->bmiHeader.biHeight; Row++)
+ {
+ xOffset = 0;
+
+ for (int Column=0; Column < pbmi->bmiHeader.biWidth; Column++)
+ {
+ // Calculate Index
+ Index = yOffset + xOffset;
+
+ BYTE byGray = (BYTE) ((pByteArray[Index] + pByteArray[Index+1]*6 + pByteArray[Index+2] *3)/10);
+ pByteArray[Index] = byGray;
+ pByteArray[Index+1] = byGray;
+ pByteArray[Index+2] = byGray;
+
+ // Increment the horizontal offset
+ xOffset += pbmi->bmiHeader.biBitCount >> 3;
+ }
+
+ // Increment vertical offset
+ yOffset += nWidthBytes;
+ }
+
+ // Save the modified colour back into our source DDB
+ MemDC.SetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS);
+ }
+
+ inline HIMAGELIST CreateDisabledImageList(HIMAGELIST himlNormal)
+ // Returns a greyed image list, created from hImageList
+ {
+ int cx, cy;
+ int nCount = ImageList_GetImageCount(himlNormal);
+ if (0 == nCount)
+ return NULL;
+
+ ImageList_GetIconSize(himlNormal, &cx, &cy);
+
+ // Create the disabled ImageList
+ HIMAGELIST himlDisabled = ImageList_Create(cx, cy, ILC_COLOR24 | ILC_MASK, nCount, 0);
+
+ // Process each image in the ImageList
+ for (int i = 0 ; i < nCount; ++i)
+ {
+ CClientDC DesktopDC(NULL);
+ CMemDC MemDC(NULL);
+ CBitmap* pOldBitmap = MemDC.CreateCompatibleBitmap(&DesktopDC, cx, cx);
+ CRect rc;
+ rc.SetRect(0, 0, cx, cx);
+
+ // Set the mask color to grey for the new ImageList
+ COLORREF crMask = RGB(200, 199, 200);
+ if ( GetDeviceCaps(DesktopDC, BITSPIXEL) < 24)
+ {
+ HPALETTE hPal = (HPALETTE)GetCurrentObject(DesktopDC, OBJ_PAL);
+ UINT Index = GetNearestPaletteIndex(hPal, crMask);
+ if (Index != CLR_INVALID) crMask = PALETTEINDEX(Index);
+ }
+
+ MemDC.SolidFill(crMask, rc);
+
+ // Draw the image on the memory DC
+ ImageList_SetBkColor(himlNormal, crMask);
+ ImageList_Draw(himlNormal, i, MemDC, 0, 0, ILD_NORMAL);
+
+ // Convert colored pixels to gray
+ for (int x = 0 ; x < cx; ++x)
+ {
+ for (int y = 0; y < cy; ++y)
+ {
+ COLORREF clr = ::GetPixel(MemDC, x, y);
+
+ if (clr != crMask)
+ {
+ BYTE byGray = (BYTE) (95 + (GetRValue(clr) *3 + GetGValue(clr)*6 + GetBValue(clr))/20);
+ MemDC.SetPixel(x, y, RGB(byGray, byGray, byGray));
+ }
+
+ }
+ }
+
+ // Detach the bitmap so we can use it.
+ CBitmap* pBitmap = MemDC.SelectObject(pOldBitmap);
+ ImageList_AddMasked(himlDisabled, *pBitmap, crMask);
+ }
+
+ return himlDisabled;
+ }
+#endif
+
+ ////////////////////////////////////////////
+ // Global Function Definitions
+ //
+
+ inline CDC* FromHandle(HDC hDC)
+ // Returns the CDC object associated with the device context handle
+ // If a CDC object doesn't already exist, a temporary CDC object is created.
+ // The HDC belonging to a temporary CDC is not released or destroyed when the
+ // temporary CDC is deconstructed.
+ {
+ assert( GetApp() );
+ CDC* pDC = GetApp()->GetCDCFromMap(hDC);
+ if (hDC != 0 && pDC == 0)
+ {
+ pDC = new CDC;
+ GetApp()->AddTmpDC(pDC);
+ pDC->m_pData->hDC = hDC;
+ pDC->m_pData->bRemoveHDC = FALSE;
+ }
+ return pDC;
+ }
+
+ inline CBitmap* FromHandle(HBITMAP hBitmap)
+ // Returns the CBitmap associated with the Bitmap handle
+ // If a CBitmap object doesn't already exist, a temporary CBitmap object is created.
+ // The HBITMAP belonging to a temporary CBitmap is not released or destroyed
+ // when the temporary CBitmap is deconstructed.
+ {
+ assert( GetApp() );
+ CBitmap* pBitmap = (CBitmap*)GetApp()->GetCGDIObjectFromMap(hBitmap);
+ if (hBitmap != 0 && pBitmap == 0)
+ {
+ pBitmap = new CBitmap;
+ GetApp()->AddTmpGDI(pBitmap);
+ pBitmap->m_pData->hGDIObject = hBitmap;
+ pBitmap->m_pData->bRemoveObject = FALSE;
+ }
+ return pBitmap;
+ }
+
+ inline CBrush* FromHandle(HBRUSH hBrush)
+ // Returns the CBrush associated with the Brush handle
+ // If a CBrush object doesn't already exist, a temporary CBrush object is created.
+ // The HBRUSH belonging to a temporary CBrush is not released or destroyed
+ // when the temporary CBrush is deconstructed.
+ {
+ assert( GetApp() );
+ CBrush* pBrush = (CBrush*)GetApp()->GetCGDIObjectFromMap(hBrush);
+ if (hBrush != 0 && pBrush == 0)
+ {
+ pBrush = new CBrush;
+ GetApp()->AddTmpGDI(pBrush);
+ pBrush->m_pData->hGDIObject = hBrush;
+ pBrush->m_pData->bRemoveObject = FALSE;
+ }
+ return pBrush;
+ }
+
+ inline CFont* FromHandle(HFONT hFont)
+ // Returns the CFont associated with the Font handle
+ // If a CFont object doesn't already exist, a temporary CFont object is created.
+ // The HFONT belonging to a temporary CFont is not released or destroyed
+ // when the temporary CFont is deconstructed.
+ {
+ assert( GetApp() );
+ CFont* pFont = (CFont*)GetApp()->GetCGDIObjectFromMap(hFont);
+ if (hFont != 0 && pFont == 0)
+ {
+ pFont = new CFont;
+ GetApp()->AddTmpGDI(pFont);
+ pFont->m_pData->hGDIObject = hFont;
+ pFont->m_pData->bRemoveObject = FALSE;
+ }
+ return pFont;
+ }
+
+ inline CPalette* FromHandle(HPALETTE hPalette)
+ // Returns the CPalette associated with the palette handle
+ // If a CPalette object doesn't already exist, a temporary CPalette object is created.
+ // The HPALETTE belonging to a temporary CPalette is not released or destroyed
+ // when the temporary CPalette is deconstructed.
+ {
+ assert( GetApp() );
+ CPalette* pPalette = (CPalette*)GetApp()->GetCGDIObjectFromMap(hPalette);
+ if (hPalette != 0 && pPalette == 0)
+ {
+ pPalette = new CPalette;
+ GetApp()->AddTmpGDI(pPalette);
+ pPalette->m_pData->hGDIObject = hPalette;
+ pPalette->m_pData->bRemoveObject = FALSE;
+ }
+ return pPalette;
+ }
+
+ inline CPen* FromHandle(HPEN hPen)
+ // Returns the CPen associated with the HPEN.
+ // If a CPen object doesn't already exist, a temporary CPen object is created.
+ // The HPEN belonging to a temporary CPen is not released or destroyed
+ // when the temporary CPen is deconstructed.
+ {
+ assert( GetApp() );
+ CPen* pPen = (CPen*)GetApp()->GetCGDIObjectFromMap(hPen);
+ if (hPen != 0 && pPen == 0)
+ {
+ pPen = new CPen;
+ GetApp()->AddTmpGDI(pPen);
+ pPen->m_pData->hGDIObject = hPen;
+ pPen->m_pData->bRemoveObject = FALSE;
+ }
+ return pPen;
+ }
+
+ inline CRgn* FromHandle(HRGN hRgn)
+ // Returns the CRgn associated with the HRGN.
+ // If a CRgn object doesn't already exist, a temporary CRgn object is created.
+ // The HRGN belonging to a temporary CRgn is not released or destroyed
+ // when the temporary CRgn is deconstructed.
+ {
+ assert( GetApp() );
+ CRgn* pRgn = (CRgn*)GetApp()->GetCGDIObjectFromMap(hRgn);
+ if (hRgn != 0 && pRgn == 0)
+ {
+ pRgn = new CRgn;
+ GetApp()->AddTmpGDI(pRgn);
+ pRgn->m_pData->hGDIObject = hRgn;
+ pRgn->m_pData->bRemoveObject = FALSE;
+ }
+ return pRgn;
+ }
+
+
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_GDI_H_
+
diff --git a/mmc_updater/depends/win32cpp/info.txt b/mmc_updater/depends/win32cpp/info.txt
new file mode 100644
index 00000000..a4dbda8e
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/info.txt
@@ -0,0 +1,205 @@
+Generic Information about Win32++ Projects
+==========================================
+The various directories may contain the following types of files:
+
+Extension | Description
+----------+------------
+cbp | A project file used by CodeBlocks
+dsp | A project file used by Visual Studio 6
+dsw | A project file used by Visual Studio 6
+sln | A project file used by Visual Studio 2003, VS2005 or VS2008
+vcproj | A project file used by Visual Studio 2003, VS2005 or VS2008
+vcxproj | A project file used by Visual Studio 2010
+filters | A supplementary project file used by Visual Studio 2010
+bdsproj | A project file used by Borland Developer Studio 2006
+bpf | A project file used by Borland Developer Studio 2006
+vcp | A project file used by eMbedded Visual C++
+vcw | A project file used by eMbedded Visual C++
+dev | A project file used by Dev-C++
+cpp | A C++ source file
+h | A C++ header file
+rc | A C++ resouce script file
+jpg | A jpeg resource file
+ico | An icon resource file
+bmp | A bitmap resource file
+cur | A cursor resource file
+manifest | A manifest resource file
+txt | A text file
+xml | An Extensible Markup Language file (defines the ribbon UI)
+
+Supported Compilers and Integrated Development Environments (IDEs)
+==================================================================
+Win32++ supports the following:
+* Borland Compiler Version 5.5
+* Borland Developer Studio 2006
+* Borland Turbo C++ 2006
+* CodeBlocks
+* Dev-C++
+* MinGW GCC Compiler
+* Visual Studio 6
+* Visual Studio.net 2003
+* Visual C++ Toolkit 2003
+* Visual Studio.net 2005
+* Visual Studio.net 2005 Express
+* Visual Studio.net 2008
+* Visual Studio.net 2008 Express
+* Visual Studio.net 2010
+
+CodeBlocks is an IDE. The project files are configured for the following
+compilers:
+* Borland Compiler Version 5.5
+* MinGW GNU compiler
+* Visual C++ Toolkit 2003
+
+Dev-C++ is an IDE which supports the MinGW GNU compiler
+
+Supported Operating Systems
+===========================
+The programs compiled with Win32++ can run on the following operating systems:
+* Win95 (all versions, with or without Internet Explorer 4 installed)
+* Win98 (both versions)
+* WinME
+* Windows NT 4
+* Windows 2000
+* Windows XP
+* Windows XP x64
+* Windows Vista
+* Windows Vista x64
+* Windows 7
+* Windows 7 x64
+* Windows Server 2003
+* Windows Server 2003 x64
+* Windows Server 2008
+* Windows Server 2008 x64
+* Windows CE
+
+Note: Programs compiled with Visual Studio.net 2008 and Visual Studio.net 2008
+Express will not run on Win32 operating systems earlier than Windows 2000.
+
+Win32++ automatically detects if the operating system is capable of using
+rebars. If rebars are not supported by the OS, Win32++ produces a frame without
+rebars.
+
+Win32++ is Unicode compliant and can therefore be used to develop Unicode
+applications. Users are advised that older operating systems (namely Win95,
+Win98 and WinME) don't support Unicode applications.
+
+Win32++ supports 64bit compilers, and can be used to develop 64bit code.
+
+Directory Structure
+===================
+When extracting the files from the zip archive, be sure to preserve the
+directory structure. The directory structure will typically look like this:
+
+.\include
+.\new projects
+.\output
+.\samples
+.\tools
+.\tutorials
+.\WCE samples
+
+The files which form the Win32++ library are contained in the include
+subdirectory.
+
+Components of Win32++
+=====================
+
+ Files | Classes | Operating Systems | Description
+==================+==================+===================+=====================
+controls.h | CAnimation | Win32, Win64 | Adds support for the
+ | CComboBox | and WinCE | following controls:
+ | CComboBoxEx | | Animation, ComboBox,
+ | CProgressBar | | ComboBoxEx, Progress
+ | CScrollBar | | bar, Scroll bar,
+ | CSlider | | Slider, Spin button.
+ | CSpinButton | |
+------------------+------------------+-------------------+---------------------
+dialog.h | CDialog | Win32, Win64 | Adds dialog support.
+ | CResizer | WinCE for CDialog |
+------------------+------------------+-------------------+---------------------
+docking.h | CDocker | Win32, Win64 | Adds support for
+ | CDockContainer | | docking windows and
+ | | | splitter windows.
+------------------+------------------+-------------------+---------------------
+frame.h | CMenubar | Win32, Win64 | Adds support for
+ | CFrame | | frames. Frames use a
+ | | | toolbar and menubar
+ | | | inside a rebar, and
+ | | | a statusbar.
+------------------+------------------+-------------------+---------------------
+gdi.h | CDC | Win32, Win64 | A helper class for
+ | CBitmap | and WinCE | GDI graphics.
+ | CBrush | |
+ | CFont | |
+ | CPalette | |
+ | CPen | |
+ | CRgn | |
+------------------+------------------+-------------------+---------------------
+listView.h | CListView | Win32, Win64 | Adds support for a
+ | | and WinCE | ListView control.
+------------------+------------------+-------------------+---------------------
+mdi.h | CMDIFrame | Win32, Win64 | Adds support for MDI
+ | CMDIChild | | frames.
+------------------+------------------+-------------------+---------------------
+propertysheet.h | CPropertySheet | Win32, Win64 | Adds property sheet
+ | CPropertyPage | and WinCE | support.
+------------------+------------------+-------------------+---------------------
+rebar.h | CRebar | Win32, Win64 | Adds support for a
+ | | and WinCE | Rebar control.
+------------------+------------------+-------------------+---------------------
+ribbon.h | CRibbon | Win32, Win64 | Adds support for the
+ | CRibbonFrame | | Windows 7 ribbon.
+------------------+------------------+-------------------+---------------------
+shared_ptr.h | Shared_Ptr | Win32, Win64, | Add a smart pointer
+ | | and WinCE | for use in vectors.
+------------------+------------------+-------------------+---------------------
+socket.h | CSocket | Win32, Win64 | Adds network
+ | | and WinCE | support.
+------------------+------------------+-------------------+---------------------
+splitter.h | CSplitter | Win32, Win64 | Adds splitter support
+ | | | (depreciated)
+------------------+------------------+-------------------+----------------------
+statusbar.h | CStatusbar | Win32, Win64 | Adds support for a
+ | | and WinCE | Status bar control.
+------------------+------------------+-------------------+---------------------
+stdcontrols.h | CButton | Win32, Win64 | Adds support for
+ | CEdit | and WinCE | Button, Edit,
+ | CListBox | | ListBox and Static
+ | CStatic | | controls.
+------------------+------------------+-------------------+---------------------
+tab.h | CTab | Win32, Win64 | Adds support for tab
+ | CMDITab | | controls, and MDI
+ | | | tab windows.
+------------------+------------------+-------------------+---------------------
+taskdialog.h | CTaskDialog | Win32, Win64 | Adds support for tab
+ | | | task dialogs.
+------------------+------------------+-------------------+---------------------
+thread.h | CThread | Win32, Win64 | Adds support for
+ | | and WinCE | threads.
+------------------+------------------+-------------------+---------------------
+toolbar.h | CToolbar | Win32, Win64 | Adds support for a
+ | | and WinCE | Toolbar control.
+------------------+------------------+-------------------+---------------------
+treeview.h | CTreeView | Win32, Win64 | Adds support for a
+ | | and WinCE | TreeView control.
+------------------+------------------+-------------------+---------------------
+wceframe.h | CWceFrame | WinCE only | Adds support for
+ | CCmdbar | | frames in WinCE.
+------------------+------------------+-------------------+---------------------
+webbrowser.h | CAXWindow | Win32, Win64 | Adds support for a
+ | CWebBrowser | and WinCE | ActiveX container and
+ | | | a WebBrowser window.
+------------------+------------------+-------------------+---------------------
+wincore.h | CCriticalSection | Win32, Win64, | The core set of
+ | CWinApp | and WinCE | classes required for
+ | CWinException | | all Win32++
+ | CWnd | | applications.
+------------------+------------------+-------------------+---------------------
+winutils.h | CPoint | Win32, Win64, | Additional utility
+ | CRect | and WinCE | classes.
+ | CSize | |
+------------------+------------------+-------------------+---------------------
+
+Refer to the help documentation that ships with Win32++ for more information on
+using Win32++. \ No newline at end of file
diff --git a/mmc_updater/depends/win32cpp/listview.h b/mmc_updater/depends/win32cpp/listview.h
new file mode 100644
index 00000000..810e7627
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/listview.h
@@ -0,0 +1,867 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+
+#ifndef _WIN32XX_LISTVIEW_H_
+#define _WIN32XX_LISTVIEW_H_
+
+#include "wincore.h"
+#include "commctrl.h"
+
+namespace Win32xx
+{
+
+ class CListView : public CWnd
+ {
+ public:
+ CListView() {}
+ virtual ~CListView() {}
+ virtual void PreRegisterClass(WNDCLASS &wc);
+
+ // Attributes
+ CSize ApproximateViewRect(CSize sz = CSize(-1, -1), int iCount = -1) const;
+ COLORREF GetBkColor( ) const;
+ BOOL GetBkImage( LVBKIMAGE& lvbkImage ) const;
+ UINT GetCallbackMask( ) const;
+ BOOL GetCheckState( UINT nItem ) const;
+ BOOL GetColumn( int iCol, LVCOLUMN& Column ) const;
+ BOOL GetColumnOrderArray( LPINT piArray, int iCount = -1 );
+ int GetColumnWidth( int iCol ) const;
+ int GetCountPerPage( ) const;
+ HWND GetEditControl( ) const;
+ DWORD GetExtendedStyle( ) const;
+ HWND GetHeader( ) const;
+ HCURSOR GetHotCursor( );
+ int GetHotItem( ) const;
+ DWORD GetHoverTime( ) const;
+ HIMAGELIST GetImageList( int nImageType ) const;
+ BOOL GetItem( LVITEM& lvItem ) const;
+ int GetItemCount( ) const;
+ DWORD_PTR GetItemData( int iItem ) const;
+ BOOL GetItemPosition( int iItem, CPoint& pt ) const;
+ BOOL GetItemRect( int iItem, CRect& rc, UINT nCode ) const;
+ UINT GetItemState( int iItem, UINT nMask ) const;
+ tString GetItemText( int iItem, int iSubItem, UINT nTextMax = 260 ) const;
+ int GetNextItem( int iItem, int iFlags ) const;
+ UINT GetNumberOfWorkAreas( ) const;
+ BOOL GetOrigin( CPoint& pt ) const;
+ UINT GetSelectedCount( ) const;
+ int GetSelectionMark( ) const;
+ int GetStringWidth( LPCTSTR pszString ) const;
+ BOOL GetSubItemRect( int iItem, int iSubItem, int iCode, CRect& rc ) const;
+ COLORREF GetTextBkColor( ) const;
+ COLORREF GetTextColor( ) const;
+ HWND GetToolTips( ) const;
+ int GetTopIndex( ) const;
+ BOOL GetViewRect( CRect& rc ) const;
+ void GetWorkAreas( int iWorkAreas, LPRECT pRectArray ) const;
+ BOOL SetBkColor( COLORREF clrBk ) const;
+ BOOL SetBkImage( LVBKIMAGE& plvbkImage ) const;
+ BOOL SetCallbackMask( UINT nMask ) const;
+ void SetCheckState( int iItem, BOOL fCheck = TRUE ) const;
+ BOOL SetColumn( int iCol, const LVCOLUMN& pColumn ) const;
+ BOOL SetColumnOrderArray( int iCount, LPINT piArray ) const;
+ BOOL SetColumnWidth( int iCol, int cx ) const;
+ DWORD SetExtendedStyle( DWORD dwNewStyle ) const;
+ HCURSOR SetHotCursor( HCURSOR hCursor ) const;
+ int SetHotItem( int nIndex ) const;
+ DWORD SetHoverTime( DWORD dwHoverTime = (DWORD)-1 ) const;
+ CSize SetIconSpacing( int cx, int cy ) const;
+ CSize SetIconSpacing( CSize sz ) const;
+ HIMAGELIST SetImageList( HIMAGELIST himl, int iImageListType ) const;
+ BOOL SetItem( LVITEM& pItem ) const;
+ BOOL SetItem( int iItem, int iSubItem, UINT nMask, LPCTSTR pszText, int iImage,
+ UINT nState, UINT nStateMask, LPARAM lParam, int iIndent ) const;
+ void SetItemCount( int iCount ) const;
+ void SetItemCountEx( int iCount, DWORD dwFlags = LVSICF_NOINVALIDATEALL ) const;
+ BOOL SetItemData( int iItem, DWORD_PTR dwData ) const;
+ BOOL SetItemPosition( int iItem, CPoint& pt ) const;
+ BOOL SetItemState( int iItem, LVITEM& Item ) const;
+ void SetItemState( int iItem, UINT nState, UINT nMask ) const;
+ void SetItemText( int iItem, int iSubItem, LPCTSTR pszText ) const;
+ int SetSelectionMark( int iIndex ) const;
+ BOOL SetTextBkColor( COLORREF clrBkText ) const;
+ BOOL SetTextColor( COLORREF clrText ) const;
+ HWND SetToolTips( HWND hWndToolTip ) const;
+ void SetWorkAreas( int nWorkAreas, CRect& pRectArray ) const;
+ int SubItemHitTest( LVHITTESTINFO& htInfo ) const;
+
+ // Operations
+ BOOL Arrange( UINT nCode ) const;
+ HIMAGELIST CreateDragImage( int iItem, CPoint& pt ) const;
+ BOOL DeleteAllItems( ) const;
+ BOOL DeleteColumn( int iCol ) const;
+ BOOL DeleteItem( int iItem ) const;
+ HWND EditLabel( int iItem ) const;
+ BOOL EnsureVisible( int iItem, BOOL fPartialOK ) const;
+ int FindItem( LVFINDINFO& FindInfo, int iStart = -1 ) const;
+ int HitTest( LVHITTESTINFO& HitTestInfo ) const;
+ int HitTest( CPoint pt, UINT* pFlags = NULL ) const;
+ int InsertColumn( int iCol, const LVCOLUMN& pColumn ) const;
+ int InsertColumn( int iCol, LPCTSTR pszColumnHeading, int iFormat = LVCFMT_LEFT,
+ int iWidth = -1, int iSubItem = -1 ) const;
+ int InsertItem( const LVITEM& pItem ) const;
+ int InsertItem( int iItem, LPCTSTR pszText ) const;
+ int InsertItem( int iItem, LPCTSTR pszText, int iImage ) const;
+ BOOL RedrawItems( int iFirst, int iLast ) const;
+ BOOL Scroll( CSize sz ) const;
+ BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORD_PTR dwData ) const;
+ BOOL Update( int iItem ) const;
+
+ private:
+ CListView(const CListView&); // Disable copy construction
+ CListView& operator = (const CListView&); // Disable assignment operator
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+
+ inline void CListView::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = WC_LISTVIEW;
+ }
+
+ inline CSize CListView::ApproximateViewRect(CSize sz /*= CSize(-1, -1)*/, int iCount /* = -1*/) const
+ // Calculates the approximate width and height required to display a given number of items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return CSize( ListView_ApproximateViewRect( m_hWnd, sz.cx, sz.cy, iCount ) );
+ }
+
+ inline COLORREF CListView::GetBkColor( ) const
+ // Retrieves the background color of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetBkColor( m_hWnd );
+ }
+
+ inline BOOL CListView::GetBkImage( LVBKIMAGE& lvbkImage ) const
+ // Retrieves the background image in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetBkImage( m_hWnd, &lvbkImage );
+ }
+
+ inline UINT CListView::GetCallbackMask( ) const
+ // Retrieves the callback mask for a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetCallbackMask( m_hWnd );
+ }
+
+ inline BOOL CListView::GetCheckState( UINT nItem ) const
+ // Determines if an item in a list-view control is selected.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetCheckState( m_hWnd, nItem );
+ }
+
+ inline BOOL CListView::GetColumn( int iCol, LVCOLUMN& Column ) const
+ // Retrieves the attributes of a list-view control's column.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetColumn( m_hWnd, iCol, &Column );
+ }
+
+ inline BOOL CListView::GetColumnOrderArray( LPINT piArray, int iCount /*= -1*/ )
+ // Retrieves the current left-to-right order of columns in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetColumnOrderArray( m_hWnd, iCount, piArray );
+ }
+
+ inline int CListView::GetColumnWidth( int iCol ) const
+ // Retrieves the width of a column in report or list view.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetColumnWidth( m_hWnd, iCol );
+ }
+
+ inline int CListView::GetCountPerPage( ) const
+ // Calculates the number of items that can fit vertically in the visible area of a
+ // list-view control when in list or report view. Only fully visible items are counted.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetCountPerPage( m_hWnd );
+ }
+
+ inline HWND CListView::GetEditControl( ) const
+ // Retrieves the handle to the edit control being used to edit a list-view item's text.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetEditControl( m_hWnd );
+ }
+
+ inline DWORD CListView::GetExtendedStyle( ) const
+ // Retrieves the extended styles that are currently in use for a given list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetExtendedListViewStyle( m_hWnd );
+ }
+
+ inline HWND CListView::GetHeader( ) const
+ // Retrieves the handle to the header control used by a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetHeader( m_hWnd );
+ }
+
+ inline HCURSOR CListView::GetHotCursor( )
+ // Retrieves the HCURSOR used when the pointer is over an item while hot tracking is enabled.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetHotCursor( m_hWnd );
+ }
+
+ inline int CListView::GetHotItem( ) const
+ // Retrieves the index of the hot item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetHotItem( m_hWnd );
+ }
+
+ inline DWORD CListView::GetHoverTime( ) const
+ // Retrieves the amount of time that the mouse cursor must hover over an item before it is selected.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetHoverTime( m_hWnd );
+ }
+
+ inline HIMAGELIST CListView::GetImageList( int nImageType ) const
+ // Retrieves the handle to an image list used for drawing list-view items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetImageList( m_hWnd, nImageType );
+ }
+
+ inline BOOL CListView::GetItem( LVITEM& Item ) const
+ // Retrieves some or all of a list-view item's attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetItem( m_hWnd, &Item );
+ }
+
+ inline int CListView::GetItemCount( ) const
+ // Retrieves the number of items in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetItemCount( m_hWnd );
+ }
+
+ inline DWORD_PTR CListView::GetItemData( int iItem ) const
+ // Retrieves the value(lParam) specific to the item.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVITEM lvi = {0};
+ lvi.iItem = iItem;
+ lvi.mask = LVIF_PARAM;
+ ListView_GetItem(m_hWnd, &lvi);
+ return lvi.lParam;
+ }
+
+ inline BOOL CListView::GetItemPosition( int iItem, CPoint& pt ) const
+ // Retrieves the position of a list-view item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetItemPosition( m_hWnd, iItem, &pt );
+ }
+
+ inline BOOL CListView::GetItemRect( int iItem, CRect& rc, UINT nCode ) const
+ // Retrieves the bounding rectangle for all or part of an item in the current view.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetItemRect( m_hWnd, iItem, &rc, nCode );
+ }
+
+ inline UINT CListView::GetItemState( int iItem, UINT nMask ) const
+ // Retrieves the state of a list-view item.
+
+ // Possible values of nMask:
+ // LVIS_CUT The item is marked for a cut-and-paste operation.
+ // LVIS_DROPHILITED The item is highlighted as a drag-and-drop target.
+ // LVIS_FOCUSED The item has the focus, so it is surrounded by a standard focus rectangle.
+ // LVIS_SELECTED The item is selected.
+ // LVIS_OVERLAYMASK Use this mask to retrieve the item's overlay image index.
+ // LVIS_STATEIMAGEMASK Use this mask to retrieve the item's state image index.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetItemState( m_hWnd, iItem, nMask );
+ }
+
+ inline tString CListView::GetItemText( int iItem, int iSubItem, UINT nTextMax /* = 260 */ ) const
+ // Retrieves the text of a list-view item.
+ // Note: Although the list-view control allows any length string to be stored
+ // as item text, only the first 260 characters are displayed.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ tString t;
+ if (nTextMax > 0)
+ {
+ std::vector<TCHAR> vTChar(nTextMax +1, _T('\0'));
+ TCHAR* pszText = &vTChar.front();
+ LVITEM lvi = {0};
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.mask = LVIF_TEXT;
+ lvi.cchTextMax = nTextMax;
+ lvi.pszText = pszText;
+ ListView_GetItem( m_hWnd, &lvi );
+ t = lvi.pszText;
+ }
+ return t;
+ }
+
+ inline int CListView::GetNextItem( int iItem, int iFlags ) const
+ // Searches for a list-view item that has the specified properties and
+ // bears the specified relationship to a specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetNextItem( m_hWnd, iItem, iFlags );
+ }
+
+ inline UINT CListView::GetNumberOfWorkAreas( ) const
+ // Retrieves the working areas from a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ UINT nWorkAreas = 0;
+ ListView_GetWorkAreas( m_hWnd, nWorkAreas, NULL );
+ return nWorkAreas;
+ }
+
+ inline BOOL CListView::GetOrigin( CPoint& pt ) const
+ // Retrieves the current view origin for a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetOrigin( m_hWnd, &pt );
+ }
+
+ inline UINT CListView::GetSelectedCount( ) const
+ // Determines the number of selected items in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)::SendMessage( m_hWnd, LVM_GETSELECTEDCOUNT, 0L, 0L );
+ }
+
+ inline int CListView::GetSelectionMark( ) const
+ // Retrieves the selection mark from a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)::SendMessage( m_hWnd, LVM_GETSELECTIONMARK, 0L, 0L );
+ }
+
+ inline int CListView::GetStringWidth( LPCTSTR pszString ) const
+ // Determines the width of a specified string using the specified list-view control's current font.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)::SendMessage( m_hWnd, LVM_GETSTRINGWIDTH, 0L, (LPARAM)pszString );
+ }
+
+ inline BOOL CListView::GetSubItemRect( int iItem, int iSubItem, int iCode, CRect& rc ) const
+ // Retrieves information about the rectangle that surrounds a subitem in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetSubItemRect( m_hWnd, iItem, iSubItem, iCode, &rc );
+ }
+
+ inline COLORREF CListView::GetTextBkColor( ) const
+ // Retrieves the text background color of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetTextBkColor( m_hWnd );
+ }
+
+ inline COLORREF CListView::GetTextColor( ) const
+ // Retrieves the text color of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetTextColor( m_hWnd );
+ }
+
+ inline HWND CListView::GetToolTips( ) const
+ // Retrieves the ToolTip control that the list-view control uses to display ToolTips.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetToolTips( m_hWnd );
+ }
+
+ inline int CListView::GetTopIndex( ) const
+ // Retrieves the index of the topmost visible item when in list or report view.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetTopIndex( m_hWnd );
+ }
+
+ inline BOOL CListView::GetViewRect( CRect& rc ) const
+ // Retrieves the bounding rectangle of all items in the list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_GetViewRect( m_hWnd, &rc );
+ }
+
+ inline void CListView::GetWorkAreas( int iWorkAreas, LPRECT pRectArray ) const
+ // Retrieves the working areas from a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_GetWorkAreas( m_hWnd, iWorkAreas, pRectArray );
+ }
+
+ inline BOOL CListView::SetBkColor( COLORREF clrBk ) const
+ // Sets the background color of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetBkColor( m_hWnd, clrBk );
+ }
+
+ inline BOOL CListView::SetBkImage( LVBKIMAGE& lvbkImage ) const
+ // Sets the background image in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetBkImage( m_hWnd, &lvbkImage );
+ }
+
+ inline BOOL CListView::SetCallbackMask( UINT nMask ) const
+ // Changes the callback mask for a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetCallbackMask( m_hWnd, nMask );
+ }
+
+ inline void CListView::SetCheckState( int iItem, BOOL fCheck /*= TRUE*/ ) const
+ // Used to select or deselect an item in a list-view control.
+ // This macro should only be used for list-view controls with the LVS_EX_CHECKBOXES style.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_SetItemState(m_hWnd, iItem, INDEXTOSTATEIMAGEMASK((fCheck==TRUE)?2:1),LVIS_STATEIMAGEMASK);
+ }
+
+ inline BOOL CListView::SetColumn( int iCol, const LVCOLUMN& Column ) const
+ // Sets the attributes of a list-view column.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetColumn( m_hWnd, iCol, &Column );
+ }
+
+ inline BOOL CListView::SetColumnOrderArray( int iCount, LPINT piArray ) const
+ // Sets the left-to-right order of columns in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetColumnOrderArray( m_hWnd, iCount, piArray );
+ }
+
+ inline BOOL CListView::SetColumnWidth( int iCol, int cx ) const
+ // Used to change the width of a column in report view or the width of all columns in list-view mode.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetColumnWidth( m_hWnd, iCol, cx );
+ }
+
+ inline DWORD CListView::SetExtendedStyle( DWORD dwNewStyle ) const
+ // Sets extended styles for list-view controls.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetExtendedListViewStyle( m_hWnd, dwNewStyle );
+ }
+
+ inline HCURSOR CListView::SetHotCursor( HCURSOR hCursor ) const
+ // Sets the HCURSOR that the list-view control uses when the pointer is
+ // over an item while hot tracking is enabled.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetHotCursor( m_hWnd, hCursor );
+ }
+
+ inline int CListView::SetHotItem( int nIndex ) const
+ // Sets the hot item in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetHotItem( m_hWnd, nIndex );
+ }
+
+ inline DWORD CListView::SetHoverTime( DWORD dwHoverTime /*= (DWORD)-1*/ ) const
+ // Sets the amount of time that the mouse cursor must hover over an item before it is selected.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetHoverTime( m_hWnd, dwHoverTime );
+ }
+
+ inline CSize CListView::SetIconSpacing( int cx, int cy ) const
+ // Sets the spacing between icons in list-view controls set to the LVS_ICON style.
+ {
+ assert(::IsWindow(m_hWnd));
+ return CSize( ListView_SetIconSpacing( m_hWnd, cx, cy ) );
+ }
+
+ inline CSize CListView::SetIconSpacing( CSize sz ) const
+ // Sets the spacing between icons in list-view controls set to the LVS_ICON style.
+ {
+ assert(::IsWindow(m_hWnd));
+ return CSize( ListView_SetIconSpacing( m_hWnd, sz.cx, sz.cy ) );
+ }
+
+ inline HIMAGELIST CListView::SetImageList( HIMAGELIST himl, int iImageListType ) const
+ // Assigns an image list to a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetImageList( m_hWnd, himl, iImageListType );
+ }
+
+ inline BOOL CListView::SetItem( LVITEM& Item ) const
+ // Sets some or all of a list-view item's attributes.
+
+ // The declaration for TVITEM:
+ // typedef struct _LVITEM {
+ // UINT mask;
+ // int iItem;
+ // int iSubItem;
+ // UINT state;
+ // UINT stateMask;
+ // LPTSTR pszText;
+ // int cchTextMax;
+ // int iImage;
+ // LPARAM lParam;
+ // } LVITEM, *LVITEM&;
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetItem( m_hWnd, &Item );
+ }
+
+ inline BOOL CListView::SetItem( int iItem, int iSubItem, UINT nMask, LPCTSTR pszText, int iImage,
+ UINT nState, UINT nStateMask, LPARAM lParam, int iIndent ) const
+ // Sets some or all of a list-view item's attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVITEM lvi = {0};
+ lvi.iItem = iItem;
+ lvi.iSubItem = iSubItem;
+ lvi.mask = nMask;
+ lvi.pszText = (LPTSTR)pszText;
+ lvi.iImage = iImage;
+ lvi.state = nState;
+ lvi.stateMask = nStateMask;
+ lvi.lParam = lParam;
+ lvi.iIndent = iIndent;
+
+ return ListView_SetItem( m_hWnd, &lvi);
+ }
+
+ inline void CListView::SetItemCount( int iCount ) const
+ // Causes the list-view control to allocate memory for the specified number of items.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_SetItemCount( m_hWnd, iCount );
+ }
+
+ inline void CListView::SetItemCountEx( int iCount, DWORD dwFlags /*= LVSICF_NOINVALIDATEALL*/ ) const
+ // Sets the virtual number of items in a virtual list view.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_SetItemCountEx( m_hWnd, iCount, dwFlags );
+ }
+
+ inline BOOL CListView::SetItemData( int iItem, DWORD_PTR dwData ) const
+ // Sets the value(lParam) specific to the item.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVITEM lvi = {0};
+ lvi.iItem = iItem;
+ lvi.lParam = dwData;
+ lvi.mask = LVIF_PARAM;
+ return ListView_SetItem(m_hWnd, &lvi);
+ }
+
+ inline BOOL CListView::SetItemPosition( int iItem, CPoint& pt ) const
+ // Moves an item to a specified position in a list-view control (in icon or small icon view).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetItemPosition( m_hWnd, iItem, pt.x, pt.y );
+ }
+
+ inline BOOL CListView::SetItemState( int iItem, LVITEM& Item ) const
+ // Changes the state of an item in a list-view control.
+
+ // Possible values of nMask:
+ // LVIS_CUT The item is marked for a cut-and-paste operation.
+ // LVIS_DROPHILITED The item is highlighted as a drag-and-drop target.
+ // LVIS_FOCUSED The item has the focus, so it is surrounded by a standard focus rectangle.
+ // LVIS_SELECTED The item is selected.
+ // LVIS_OVERLAYMASK Use this mask to retrieve the item's overlay image index.
+ // LVIS_STATEIMAGEMASK Use this mask to retrieve the item's state image index.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMSTATE, (WPARAM)iItem, (LPARAM)&Item);
+ }
+
+ inline void CListView::SetItemState( int iItem, UINT nState, UINT nMask ) const
+ // Changes the state of an item in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_SetItemState(m_hWnd, iItem, nState, nMask);
+ }
+
+ inline void CListView::SetItemText( int iItem, int iSubItem, LPCTSTR pszText ) const
+ // Sets the text color of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_SetItemText(m_hWnd, iItem, iSubItem, (LPTSTR)pszText );
+ }
+
+ inline int CListView::SetSelectionMark( int iIndex ) const
+ // Sets the selection mark in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetSelectionMark( m_hWnd, iIndex );
+ }
+
+ inline BOOL CListView::SetTextBkColor( COLORREF clrBkText ) const
+ // Sets the background color of text in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetTextBkColor( m_hWnd, clrBkText );
+ }
+
+ inline BOOL CListView::SetTextColor( COLORREF clrText ) const
+ // Sets the text color of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SetTextColor( m_hWnd, clrText );
+ }
+
+ inline HWND CListView::SetToolTips( HWND hWndToolTip ) const
+ // Sets the ToolTip control that the list-view control will use to display ToolTips.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HWND)::SendMessage(m_hWnd, LVM_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L);
+ }
+
+ inline void CListView::SetWorkAreas( int nWorkAreas, CRect& pRectArray ) const
+ // Sets the working area within a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ ListView_SetWorkAreas( m_hWnd, nWorkAreas, pRectArray );
+ }
+
+ inline int CListView::SubItemHitTest( LVHITTESTINFO& htInfo ) const
+ // Determines which list-view item or subitem is located at a given position.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SubItemHitTest( m_hWnd, &htInfo );
+ }
+
+ // Operations
+
+ inline BOOL CListView::Arrange( UINT nCode ) const
+ // Arranges items in icon view.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_Arrange( m_hWnd, nCode );
+ }
+
+ inline HIMAGELIST CListView::CreateDragImage( int iItem, CPoint& pt ) const
+ // Creates a drag image list for the specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_CreateDragImage( m_hWnd, iItem, &pt );
+ }
+
+ inline BOOL CListView::DeleteAllItems( ) const
+ // ListView_DeleteAllItems
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_DeleteAllItems( m_hWnd );
+ }
+
+ inline BOOL CListView::DeleteColumn( int iCol ) const
+ // Removes a column from a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_DeleteColumn( m_hWnd, iCol );
+ }
+
+ inline BOOL CListView::DeleteItem( int iItem ) const
+ // Removes an item from a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_DeleteItem( m_hWnd, iItem );
+ }
+
+ inline HWND CListView::EditLabel( int iItem ) const
+ // Begins in-place editing of the specified list-view item's text.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_EditLabel( m_hWnd, iItem );
+ }
+
+ inline BOOL CListView::EnsureVisible( int iItem, BOOL fPartialOK ) const
+ // Ensures that a list-view item is either entirely or partially visible,
+ // scrolling the list-view control if necessary.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(LVM_ENSUREVISIBLE, (WPARAM)iItem, (LPARAM)fPartialOK );
+ }
+
+ inline int CListView::FindItem( LVFINDINFO& FindInfo, int iStart /*= -1*/ ) const
+ // Searches for a list-view item with the specified characteristics.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_FindItem( m_hWnd, iStart, &FindInfo );
+ }
+
+ inline int CListView::HitTest( LVHITTESTINFO& HitTestInfo ) const
+ // Determines which list-view item, if any, is at a specified position.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_HitTest( m_hWnd, &HitTestInfo );
+ }
+
+ inline int CListView::HitTest( CPoint pt, UINT* pFlags /*= NULL*/ ) const
+ // Determines which list-view item, if any, is at a specified position.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVHITTESTINFO hti = {0};
+ hti.flags = *pFlags;
+ hti.pt = pt;
+ return ListView_HitTest( m_hWnd, &hti );
+ }
+
+ inline int CListView::InsertColumn( int iCol, const LVCOLUMN& Column ) const
+ // Inserts a new column in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_InsertColumn( m_hWnd, iCol, &Column );
+ }
+
+ inline int CListView::InsertColumn( int iCol, LPCTSTR pszColumnHeading, int iFormat /*= LVCFMT_LEFT*/,
+ int iWidth /*= -1*/, int iSubItem /*= -1*/ ) const
+ // Inserts a new column in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVCOLUMN lvc = {0};
+ lvc.mask = LVCF_TEXT|LVCF_ORDER|LVCF_FMT;
+ if (-1 != iWidth)
+ {
+ lvc.mask |= LVCF_WIDTH;
+ lvc.cx = iWidth;
+ }
+ if (-1 != iSubItem)
+ {
+ lvc.mask |= LVCF_SUBITEM;
+ lvc.iSubItem = iSubItem;
+ }
+
+ lvc.iOrder = iCol;
+ lvc.pszText = (LPTSTR)pszColumnHeading;
+ lvc.fmt = iFormat;
+ lvc.iSubItem = iSubItem;
+ return ListView_InsertColumn( m_hWnd, iCol, &lvc );
+ }
+
+ inline int CListView::InsertItem( const LVITEM& Item ) const
+ // Inserts a new item in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_InsertItem( m_hWnd, &Item );
+ }
+
+ inline int CListView::InsertItem( int iItem, LPCTSTR pszText ) const
+ // Inserts a new item in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVITEM lvi = {0};
+ lvi.iItem = iItem;
+ lvi.pszText = (LPTSTR)pszText;
+ lvi.mask = LVIF_TEXT;
+ return ListView_InsertItem( m_hWnd, &lvi );
+ }
+
+ inline int CListView::InsertItem( int iItem, LPCTSTR pszText, int iImage ) const
+ // Inserts a new item in a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ LVITEM lvi = {0};
+ lvi.iItem = iItem;
+ lvi.pszText = (LPTSTR)pszText;
+ lvi.iImage = iImage;
+ lvi.mask = LVIF_TEXT | LVIF_IMAGE;
+ return ListView_InsertItem( m_hWnd, &lvi );
+ }
+
+ inline BOOL CListView::RedrawItems( int iFirst, int iLast ) const
+ // Forces a list-view control to redraw a range of items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_RedrawItems( m_hWnd, iFirst, iLast );
+ }
+
+ inline BOOL CListView::Scroll( CSize sz ) const
+ // Scrolls the content of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_Scroll( m_hWnd, sz.cx, sz.cy );
+ }
+
+ inline BOOL CListView::SortItems( PFNLVCOMPARE pfnCompare, DWORD_PTR dwData ) const
+ // Uses an application-defined comparison function to sort the items of a list-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_SortItems( m_hWnd, pfnCompare, dwData );
+ }
+
+ inline BOOL CListView::Update( int iItem ) const
+ // Updates a list-view item. If the list-view control has the LVS_AUTOARRANGE style,
+ // the list-view control is rearranged.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ListView_Update( m_hWnd, iItem );
+ }
+
+} // namespace Win32xx
+
+#endif // #ifndef _WIN32XX_LISTVIEW_H_
+
diff --git a/mmc_updater/depends/win32cpp/mdi.h b/mmc_updater/depends/win32cpp/mdi.h
new file mode 100644
index 00000000..0aa35ffc
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/mdi.h
@@ -0,0 +1,783 @@
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// mdi.h
+// Declaration of the CMDIChild and CMDIFrame classes
+
+// The classes defined here add MDI frames support to Win32++. MDI
+// (Multiple Document Interface) frames host one or more child windows. The
+// child windows hosted by a MDI frame can be different types. For example,
+// some MDI child windows could be used to edit text, while others could be
+// used to display a bitmap. Four classes are defined here to support MDI
+// frames:
+
+
+// 1) CMDIFrame. This class inherits from CFrame, and adds the functionality
+// required by MDI frames. It keeps track of the MDI children created and
+// destroyed, and adjusts the menu when a MDI child is activated. Use the
+// AddMDIChild function to add MDI child windows to the MDI frame. Inherit
+// from CMDIFrame to create your own MDI frame.
+//
+// 2) CMDIChild: All MDI child windows (ie. CWnd classes) should inherit from
+// this class. Each MDI child type can have a different frame menu.
+
+// Use the MDIFrame generic application as the starting point for your own MDI
+// frame applications.
+// Refer to the MDIDemo sample for an example on how to use these classes to
+// create a MDI frame application with different types of MDI child windows.
+
+
+#ifndef _WIN32XX_MDI_H_
+#define _WIN32XX_MDI_H_
+
+#include "frame.h"
+#include <vector>
+
+
+
+namespace Win32xx
+{
+ class CMDIChild;
+ class CMDIFrame;
+ typedef Shared_Ptr<CMDIChild> MDIChildPtr;
+
+ /////////////////////////////////////
+ // Declaration of the CMDIChild class
+ //
+ class CMDIChild : public CWnd
+ {
+ friend class CMDIFrame;
+ public:
+ CMDIChild();
+ virtual ~CMDIChild();
+
+ // These are the functions you might wish to override
+ virtual HWND Create(CWnd* pParent = NULL);
+ virtual void RecalcLayout();
+
+ // These functions aren't virtual, and shouldn't be overridden
+ void SetHandles(HMENU MenuName, HACCEL AccelName);
+ CMDIFrame* GetMDIFrame() const;
+ CWnd* GetView() const {return m_pView;}
+ void SetView(CWnd& pwndView);
+ void MDIActivate() const;
+ void MDIDestroy() const;
+ void MDIMaximize() const;
+ void MDIRestore() const;
+
+ protected:
+ // Its unlikely you would need to override these functions
+ virtual LRESULT FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual void OnCreate();
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CMDIChild(const CMDIChild&); // Disable copy construction
+ CMDIChild& operator = (const CMDIChild&); // Disable assignment operator
+
+ CWnd* m_pView; // pointer to the View CWnd object
+ HMENU m_hChildMenu;
+ HACCEL m_hChildAccel;
+ };
+
+
+ /////////////////////////////////////
+ // Declaration of the CMDIFrame class
+ //
+ class CMDIFrame : public CFrame
+ {
+ friend class CMDIChild; // CMDIChild uses m_hOrigMenu
+ typedef Shared_Ptr<CMDIChild> MDIChildPtr;
+
+ public:
+ class CMDIClient : public CWnd // a nested class within CMDIFrame
+ {
+ public:
+ CMDIClient() {}
+ virtual ~CMDIClient() {}
+ virtual HWND Create(CWnd* pParent = NULL);
+ virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ CMDIFrame* GetMDIFrame() const { return (CMDIFrame*)GetParent(); }
+
+ private:
+ CMDIClient(const CMDIClient&); // Disable copy construction
+ CMDIClient& operator = (const CMDIClient&); // Disable assignment operator
+ };
+
+
+ CMDIFrame();
+ virtual ~CMDIFrame() {}
+
+ virtual CMDIChild* AddMDIChild(MDIChildPtr pMDIChild);
+ virtual CMDIClient& GetMDIClient() const { return (CMDIClient&)m_MDIClient; }
+ virtual BOOL IsMDIFrame() const { return TRUE; }
+ virtual void RemoveMDIChild(HWND hWnd);
+ virtual BOOL RemoveAllMDIChildren();
+ virtual void UpdateCheckMarks();
+
+ // These functions aren't virtual, so don't override them
+ std::vector <MDIChildPtr>& GetAllMDIChildren() {return m_vMDIChild;}
+ CMDIChild* GetActiveMDIChild() const;
+ BOOL IsMDIChildMaxed() const;
+ void MDICascade(int nType = 0) const;
+ void MDIIconArrange() const;
+ void MDIMaximize() const;
+ void MDINext() const;
+ void MDIPrev() const;
+ void MDIRestore() const;
+ void MDITile(int nType = 0) const;
+ void SetActiveMDIChild(CMDIChild* pChild);
+
+ protected:
+ // These are the functions you might wish to override
+ virtual void OnClose();
+ virtual void OnViewStatusBar();
+ virtual void OnViewToolBar();
+ virtual void OnWindowPosChanged();
+ virtual void RecalcLayout();
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CMDIFrame(const CMDIFrame&); // Disable copy construction
+ CMDIFrame& operator = (const CMDIFrame&); // Disable assignment operator
+ void AppendMDIMenu(HMENU hMenuWindow);
+ LRESULT FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ void UpdateFrameMenu(HMENU hMenu);
+
+ CMDIClient m_MDIClient;
+ std::vector <MDIChildPtr> m_vMDIChild;
+ HWND m_hActiveMDIChild;
+ };
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ /////////////////////////////////////
+ // Definitions for the CMDIFrame class
+ //
+ inline CMDIFrame::CMDIFrame() : m_hActiveMDIChild(NULL)
+ {
+ SetView(GetMDIClient());
+ }
+
+ inline CMDIChild* CMDIFrame::AddMDIChild(MDIChildPtr pMDIChild)
+ {
+ assert(NULL != pMDIChild.get()); // Cannot add Null MDI Child
+
+ m_vMDIChild.push_back(pMDIChild);
+ pMDIChild->Create(GetView());
+
+ return pMDIChild.get();
+ }
+
+ inline void CMDIFrame::AppendMDIMenu(HMENU hMenuWindow)
+ {
+ // Adds the additional menu items the the "Window" submenu when
+ // MDI child windows are created
+
+ if (!IsMenu(hMenuWindow))
+ return;
+
+ // Delete previously appended items
+ int nItems = ::GetMenuItemCount(hMenuWindow);
+ UINT uLastID = ::GetMenuItemID(hMenuWindow, --nItems);
+ if ((uLastID >= IDW_FIRSTCHILD) && (uLastID < IDW_FIRSTCHILD + 10))
+ {
+ while ((uLastID >= IDW_FIRSTCHILD) && (uLastID < IDW_FIRSTCHILD + 10))
+ {
+ ::DeleteMenu(hMenuWindow, nItems, MF_BYPOSITION);
+ uLastID = ::GetMenuItemID(hMenuWindow, --nItems);
+ }
+ //delete the separator too
+ ::DeleteMenu(hMenuWindow, nItems, MF_BYPOSITION);
+ }
+
+ int nWindow = 0;
+
+ // Allocate an iterator for our MDIChild vector
+ std::vector <MDIChildPtr>::iterator v;
+
+ for (v = GetAllMDIChildren().begin(); v < GetAllMDIChildren().end(); ++v)
+ {
+ if ((*v)->GetWindowLongPtr(GWL_STYLE) & WS_VISIBLE) // IsWindowVisible is unreliable here
+ {
+ // Add Separator
+ if (0 == nWindow)
+ ::AppendMenu(hMenuWindow, MF_SEPARATOR, 0, NULL);
+
+ // Add a menu entry for each MDI child (up to 9)
+ if (nWindow < 9)
+ {
+ tString tsMenuItem ( (*v)->GetWindowText() );
+
+ if (tsMenuItem.length() > MAX_MENU_STRING -10)
+ {
+ // Truncate the string if its too long
+ tsMenuItem.erase(tsMenuItem.length() - MAX_MENU_STRING +10);
+ tsMenuItem += _T(" ...");
+ }
+
+ TCHAR szMenuString[MAX_MENU_STRING+1];
+ wsprintf(szMenuString, _T("&%d %s"), nWindow+1, tsMenuItem.c_str());
+
+ ::AppendMenu(hMenuWindow, MF_STRING, IDW_FIRSTCHILD + nWindow, szMenuString);
+
+ if (GetActiveMDIChild() == (*v).get())
+ ::CheckMenuItem(hMenuWindow, IDW_FIRSTCHILD+nWindow, MF_CHECKED);
+
+ ++nWindow;
+ }
+ else if (9 == nWindow)
+ // For the 10th MDI child, add this menu item and return
+ {
+ ::AppendMenu(hMenuWindow, MF_STRING, IDW_FIRSTCHILD + nWindow, _T("&Windows..."));
+ return;
+ }
+ }
+ }
+ }
+
+ inline LRESULT CMDIFrame::FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ return ::DefFrameProc(m_hWnd, GetMDIClient(), uMsg, wParam, lParam);
+ }
+
+ inline CMDIChild* CMDIFrame::GetActiveMDIChild() const
+ {
+ return (CMDIChild*)FromHandle(m_hActiveMDIChild);
+ }
+
+ inline BOOL CMDIFrame::IsMDIChildMaxed() const
+ {
+ BOOL bMaxed = FALSE;
+ GetMDIClient().SendMessage(WM_MDIGETACTIVE, 0L, (LPARAM)&bMaxed);
+ return bMaxed;
+ }
+
+ inline void CMDIFrame::MDICascade(int nType /* = 0*/) const
+ {
+ // Possible values for nType are:
+ // MDITILE_SKIPDISABLED Prevents disabled MDI child windows from being cascaded.
+
+ assert(::IsWindow(m_hWnd));
+ GetView()->SendMessage(WM_MDICASCADE, (WPARAM)nType, 0L);
+ }
+
+ inline void CMDIFrame::MDIIconArrange() const
+ {
+ assert(::IsWindow(m_hWnd));
+ GetView()->SendMessage(WM_MDIICONARRANGE, 0L, 0L);
+ }
+
+ inline void CMDIFrame::MDIMaximize() const
+ {
+ assert(::IsWindow(m_hWnd));
+ GetView()->SendMessage(WM_MDIMAXIMIZE, 0L, 0L);
+ }
+
+ inline void CMDIFrame::MDINext() const
+ {
+ assert(::IsWindow(m_hWnd));
+ HWND hMDIChild = GetActiveMDIChild()->GetHwnd();
+ GetView()->SendMessage(WM_MDINEXT, (WPARAM)hMDIChild, FALSE);
+ }
+
+ inline void CMDIFrame::MDIPrev() const
+ {
+ assert(::IsWindow(m_hWnd));
+ HWND hMDIChild = GetActiveMDIChild()->GetHwnd();
+ GetView()->SendMessage(WM_MDINEXT, (WPARAM)hMDIChild, TRUE);
+ }
+
+ inline void CMDIFrame::MDIRestore() const
+ {
+ assert(::IsWindow(m_hWnd));
+ GetView()->SendMessage(WM_MDIRESTORE, 0L, 0L);
+ }
+
+ inline void CMDIFrame::MDITile(int nType /* = 0*/) const
+ {
+ // Possible values for nType are:
+ // MDITILE_HORIZONTAL Tiles MDI child windows so that one window appears above another.
+ // MDITILE_SKIPDISABLED Prevents disabled MDI child windows from being tiled.
+ // MDITILE_VERTICAL Tiles MDI child windows so that one window appears beside another.
+
+ assert(::IsWindow(m_hWnd));
+ GetView()->SendMessage(WM_MDITILE, (WPARAM)nType, 0L);
+ }
+
+ inline void CMDIFrame::OnClose()
+ {
+ if (RemoveAllMDIChildren())
+ {
+ CFrame::OnClose();
+ Destroy();
+ }
+ }
+
+ inline void CMDIFrame::OnViewStatusBar()
+ {
+ CFrame::OnViewStatusBar();
+ UpdateCheckMarks();
+ GetView()->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
+ }
+
+ inline void CMDIFrame::OnViewToolBar()
+ {
+ CFrame::OnViewToolBar();
+ UpdateCheckMarks();
+ GetView()->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
+ }
+
+ inline void CMDIFrame::OnWindowPosChanged()
+ {
+ if (IsMenuBarUsed())
+ {
+ // Refresh MenuBar Window
+ HMENU hMenu= GetMenuBar().GetMenu();
+ GetMenuBar().SetMenu(hMenu);
+ UpdateCheckMarks();
+ }
+ }
+
+ inline BOOL CMDIFrame::PreTranslateMessage(MSG* pMsg)
+ {
+ if (WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST)
+ {
+ if (TranslateMDISysAccel(GetView()->GetHwnd(), pMsg))
+ return TRUE;
+ }
+
+ return CFrame::PreTranslateMessage(pMsg);
+ }
+
+ inline void CMDIFrame::RecalcLayout()
+ {
+ CFrame::RecalcLayout();
+
+ if (GetView()->IsWindow())
+ MDIIconArrange();
+ }
+
+ inline BOOL CMDIFrame::RemoveAllMDIChildren()
+ {
+ BOOL bResult = TRUE;
+ int Children = (int)m_vMDIChild.size();
+
+ // Remove the children in reverse order
+ for (int i = Children-1; i >= 0; --i)
+ {
+ if (IDNO == m_vMDIChild[i]->SendMessage(WM_CLOSE, 0L, 0L)) // Also removes the MDI child
+ bResult = FALSE;
+ }
+
+ return bResult;
+ }
+
+ inline void CMDIFrame::RemoveMDIChild(HWND hWnd)
+ {
+ // Allocate an iterator for our HWND map
+ std::vector <MDIChildPtr>::iterator v;
+
+ for (v = m_vMDIChild.begin(); v!= m_vMDIChild.end(); ++v)
+ {
+ if ((*v)->GetHwnd() == hWnd)
+ {
+ m_vMDIChild.erase(v);
+ break;
+ }
+ }
+
+ if (GetActiveMDIChild())
+ {
+ if (GetActiveMDIChild()->m_hChildMenu)
+ UpdateFrameMenu(GetActiveMDIChild()->m_hChildMenu);
+ if (GetActiveMDIChild()->m_hChildAccel)
+ GetApp()->SetAccelerators(GetActiveMDIChild()->m_hChildAccel, this);
+ }
+ else
+ {
+ if (IsMenuBarUsed())
+ GetMenuBar().SetMenu(GetFrameMenu());
+ else
+ SetMenu(FromHandle(GetFrameMenu()));
+
+ GetApp()->SetAccelerators(GetFrameAccel(), this);
+ }
+ }
+
+ inline void CMDIFrame::SetActiveMDIChild(CMDIChild* pChild)
+ {
+ assert ( pChild->IsWindow() );
+
+ GetMDIClient().SendMessage(WM_MDIACTIVATE, (WPARAM)pChild->GetHwnd(), 0L);
+
+ // Verify
+ assert ( m_hActiveMDIChild == pChild->GetHwnd() );
+ }
+
+ inline void CMDIFrame::UpdateCheckMarks()
+ {
+ if ((GetActiveMDIChild()) && GetActiveMDIChild()->m_hChildMenu)
+ {
+ HMENU hMenu = GetActiveMDIChild()->m_hChildMenu;
+
+ UINT uCheck = GetToolBar().IsWindowVisible()? MF_CHECKED : MF_UNCHECKED;
+ ::CheckMenuItem(hMenu, IDW_VIEW_TOOLBAR, uCheck);
+
+ uCheck = GetStatusBar().IsWindowVisible()? MF_CHECKED : MF_UNCHECKED;
+ ::CheckMenuItem (hMenu, IDW_VIEW_STATUSBAR, uCheck);
+ }
+ }
+
+ inline void CMDIFrame::UpdateFrameMenu(HMENU hMenu)
+ {
+ int nMenuItems = GetMenuItemCount(hMenu);
+ if (nMenuItems > 0)
+ {
+ // The Window menu is typically second from the right
+ int nWindowItem = MAX (nMenuItems -2, 0);
+ HMENU hMenuWindow = ::GetSubMenu (hMenu, nWindowItem);
+
+ if (hMenuWindow)
+ {
+ if (IsMenuBarUsed())
+ {
+ AppendMDIMenu(hMenuWindow);
+ GetMenuBar().SetMenu(hMenu);
+ }
+ else
+ {
+ GetView()->SendMessage (WM_MDISETMENU, (WPARAM) hMenu, (LPARAM)hMenuWindow);
+ DrawMenuBar();
+ }
+ }
+ }
+ UpdateCheckMarks();
+ }
+
+ inline LRESULT CMDIFrame::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_CLOSE:
+ OnClose();
+ return 0;
+
+ case WM_WINDOWPOSCHANGED:
+ // MDI Child or MDI frame has been resized
+ OnWindowPosChanged();
+ break; // Continue with default processing
+
+ } // switch uMsg
+ return CFrame::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+ inline HWND CMDIFrame::CMDIClient::Create(CWnd* pParent)
+ {
+ assert(pParent != 0);
+
+ CLIENTCREATESTRUCT clientcreate ;
+ clientcreate.hWindowMenu = m_hWnd;
+ clientcreate.idFirstChild = IDW_FIRSTCHILD ;
+ DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | MDIS_ALLCHILDSTYLES;
+
+ // Create the view window
+ CreateEx(WS_EX_CLIENTEDGE, _T("MDICLient"), TEXT(""), dwStyle, 0, 0, 0, 0, pParent, NULL, (PSTR) &clientcreate);
+
+ return m_hWnd;
+ }
+
+ inline LRESULT CMDIFrame::CMDIClient::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_MDIDESTROY:
+ {
+ // Do default processing first
+ CallWindowProc(GetPrevWindowProc(), uMsg, wParam, lParam);
+
+ // Now remove MDI child
+ GetMDIFrame()->RemoveMDIChild((HWND) wParam);
+ }
+ return 0; // Discard message
+
+ case WM_MDISETMENU:
+ {
+ if (GetMDIFrame()->IsMenuBarUsed())
+ {
+ return 0L;
+ }
+ }
+ break;
+
+ case WM_MDIACTIVATE:
+ {
+ // Suppress redraw to avoid flicker when activating maximised MDI children
+ SendMessage(WM_SETREDRAW, FALSE, 0L);
+ LRESULT lr = CallWindowProc(GetPrevWindowProc(), WM_MDIACTIVATE, wParam, lParam);
+ SendMessage(WM_SETREDRAW, TRUE, 0L);
+ RedrawWindow(0, 0, RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
+
+ return lr;
+ }
+ }
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+ /////////////////////////////////////
+ //Definitions for the CMDIChild class
+ //
+ inline CMDIChild::CMDIChild() : m_pView(NULL), m_hChildMenu(NULL)
+ {
+ // Set the MDI Child's menu and accelerator in the constructor, like this ...
+ // HMENU hChildMenu = LoadMenu(GetApp()->GetResourceHandle(), _T("MdiMenuView"));
+ // HACCEL hChildAccel = LoadAccelerators(GetApp()->GetResourceHandle(), _T("MDIAccelView"));
+ // SetHandles(hChildMenu, hChildAccel);
+ }
+
+ inline CMDIChild::~CMDIChild()
+ {
+ if (IsWindow())
+ GetParent()->SendMessage(WM_MDIDESTROY, (WPARAM)m_hWnd, 0L);
+
+ if (m_hChildMenu)
+ ::DestroyMenu(m_hChildMenu);
+ }
+
+ inline HWND CMDIChild::Create(CWnd* pParent /*= NULL*/)
+ // We create the MDI child window and then maximize if required.
+ // This technique avoids unnecessary flicker when creating maximized MDI children.
+ {
+ //Call PreCreate in case its overloaded
+ PreCreate(*m_pcs);
+
+ //Determine if the window should be created maximized
+ BOOL bMax = FALSE;
+ pParent->SendMessage(WM_MDIGETACTIVE, 0L, (LPARAM)&bMax);
+ bMax = bMax | (m_pcs->style & WS_MAXIMIZE);
+
+ // Set the Window Class Name
+ TCHAR szClassName[MAX_STRING_SIZE + 1] = _T("Win32++ MDI Child");
+ if (m_pcs->lpszClass)
+ lstrcpyn(szClassName, m_pcs->lpszClass, MAX_STRING_SIZE);
+
+ // Set the window style
+ DWORD dwStyle;
+ dwStyle = m_pcs->style & ~WS_MAXIMIZE;
+ dwStyle |= WS_VISIBLE | WS_OVERLAPPEDWINDOW ;
+
+ // Set window size and position
+ int x = CW_USEDEFAULT;
+ int y = CW_USEDEFAULT;
+ int cx = CW_USEDEFAULT;
+ int cy = CW_USEDEFAULT;
+ if(m_pcs->cx && m_pcs->cy)
+ {
+ x = m_pcs->x;
+ y = m_pcs->y;
+ cx = m_pcs->cx;
+ cy = m_pcs->cy;
+ }
+
+ // Set the extended style
+ DWORD dwExStyle = m_pcs->dwExStyle | WS_EX_MDICHILD;
+
+ // Turn off redraw while creating the window
+ pParent->SendMessage(WM_SETREDRAW, FALSE, 0L);
+
+ // Create the window
+ if (!CreateEx(dwExStyle, szClassName, m_pcs->lpszName, dwStyle, x, y,
+ cx, cy, pParent, FromHandle(m_pcs->hMenu), m_pcs->lpCreateParams))
+ throw CWinException(_T("CMDIChild::Create ... CreateEx failed"));
+
+ if (bMax)
+ ShowWindow(SW_MAXIMIZE);
+
+ // Turn redraw back on
+ pParent->SendMessage(WM_SETREDRAW, TRUE, 0L);
+ pParent->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+
+ // Ensure bits revealed by round corners (XP themes) are redrawn
+ SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
+
+ if (m_hChildMenu)
+ GetMDIFrame()->UpdateFrameMenu(m_hChildMenu);
+ if (m_hChildAccel)
+ GetApp()->SetAccelerators(m_hChildAccel, this);
+
+ return m_hWnd;
+ }
+
+ inline CMDIFrame* CMDIChild::GetMDIFrame() const
+ {
+ CMDIFrame* pMDIFrame = (CMDIFrame*)GetParent()->GetParent();
+ assert(dynamic_cast<CMDIFrame*>(pMDIFrame));
+ return pMDIFrame;
+ }
+
+ inline LRESULT CMDIChild::FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ return ::DefMDIChildProc(m_hWnd, uMsg, wParam, lParam);
+ }
+
+ inline void CMDIChild::MDIActivate() const
+ {
+ GetParent()->SendMessage(WM_MDIACTIVATE, (WPARAM)m_hWnd, 0L);
+ }
+
+ inline void CMDIChild::MDIDestroy() const
+ {
+ GetParent()->SendMessage(WM_MDIDESTROY, (WPARAM)m_hWnd, 0L);
+ }
+
+ inline void CMDIChild::MDIMaximize() const
+ {
+ GetParent()->SendMessage(WM_MDIMAXIMIZE, (WPARAM)m_hWnd, 0L);
+ }
+
+ inline void CMDIChild::MDIRestore() const
+ {
+ GetParent()->SendMessage(WM_MDIRESTORE, (WPARAM)m_hWnd, 0L);
+ }
+
+ inline void CMDIChild::OnCreate()
+ {
+ // Create the view window
+ assert(GetView()); // Use SetView in CMDIChild's constructor to set the view window
+ GetView()->Create(this);
+ RecalcLayout();
+ }
+
+ inline void CMDIChild::RecalcLayout()
+ {
+ // Resize the View window
+ CRect rc = GetClientRect();
+ m_pView->SetWindowPos( NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_SHOWWINDOW );
+ }
+
+ inline void CMDIChild::SetHandles(HMENU hMenu, HACCEL hAccel)
+ {
+ m_hChildMenu = hMenu;
+ m_hChildAccel = hAccel;
+
+ // Note: It is valid to call SetChildMenu before the window is created
+ if (IsWindow())
+ {
+ CWnd* pWnd = GetMDIFrame()->GetActiveMDIChild();
+ if (pWnd == this)
+ {
+ if (m_hChildMenu)
+ GetMDIFrame()->UpdateFrameMenu(m_hChildMenu);
+
+ if (m_hChildAccel)
+ GetApp()->SetAccelerators(m_hChildAccel, GetMDIFrame());
+ }
+ }
+ }
+
+ inline void CMDIChild::SetView(CWnd& wndView)
+ // Sets or changes the View window displayed within the frame
+ {
+ if (m_pView != &wndView)
+ {
+ // Destroy the existing view window (if any)
+ if (m_pView) m_pView->Destroy();
+
+ // Assign the view window
+ m_pView = &wndView;
+
+ if (m_hWnd)
+ {
+ // The frame is already created, so create and position the new view too
+ assert(GetView()); // Use SetView in CMDIChild's constructor to set the view window
+ GetView()->Create(this);
+ RecalcLayout();
+ }
+ }
+ }
+
+ inline LRESULT CMDIChild::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_MDIACTIVATE:
+ {
+ // This child is being activated
+ if (lParam == (LPARAM) m_hWnd)
+ {
+ GetMDIFrame()->m_hActiveMDIChild = m_hWnd;
+ // Set the menu to child default menu
+ if (m_hChildMenu)
+ GetMDIFrame()->UpdateFrameMenu(m_hChildMenu);
+ if (m_hChildAccel)
+ GetApp()->SetAccelerators(m_hChildAccel, this);
+ }
+
+ // No child is being activated
+ if (0 == lParam)
+ {
+ GetMDIFrame()->m_hActiveMDIChild = NULL;
+ // Set the menu to frame's original menu
+ GetMDIFrame()->UpdateFrameMenu(GetMDIFrame()->GetFrameMenu());
+ GetApp()->SetAccelerators(GetMDIFrame()->GetFrameAccel(), this);
+ }
+ }
+ return 0L ;
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ RecalcLayout();
+ break;
+ }
+ }
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_MDI_H_
+
diff --git a/mmc_updater/depends/win32cpp/menu.h b/mmc_updater/depends/win32cpp/menu.h
new file mode 100644
index 00000000..136bafa3
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/menu.h
@@ -0,0 +1,600 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// menu.h
+// Declaration of the CMenu class
+
+// Notes
+// 1) Owner-drawn menus send the WM_MEASUREITEM and WM_DRAWITEM messages
+// to the window that owns the menu. To manage owner drawing for menus,
+// handle these two messages in the CWnd's WndProc function.
+//
+// 2) The CMenu pointer returned by FromHandle might be a temporary pointer. It
+// should be used immediately, not saved for later use.
+//
+// 3) The CMenu pointers returned by FromHandle or GetSubMenu do not need
+// to be deleted. They are automatically deleted by the Win32++.
+//
+// 4) CMenu pointers returned by GetSubMenu are deleted when the parent CMenu is
+// detached, destroyed or deconstructed.
+//
+// 5) The HMENU that is attached to a CMenu object (using the attach function) is
+// automatically deleted when the CMenu object goes out of scope. Detach the
+// HMENU to stop it being deleted when CMenu's destructor is called.
+//
+// 6) Pass CMenu objects by reference or by pointer when passing them as function
+// arguments.
+//
+// 7) In those functions that use a MENUITEMINFO structure, its cbSize member is
+// automatically set to the correct value.
+
+// Program sample
+// --------------
+// void CView::CreatePopup()
+// {
+// CPoint pt = GetCursorPos();
+//
+// // Create the menu
+// CMenu Popup;
+// Popup.CreatePopupMenu();
+//
+// // Add some menu items
+// Popup.AppendMenu(MF_STRING, 101, _T("Menu Item &1"));
+// Popup.AppendMenu(MF_STRING, 102, _T("Menu Item &2"));
+// Popup.AppendMenu(MF_STRING, 103, _T("Menu Item &3"));
+// Popup.AppendMenu(MF_SEPARATOR);
+// Popup.AppendMenu(MF_STRING, 104, _T("Menu Item &4"));
+//
+// // Set menu item states
+// Popup.CheckMenuRadioItem(101, 101, 101, MF_BYCOMMAND);
+// Popup.CheckMenuItem(102, MF_BYCOMMAND | MF_CHECKED);
+// Popup.EnableMenuItem(103, MF_BYCOMMAND | MF_GRAYED);
+// Popup.SetDefaultItem(104);
+//
+// // Display the popup menu
+// Popup.TrackPopupMenu(0, pt.x, pt.y, this);
+// }
+
+
+
+#if !defined(_WIN32XX_MENU_H_) && !defined(_WIN32_WCE)
+#define _WIN32XX_MENU_H_
+
+
+#include "wincore.h"
+#include "gdi.h"
+
+
+namespace Win32xx
+{
+
+ // Forward declarations
+ class CBitmap;
+
+ class CMenu
+ {
+ friend class CWinApp;
+
+ public:
+ //Construction
+ CMenu() : m_hMenu(0), m_IsTmpMenu(FALSE) {}
+ CMenu(UINT nID) : m_IsTmpMenu(FALSE)
+ {
+ m_hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(nID));
+ }
+ ~CMenu();
+
+ //Initialization
+ void Attach(HMENU hMenu);
+ void CreateMenu();
+ void CreatePopupMenu();
+ void DestroyMenu();
+ HMENU Detach();
+ HMENU GetHandle() const;
+ BOOL LoadMenu(LPCTSTR lpszResourceName);
+ BOOL LoadMenu(UINT uIDResource);
+ BOOL LoadMenuIndirect(const void* lpMenuTemplate);
+
+ //Menu Operations
+ BOOL TrackPopupMenu(UINT uFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = 0);
+ BOOL TrackPopupMenuEx(UINT uFlags, int x, int y, CWnd* pWnd, LPTPMPARAMS lptpm);
+
+ //Menu Item Operations
+ BOOL AppendMenu(UINT uFlags, UINT_PTR uIDNewItem = 0, LPCTSTR lpszNewItem = NULL);
+ BOOL AppendMenu(UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp);
+ UINT CheckMenuItem(UINT uIDCheckItem, UINT uCheck);
+ BOOL CheckMenuRadioItem(UINT uIDFirst, UINT uIDLast, UINT uIDItem, UINT uFlags);
+ BOOL DeleteMenu(UINT uPosition, UINT uFlags);
+ UINT EnableMenuItem(UINT uIDEnableItem, UINT uEnable);
+ UINT GetDefaultItem(UINT gmdiFlags, BOOL fByPos = FALSE);
+ DWORD GetMenuContextHelpId() const;
+
+#if(WINVER >= 0x0500) // Minimum OS required is Win2000
+ BOOL GetMenuInfo(LPMENUINFO lpcmi) const;
+ BOOL SetMenuInfo(LPCMENUINFO lpcmi);
+#endif
+
+ UINT GetMenuItemCount() const;
+ UINT GetMenuItemID(int nPos) const;
+ BOOL GetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos = FALSE);
+ UINT GetMenuState(UINT uID, UINT uFlags) const;
+ int GetMenuString(UINT uIDItem, LPTSTR lpString, int nMaxCount, UINT uFlags) const;
+ int GetMenuString(UINT uIDItem, CString& rString, UINT uFlags) const;
+ CMenu* GetSubMenu(int nPos);
+ BOOL InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem = 0, LPCTSTR lpszNewItem = NULL);
+ BOOL InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp);
+ BOOL InsertMenuItem(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos = FALSE);
+ BOOL ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem = 0, LPCTSTR lpszNewItem = NULL);
+ BOOL ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp);
+ BOOL RemoveMenu(UINT uPosition, UINT uFlags);
+ BOOL SetDefaultItem(UINT uItem, BOOL fByPos = FALSE);
+ BOOL SetMenuContextHelpId(DWORD dwContextHelpId);
+ BOOL SetMenuItemBitmaps(UINT uPosition, UINT uFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked);
+ BOOL SetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos = FALSE);
+
+ //Operators
+ BOOL operator != (const CMenu& menu) const;
+ BOOL operator == (const CMenu& menu) const;
+ operator HMENU () const;
+
+ private:
+ CMenu(const CMenu&); // Disable copy construction
+ CMenu& operator = (const CMenu&); // Disable assignment operator
+ void AddToMap();
+ BOOL RemoveFromMap();
+ std::vector<MenuPtr> m_vSubMenus; // A vector of smart pointers to CMenu
+ HMENU m_hMenu;
+ BOOL m_IsTmpMenu;
+ };
+
+ inline CMenu::~CMenu()
+ {
+ if (m_hMenu)
+ {
+ if (!m_IsTmpMenu)
+ {
+ ::DestroyMenu(m_hMenu);
+ }
+
+ RemoveFromMap();
+ }
+
+ m_vSubMenus.clear();
+ }
+
+ inline void CMenu::AddToMap()
+ // Store the HMENU and CMenu pointer in the HMENU map
+ {
+ assert( GetApp() );
+ assert(m_hMenu);
+
+ GetApp()->m_csMapLock.Lock();
+ GetApp()->m_mapHMENU.insert(std::make_pair(m_hMenu, this));
+ GetApp()->m_csMapLock.Release();
+ }
+
+ inline BOOL CMenu::RemoveFromMap()
+ {
+ BOOL Success = FALSE;
+
+ if (GetApp())
+ {
+ // Allocate an iterator for our HDC map
+ std::map<HMENU, CMenu*, CompareHMENU>::iterator m;
+
+ CWinApp* pApp = GetApp();
+ if (pApp)
+ {
+ // Erase the CDC pointer entry from the map
+ pApp->m_csMapLock.Lock();
+ for (m = pApp->m_mapHMENU.begin(); m != pApp->m_mapHMENU.end(); ++m)
+ {
+ if (this == m->second)
+ {
+ pApp->m_mapHMENU.erase(m);
+ Success = TRUE;
+ break;
+ }
+ }
+
+ pApp->m_csMapLock.Release();
+ }
+ }
+
+ return Success;
+ }
+
+
+ inline BOOL CMenu::AppendMenu(UINT uFlags, UINT_PTR uIDNewItem /*= 0*/, LPCTSTR lpszNewItem /*= NULL*/)
+ // Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::AppendMenu(m_hMenu, uFlags, uIDNewItem, lpszNewItem);
+ }
+
+ inline BOOL CMenu::AppendMenu(UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp)
+ // Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu.
+ {
+ assert(IsMenu(m_hMenu));
+ assert(pBmp);
+ return ::AppendMenu(m_hMenu, uFlags, uIDNewItem, (LPCTSTR)pBmp->GetHandle());
+ }
+
+ inline void CMenu::Attach(HMENU hMenu)
+ // Attaches an existing menu to this CMenu
+ {
+ if (m_hMenu != NULL && m_hMenu != hMenu)
+ {
+ ::DestroyMenu(Detach());
+ }
+
+ if (hMenu)
+ {
+ m_hMenu = hMenu;
+ AddToMap();
+ }
+ }
+
+ inline UINT CMenu::CheckMenuItem(UINT uIDCheckItem, UINT uCheck)
+ // Sets the state of the specified menu item's check-mark attribute to either selected or clear.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::CheckMenuItem(m_hMenu, uIDCheckItem, uCheck);
+ }
+
+ inline BOOL CMenu::CheckMenuRadioItem(UINT uIDFirst, UINT uIDLast, UINT uIDItem, UINT uFlags)
+ // Checks a specified menu item and makes it a radio item. At the same time, the function clears
+ // all other menu items in the associated group and clears the radio-item type flag for those items.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::CheckMenuRadioItem(m_hMenu, uIDFirst, uIDLast, uIDItem, uFlags);
+ }
+
+ inline void CMenu::CreateMenu()
+ // Creates an empty menu.
+ {
+ assert(NULL == m_hMenu);
+ m_hMenu = ::CreateMenu();
+ AddToMap();
+ }
+
+ inline void CMenu::CreatePopupMenu()
+ // Creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty.
+ {
+ assert(NULL == m_hMenu);
+ m_hMenu = ::CreatePopupMenu();
+ AddToMap();
+ }
+
+ inline BOOL CMenu::DeleteMenu(UINT uPosition, UINT uFlags)
+ // Deletes an item from the specified menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::DeleteMenu(m_hMenu, uPosition, uFlags);
+ }
+
+ inline void CMenu::DestroyMenu()
+ // Destroys the menu and frees any memory that the menu occupies.
+ {
+ if (::IsMenu(m_hMenu))
+ ::DestroyMenu(m_hMenu);
+
+ m_hMenu = 0;
+ RemoveFromMap();
+ m_vSubMenus.clear();
+ }
+
+ inline HMENU CMenu::Detach()
+ // Detaches the HMENU from this CMenu. If the HMENU is not detached it will be
+ // destroyed when this CMenu is deconstructed.
+ {
+ assert(IsMenu(m_hMenu));
+ HMENU hMenu = m_hMenu;
+ m_hMenu = 0;
+ RemoveFromMap();
+ m_vSubMenus.clear();
+ return hMenu;
+ }
+
+ inline HMENU CMenu::GetHandle() const
+ // Returns the HMENU assigned to this CMenu
+ {
+ return m_hMenu;
+ }
+
+ inline UINT CMenu::EnableMenuItem(UINT uIDEnableItem, UINT uEnable)
+ // Enables, disables, or grays the specified menu item.
+ // The uEnable parameter must be a combination of either MF_BYCOMMAND or MF_BYPOSITION
+ // and MF_ENABLED, MF_DISABLED, or MF_GRAYED.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::EnableMenuItem(m_hMenu, uIDEnableItem, uEnable);
+ }
+
+ inline UINT CMenu::GetDefaultItem(UINT gmdiFlags, BOOL fByPos /*= FALSE*/)
+ // Determines the default menu item.
+ // The gmdiFlags parameter specifies how the function searches for menu items.
+ // This parameter can be zero or more of the following values: GMDI_GOINTOPOPUPS; GMDI_USEDISABLED.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuDefaultItem(m_hMenu, fByPos, gmdiFlags);
+ }
+
+ inline DWORD CMenu::GetMenuContextHelpId() const
+ // Retrieves the Help context identifier associated with the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuContextHelpId(m_hMenu);
+ }
+
+#if(WINVER >= 0x0500)
+// minimum OS required : Win2000
+
+ inline BOOL CMenu::GetMenuInfo(LPMENUINFO lpcmi) const
+ // Retrieves the menu information.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuInfo(m_hMenu, lpcmi);
+ }
+
+ inline BOOL CMenu::SetMenuInfo(LPCMENUINFO lpcmi)
+ // Sets the menu information from the specified MENUINFO structure.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::SetMenuInfo(m_hMenu, lpcmi);
+ }
+
+#endif
+
+ inline UINT CMenu::GetMenuItemCount() const
+ // Retrieves the number of menu items.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuItemCount(m_hMenu);
+ }
+
+ inline UINT CMenu::GetMenuItemID(int nPos) const
+ // Retrieves the menu item identifier of a menu item located at the specified position
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuItemID(m_hMenu, nPos);
+ }
+
+ inline BOOL CMenu::GetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos /*= FALSE*/)
+ // retrieves information about the specified menu item.
+ {
+ assert(IsMenu(m_hMenu));
+ assert(lpMenuItemInfo);
+ lpMenuItemInfo->cbSize = GetSizeofMenuItemInfo();
+ return ::GetMenuItemInfo(m_hMenu, uItem, fByPos, lpMenuItemInfo);
+ }
+
+ inline UINT CMenu::GetMenuState(UINT uID, UINT uFlags) const
+ // Retrieves the menu flags associated with the specified menu item.
+ // Possible values for uFlags are: MF_BYCOMMAND (default) or MF_BYPOSITION.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuState(m_hMenu, uID, uFlags);
+ }
+
+ inline int CMenu::GetMenuString(UINT uIDItem, LPTSTR lpString, int nMaxCount, UINT uFlags) const
+ // Copies the text string of the specified menu item into the specified buffer.
+ {
+ assert(IsMenu(m_hMenu));
+ assert(lpString);
+ return ::GetMenuString(m_hMenu, uIDItem, lpString, nMaxCount, uFlags);
+ }
+
+ inline int CMenu::GetMenuString(UINT uIDItem, CString& rString, UINT uFlags) const
+ // Copies the text string of the specified menu item into the specified buffer.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::GetMenuString(m_hMenu, uIDItem, (LPTSTR)rString.c_str(), rString.GetLength(), uFlags);
+ }
+
+ inline CMenu* CMenu::GetSubMenu(int nPos)
+ // Retrieves the CMenu object of a pop-up menu.
+ {
+ assert(IsMenu(m_hMenu));
+ CMenu* pMenu = new CMenu;
+ pMenu->m_hMenu = ::GetSubMenu(m_hMenu, nPos);
+ pMenu->m_IsTmpMenu = TRUE;
+ m_vSubMenus.push_back(pMenu);
+ return pMenu;
+ }
+
+ inline BOOL CMenu::InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem /*= 0*/, LPCTSTR lpszNewItem /*= NULL*/)
+ // Inserts a new menu item into a menu, moving other items down the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::InsertMenu(m_hMenu, uPosition, uFlags, uIDNewItem, lpszNewItem);
+ }
+
+ inline BOOL CMenu::InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp)
+ // Inserts a new menu item into a menu, moving other items down the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::InsertMenu(m_hMenu, uPosition, uFlags, uIDNewItem, (LPCTSTR)pBmp->GetHandle());
+ }
+
+ inline BOOL CMenu::InsertMenuItem(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos /*= FALSE*/)
+ // Inserts a new menu item at the specified position in a menu.
+ {
+ assert(IsMenu(m_hMenu));
+ assert(lpMenuItemInfo);
+ lpMenuItemInfo->cbSize = GetSizeofMenuItemInfo();
+ return ::InsertMenuItem(m_hMenu, uItem, fByPos, lpMenuItemInfo);
+ }
+
+ inline BOOL CMenu::LoadMenu(LPCTSTR lpszResourceName)
+ // Loads the menu from the specified windows resource.
+ {
+ assert(NULL == m_hMenu);
+ assert(lpszResourceName);
+ m_hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), lpszResourceName);
+ if (m_hMenu) AddToMap();
+ return NULL != m_hMenu;
+ }
+
+ inline BOOL CMenu::LoadMenu(UINT uIDResource)
+ // Loads the menu from the specified windows resource.
+ {
+ assert(NULL == m_hMenu);
+ m_hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(uIDResource));
+ if (m_hMenu) AddToMap();
+ return NULL != m_hMenu;
+ }
+
+ inline BOOL CMenu::LoadMenuIndirect(const void* lpMenuTemplate)
+ // Loads the specified menu template and assigns it to this CMenu.
+ {
+ assert(NULL == m_hMenu);
+ assert(lpMenuTemplate);
+ m_hMenu = ::LoadMenuIndirect(lpMenuTemplate);
+ if (m_hMenu) AddToMap();
+ return NULL != m_hMenu;
+ }
+
+ inline BOOL CMenu::ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem /*= 0*/, LPCTSTR lpszNewItem /*= NULL*/)
+ // Changes an existing menu item. This function is used to specify the content, appearance, and behavior of the menu item.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::ModifyMenu(m_hMenu, uPosition, uFlags, uIDNewItem, lpszNewItem);
+ }
+
+ inline BOOL CMenu::ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp)
+ // Changes an existing menu item. This function is used to specify the content, appearance, and behavior of the menu item.
+ {
+ assert(IsMenu(m_hMenu));
+ assert(pBmp);
+ return ::ModifyMenu(m_hMenu, uPosition, uFlags, uIDNewItem, (LPCTSTR)pBmp->GetHandle());
+ }
+
+ inline BOOL CMenu::RemoveMenu(UINT uPosition, UINT uFlags)
+ // Deletes a menu item or detaches a submenu from the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::RemoveMenu(m_hMenu, uPosition, uFlags);
+ }
+
+ inline BOOL CMenu::SetDefaultItem(UINT uItem, BOOL fByPos /*= FALSE*/)
+ // sets the default menu item for the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::SetMenuDefaultItem(m_hMenu, uItem, fByPos);
+ }
+
+ inline BOOL CMenu::SetMenuContextHelpId(DWORD dwContextHelpId)
+ // Associates a Help context identifier with the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::SetMenuContextHelpId(m_hMenu, dwContextHelpId);
+ }
+
+ inline BOOL CMenu::SetMenuItemBitmaps(UINT uPosition, UINT uFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked)
+ // Associates the specified bitmap with a menu item.
+ {
+ assert(IsMenu(m_hMenu));
+ return ::SetMenuItemBitmaps(m_hMenu, uPosition, uFlags, *pBmpUnchecked, *pBmpChecked);
+ }
+
+ inline BOOL CMenu::SetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos /*= FALSE*/)
+ // Changes information about a menu item.
+ {
+ assert(IsMenu(m_hMenu));
+ assert(lpMenuItemInfo);
+ lpMenuItemInfo->cbSize = GetSizeofMenuItemInfo();
+ return ::SetMenuItemInfo(m_hMenu, uItem, fByPos, lpMenuItemInfo);
+ }
+
+ inline BOOL CMenu::TrackPopupMenu(UINT uFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect /*= 0*/)
+ // Displays a shortcut menu at the specified location and tracks the selection of items on the menu.
+ {
+ assert(IsMenu(m_hMenu));
+ HWND hWnd = pWnd? pWnd->GetHwnd() : 0;
+ return ::TrackPopupMenu(m_hMenu, uFlags, x, y, 0, hWnd, lpRect);
+ }
+
+ inline BOOL CMenu::TrackPopupMenuEx(UINT uFlags, int x, int y, CWnd* pWnd, LPTPMPARAMS lptpm)
+ // Displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu.
+ {
+ assert(IsMenu(m_hMenu));
+ HWND hWnd = pWnd? pWnd->GetHwnd() : 0;
+ return ::TrackPopupMenuEx(m_hMenu, uFlags, x, y, hWnd, lptpm);
+ }
+
+ inline BOOL CMenu::operator != (const CMenu& menu) const
+ // Returns TRUE if the two menu objects are not equal.
+ {
+ return menu.m_hMenu != m_hMenu;
+ }
+
+ inline BOOL CMenu::operator == (const CMenu& menu) const
+ // Returns TRUE of the two menu object are equal
+ {
+ return menu.m_hMenu == m_hMenu;
+ }
+
+ inline CMenu::operator HMENU () const
+ // Retrieves the menu's handle.
+ {
+ return m_hMenu;
+ }
+
+
+ ///////////////////////////////////////
+ // Global functions
+ //
+
+ inline CMenu* FromHandle(HMENU hMenu)
+ // Returns the CMenu object associated with the menu handle (HMENU).
+ {
+ assert( GetApp() );
+ CMenu* pMenu = GetApp()->GetCMenuFromMap(hMenu);
+ if (::IsMenu(hMenu) && pMenu == 0)
+ {
+ GetApp()->AddTmpMenu(hMenu);
+ pMenu = GetApp()->GetCMenuFromMap(hMenu);
+ }
+ return pMenu;
+ }
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_MENU_H_
+
diff --git a/mmc_updater/depends/win32cpp/propertysheet.h b/mmc_updater/depends/win32cpp/propertysheet.h
new file mode 100644
index 00000000..4048942c
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/propertysheet.h
@@ -0,0 +1,960 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////
+// propertysheet.h
+// Declaration of the following classes:
+// CPropertyPage and CPropertySheet
+
+// These classes add support for property sheets to Win32++. A property sheet
+// will have one or more property pages. These pages are much like dialogs
+// which are presented within a tabbed dialog or within a wizard. The data
+// on a property page can be validated before the next page is presented.
+// Property sheets have three modes of use: Modal, Modeless, and Wizard.
+//
+// Refer to the PropertySheet demo program for an example of how propert sheets
+// can be used.
+
+
+#ifndef _WIN32XX_PROPERTYSHEET_H_
+#define _WIN32XX_PROPERTYSHEET_H_
+
+#include "dialog.h"
+
+#define ID_APPLY_NOW 0x3021
+#define ID_WIZBACK 0x3023
+#define ID_WIZNEXT 0x3024
+#define ID_WIZFINISH 0x3025
+#define ID_HELP 0xE146
+
+#ifndef PROPSHEETHEADER_V1_SIZE
+ #define PROPSHEETHEADER_V1_SIZE 40
+#endif
+
+namespace Win32xx
+{
+ class CPropertyPage;
+ typedef Shared_Ptr<CPropertyPage> PropertyPagePtr;
+
+ class CPropertyPage : public CWnd
+ {
+ public:
+ CPropertyPage (UINT nIDTemplate, LPCTSTR szTitle = NULL);
+ virtual ~CPropertyPage() {}
+
+ virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual INT_PTR DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual int OnApply();
+ virtual void OnCancel();
+ virtual void OnHelp();
+ virtual BOOL OnInitDialog();
+ virtual BOOL OnKillActive();
+ virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam);
+ virtual int OnOK();
+ virtual BOOL OnQueryCancel();
+ virtual BOOL OnQuerySiblings(WPARAM wParam, LPARAM lParam);
+ virtual int OnSetActive();
+ virtual int OnWizardBack();
+ virtual INT_PTR OnWizardFinish();
+ virtual int OnWizardNext();
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+
+ static UINT CALLBACK StaticPropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp);
+ static INT_PTR CALLBACK StaticDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ void CancelToClose() const;
+ PROPSHEETPAGE GetPSP() const {return m_PSP;}
+ BOOL IsButtonEnabled(int iButton) const;
+ LRESULT QuerySiblings(WPARAM wParam, LPARAM lParam) const;
+ void SetModified(BOOL bChanged) const;
+ void SetTitle(LPCTSTR szTitle);
+ void SetWizardButtons(DWORD dwFlags) const;
+
+ protected:
+ PROPSHEETPAGE m_PSP;
+
+ private:
+ CPropertyPage(const CPropertyPage&); // Disable copy construction
+ CPropertyPage& operator = (const CPropertyPage&); // Disable assignment operator
+
+ tString m_Title;
+ };
+
+ class CPropertySheet : public CWnd
+ {
+ public:
+ CPropertySheet(UINT nIDCaption, CWnd* pParent = NULL);
+ CPropertySheet(LPCTSTR pszCaption = NULL, CWnd* pParent = NULL);
+ virtual ~CPropertySheet() {}
+
+ // Operations
+ virtual CPropertyPage* AddPage(CPropertyPage* pPage);
+ virtual HWND Create(CWnd* pParent = 0);
+ virtual INT_PTR CreatePropertySheet(LPCPROPSHEETHEADER ppsph);
+ virtual void DestroyButton(int iButton);
+ virtual void Destroy();
+ virtual int DoModal();
+ virtual void RemovePage(CPropertyPage* pPage);
+
+ // State functions
+ BOOL IsModeless() const;
+ BOOL IsWizard() const;
+
+ //Attributes
+ CPropertyPage* GetActivePage() const;
+ int GetPageCount() const;
+ int GetPageIndex(CPropertyPage* pPage) const;
+ HWND GetTabControl() const;
+ virtual BOOL SetActivePage(int nPage);
+ virtual BOOL SetActivePage(CPropertyPage* pPage);
+ virtual void SetIcon(UINT idIcon);
+ virtual void SetTitle(LPCTSTR szTitle);
+ virtual void SetWizardMode(BOOL bWizard);
+
+ protected:
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CPropertySheet(const CPropertySheet&); // Disable copy construction
+ CPropertySheet& operator = (const CPropertySheet&); // Disable assignment operator
+ void BuildPageArray();
+ static void CALLBACK Callback(HWND hwnd, UINT uMsg, LPARAM lParam);
+
+ tString m_Title;
+ std::vector<PropertyPagePtr> m_vPages; // vector of CPropertyPage
+ std::vector<PROPSHEETPAGE> m_vPSP; // vector of PROPSHEETPAGE
+ BOOL m_bInitialUpdate;
+ PROPSHEETHEADER m_PSH;
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
+
+namespace Win32xx
+{
+
+ //////////////////////////////////////////
+ // Definitions for the CPropertyPage class
+ //
+ inline CPropertyPage::CPropertyPage(UINT nIDTemplate, LPCTSTR szTitle /* = NULL*/)
+ {
+ ZeroMemory(&m_PSP, sizeof(PROPSHEETPAGE));
+ SetTitle(szTitle);
+
+ m_PSP.dwSize = sizeof(PROPSHEETPAGE);
+ m_PSP.dwFlags |= PSP_USECALLBACK;
+ m_PSP.hInstance = GetApp()->GetResourceHandle();
+ m_PSP.pszTemplate = MAKEINTRESOURCE(nIDTemplate);
+ m_PSP.pszTitle = m_Title.c_str();
+ m_PSP.pfnDlgProc = (DLGPROC)CPropertyPage::StaticDialogProc;
+ m_PSP.lParam = (LPARAM)this;
+ m_PSP.pfnCallback = CPropertyPage::StaticPropSheetPageProc;
+ }
+
+ inline void CPropertyPage::CancelToClose() const
+ // Disables the Cancel button and changes the text of the OK button to "Close."
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(PSM_CANCELTOCLOSE, 0L, 0L);
+ }
+
+
+ inline INT_PTR CPropertyPage::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Override this function in your class derrived from CPropertyPage if you wish to handle messages
+ // A typical function might look like this:
+
+ // switch (uMsg)
+ // {
+ // case MESSAGE1: // Some Win32 API message
+ // OnMessage1(); // A user defined function
+ // break; // Also do default processing
+ // case MESSAGE2:
+ // OnMessage2();
+ // return x; // Don't do default processing, but instead return
+ // // a value recommended by the Win32 API documentation
+ // }
+
+ // Always pass unhandled messages on to DialogProcDefault
+ return DialogProcDefault(uMsg, wParam, lParam);
+ }
+
+ inline INT_PTR CPropertyPage::DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // All DialogProc functions should pass unhandled messages to this function
+ {
+ LRESULT lr = 0L;
+
+ switch (uMsg)
+ {
+ case UWM_CLEANUPTEMPS:
+ {
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ pTLSData->vTmpWnds.clear();
+ }
+ break;
+
+ case WM_INITDIALOG:
+ return OnInitDialog();
+
+ case PSM_QUERYSIBLINGS:
+ return (BOOL)OnQuerySiblings(wParam, lParam);
+
+ case WM_COMMAND:
+ {
+ // Refelect this message if it's from a control
+ CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam);
+ if (pWnd != NULL)
+ lr = pWnd->OnCommand(wParam, lParam);
+
+ // Handle user commands
+ if (!lr)
+ lr = OnCommand(wParam, lParam);
+
+ if (lr) return 0L;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ // Do Notification reflection if it came from a CWnd object
+ HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom;
+ CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom);
+
+ if (pWndFrom != NULL)
+ lr = pWndFrom->OnNotifyReflect(wParam, lParam);
+ else
+ {
+ // Some controls (eg ListView) have child windows.
+ // Reflect those notifications too.
+ CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom));
+ if (pWndFromParent != NULL)
+ lr = pWndFromParent->OnNotifyReflect(wParam, lParam);
+ }
+
+ // Handle user notifications
+ if (!lr) lr = OnNotify(wParam, lParam);
+
+ // Set the return code for notifications
+ if (IsWindow())
+ SetWindowLongPtr(DWLP_MSGRESULT, (LONG_PTR)lr);
+
+ return (BOOL)lr;
+ }
+
+ case WM_PAINT:
+ {
+ if (::GetUpdateRect(m_hWnd, NULL, FALSE))
+ {
+ CPaintDC dc(this);
+ OnDraw(&dc);
+ }
+ else
+ // RedrawWindow can require repainting without an update rect
+ {
+ CClientDC dc(this);
+ OnDraw(&dc);
+ }
+
+ break;
+ }
+
+ case WM_ERASEBKGND:
+ {
+ CDC dc((HDC)wParam);
+ BOOL bResult = OnEraseBkgnd(&dc);
+ dc.Detach();
+ if (bResult) return TRUE;
+ }
+ break;
+
+ // A set of messages to be reflected back to the control that generated them
+ case WM_CTLCOLORBTN:
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLORLISTBOX:
+ case WM_CTLCOLORSCROLLBAR:
+ case WM_CTLCOLORSTATIC:
+ case WM_DRAWITEM:
+ case WM_MEASUREITEM:
+ case WM_DELETEITEM:
+ case WM_COMPAREITEM:
+ case WM_CHARTOITEM:
+ case WM_VKEYTOITEM:
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ case WM_PARENTNOTIFY:
+ return MessageReflect(m_hWnd, uMsg, wParam, lParam);
+
+ } // switch(uMsg)
+ return FALSE;
+
+ } // INT_PTR CALLBACK CPropertyPage::DialogProc(...)
+
+ inline BOOL CPropertyPage::IsButtonEnabled(int iButton) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return GetParent()->GetDlgItem(iButton)->IsWindowEnabled();
+ }
+
+ inline int CPropertyPage::OnApply()
+ {
+ // This function is called for each page when the Apply button is pressed
+ // Override this function in your derived class if required.
+
+ // The possible return values are:
+ // PSNRET_NOERROR. The changes made to this page are valid and have been applied
+ // PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page.
+ // PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned;
+
+ return PSNRET_NOERROR;
+ }
+
+ inline void CPropertyPage::OnCancel()
+ {
+ // This function is called for each page when the Cancel button is pressed
+ // Override this function in your derived class if required.
+ }
+
+ inline void CPropertyPage::OnHelp()
+ {
+ // This function is called in response to the PSN_HELP notification.
+ SendMessage(m_hWnd, WM_COMMAND, ID_HELP, 0L);
+ }
+
+ inline BOOL CPropertyPage::OnQueryCancel()
+ {
+ // Called when the cancel button is pressed, and before the cancel has taken place
+ // Returns TRUE to prevent the cancel operation, or FALSE to allow it.
+
+ return FALSE; // Allow cancel to proceed
+ }
+
+ inline BOOL CPropertyPage::OnQuerySiblings(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ // Responds to a query request from the Property Sheet.
+ // The values for wParam and lParam are the ones set by
+ // the CPropertySheet::QuerySiblings call
+
+ // return FALSE to allow other siblings to be queried, or
+ // return TRUE to stop query at this page.
+
+ return FALSE;
+ }
+
+ inline BOOL CPropertyPage::OnInitDialog()
+ {
+ // Called when the property page is created
+ // Override this function in your derived class if required.
+
+ return TRUE; // Pass Keyboard control to handle in WPARAM
+ }
+
+ inline BOOL CPropertyPage::OnKillActive()
+ {
+ // This is called in response to a PSN_KILLACTIVE notification, which
+ // is sent whenever the OK or Apply button is pressed.
+ // It provides an opportunity to validate the page contents before it's closed.
+ // Return TRUE to prevent the page from losing the activation, or FALSE to allow it.
+
+ return FALSE;
+ }
+
+ inline int CPropertyPage::OnOK()
+ {
+ // Called for each page when the OK button is pressed
+ // Override this function in your derived class if required.
+
+ // The possible return values are:
+ // PSNRET_NOERROR. The changes made to this page are valid and have been applied
+ // PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page.
+ // PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned;
+
+ return PSNRET_NOERROR;
+ }
+
+ inline LRESULT CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ LPPSHNOTIFY pNotify = (LPPSHNOTIFY)lParam;
+ switch(pNotify->hdr.code)
+ {
+ case PSN_SETACTIVE:
+ return OnSetActive();
+ case PSN_KILLACTIVE:
+ return OnKillActive();
+ case PSN_APPLY:
+ if (pNotify->lParam)
+ return OnOK();
+ else
+ return OnApply();
+ case PSN_RESET:
+ OnCancel();
+ return FALSE;
+ case PSN_QUERYCANCEL:
+ return OnQueryCancel();
+ case PSN_WIZNEXT:
+ return OnWizardNext();
+ case PSN_WIZBACK:
+ return OnWizardBack();
+ case PSN_WIZFINISH:
+ return OnWizardFinish();
+ case PSN_HELP:
+ OnHelp();
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ inline int CPropertyPage::OnSetActive()
+ {
+ // Called when a page becomes active
+ // Override this function in your derived class if required.
+
+ // Returns zero to accept the activation, or -1 to activate the next or the previous page (depending
+ // on whether the user clicked the Next or Back button). To set the activation to a particular page,
+ // return the resource identifier of the page.
+
+ return 0;
+ }
+
+ inline int CPropertyPage::OnWizardBack()
+ {
+ // This function is called when the Back button is pressed on a wizard page
+ // Override this function in your derived class if required.
+
+ // Returns 0 to allow the wizard to go to the previous page. Returns -1 to prevent the wizard
+ // from changing pages. To display a particular page, return its dialog resource identifier.
+
+ return 0;
+ }
+
+ inline INT_PTR CPropertyPage::OnWizardFinish()
+ {
+ // This function is called when the Finish button is pressed on a wizard page
+ // Override this function in your derived class if required.
+
+ // Return Value:
+ // Return non-zero to prevent the wizard from finishing.
+ // Version 5.80. and later. Return a window handle to prevent the wizard from finishing. The wizard will set the focus to that window. The window must be owned by the wizard page.
+ // Return 0 to allow the wizard to finish.
+
+ return 0; // Allow wizard to finish
+ }
+
+ inline int CPropertyPage::OnWizardNext()
+ {
+ // This function is called when the Next button is pressed on a wizard page
+ // Override this function in your derived class if required.
+
+ // Return 0 to allow the wizard to go to the next page. Return -1 to prevent the wizard from
+ // changing pages. To display a particular page, return its dialog resource identifier.
+
+ return 0;
+ }
+
+ inline BOOL CPropertyPage::PreTranslateMessage(MSG* pMsg)
+ {
+ // allow the tab control to translate keyboard input
+ if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 &&
+ (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT))
+ {
+ CWnd* pWndParent = GetParent();
+ if (pWndParent->SendMessage(PSM_ISDIALOGMESSAGE, 0L, (LPARAM)pMsg))
+ return TRUE;
+ }
+
+ // allow the dialog to translate keyboard input
+ if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST))
+ {
+ if (IsDialogMessage(pMsg))
+ return TRUE;
+ }
+
+ return CWnd::PreTranslateMessage(pMsg);
+ }
+
+ inline LRESULT CPropertyPage::QuerySiblings(WPARAM wParam, LPARAM lParam) const
+ {
+ // Sent to a property sheet, which then forwards the message to each of its pages.
+ // Set wParam and lParam to values you want passed to the property pages.
+ // Returns the nonzero value from a page in the property sheet, or zero if no page returns a nonzero value.
+
+ assert(::IsWindow(m_hWnd));
+ return GetParent()->SendMessage(PSM_QUERYSIBLINGS, wParam, lParam);
+ }
+
+ inline void CPropertyPage::SetModified(BOOL bChanged) const
+ {
+ // The property sheet will enable the Apply button if bChanged is TRUE.
+
+ assert(::IsWindow(m_hWnd));
+
+ if (bChanged)
+ GetParent()->SendMessage(PSM_CHANGED, (WPARAM)m_hWnd, 0L);
+ else
+ GetParent()->SendMessage(PSM_UNCHANGED, (WPARAM)m_hWnd, 0L);
+ }
+
+ inline void CPropertyPage::SetTitle(LPCTSTR szTitle)
+ {
+ if (szTitle)
+ {
+ m_Title = szTitle;
+ m_PSP.dwFlags |= PSP_USETITLE;
+ }
+ else
+ {
+ m_Title.erase();
+ m_PSP.dwFlags &= ~PSP_USETITLE;
+ }
+
+ m_PSP.pszTitle = m_Title.c_str();
+ }
+
+ inline void CPropertyPage::SetWizardButtons(DWORD dwFlags) const
+ {
+ // dwFlags: A value that specifies which wizard buttons are enabled. You can combine one or more of the following flags.
+ // PSWIZB_BACK Enable the Back button. If this flag is not set, the Back button is displayed as disabled.
+ // PSWIZB_DISABLEDFINISH Display a disabled Finish button.
+ // PSWIZB_FINISH Display an enabled Finish button.
+ // PSWIZB_NEXT Enable the Next button. If this flag is not set, the Next button is displayed as disabled.
+
+ assert (::IsWindow(m_hWnd));
+ PropSheet_SetWizButtons(::GetParent(m_hWnd), dwFlags);
+ }
+
+ inline UINT CALLBACK CPropertyPage::StaticPropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
+ {
+ assert( GetApp() );
+ UNREFERENCED_PARAMETER(hwnd);
+
+ // Note: the hwnd is always NULL
+
+ switch (uMsg)
+ {
+ case PSPCB_CREATE:
+ {
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ assert(pTLSData);
+
+ // Store the CPropertyPage pointer in Thread Local Storage
+ pTLSData->pCWnd = (CWnd*)ppsp->lParam;
+ }
+ break;
+ }
+
+ return TRUE;
+ }
+
+ inline INT_PTR CALLBACK CPropertyPage::StaticDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ assert( GetApp() );
+
+ // Find matching CWnd pointer for this HWND
+ CPropertyPage* pPage = (CPropertyPage*)GetApp()->GetCWndFromMap(hwndDlg);
+ if (0 == pPage)
+ {
+ // matching CWnd pointer not found, so add it to HWNDMap now
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ pPage = (CPropertyPage*)pTLSData->pCWnd;
+
+ // Set the hWnd members and call DialogProc for this message
+ pPage->m_hWnd = hwndDlg;
+ pPage->AddToMap();
+ }
+
+ return pPage->DialogProc(uMsg, wParam, lParam);
+ }
+
+
+ ///////////////////////////////////////////
+ // Definitions for the CPropertySheet class
+ //
+ inline CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParent /* = NULL*/)
+ {
+ ZeroMemory(&m_PSH, sizeof (PROPSHEETHEADER));
+ SetTitle(LoadString(nIDCaption));
+ m_bInitialUpdate = FALSE;
+
+#ifdef _WIN32_WCE
+ m_PSH.dwSize = sizeof(PROPSHEETHEADER);
+#else
+ if (GetComCtlVersion() >= 471)
+ m_PSH.dwSize = sizeof(PROPSHEETHEADER);
+ else
+ m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE;
+#endif
+
+ m_PSH.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
+ m_PSH.hwndParent = pParent? pParent->GetHwnd() : 0;
+ m_PSH.hInstance = GetApp()->GetInstanceHandle();
+ m_PSH.pfnCallback = (PFNPROPSHEETCALLBACK)CPropertySheet::Callback;
+ }
+
+ inline CPropertySheet::CPropertySheet(LPCTSTR pszCaption /*= NULL*/, CWnd* pParent /* = NULL*/)
+ {
+ ZeroMemory(&m_PSH, sizeof (PROPSHEETHEADER));
+ SetTitle(pszCaption);
+ m_bInitialUpdate = FALSE;
+
+#ifdef _WIN32_WCE
+ m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE;
+#else
+ if (GetComCtlVersion() >= 471)
+ m_PSH.dwSize = sizeof(PROPSHEETHEADER);
+ else
+ m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE;
+#endif
+
+ m_PSH.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
+ m_PSH.hwndParent = pParent? pParent->GetHwnd() : 0;;
+ m_PSH.hInstance = GetApp()->GetInstanceHandle();
+ m_PSH.pfnCallback = (PFNPROPSHEETCALLBACK)CPropertySheet::Callback;
+ }
+
+ inline CPropertyPage* CPropertySheet::AddPage(CPropertyPage* pPage)
+ // Adds a Property Page to the Property Sheet
+ {
+ assert(NULL != pPage);
+
+ m_vPages.push_back(PropertyPagePtr(pPage));
+
+ if (m_hWnd)
+ {
+ // property sheet already exists, so add page to it
+ PROPSHEETPAGE psp = pPage->GetPSP();
+ HPROPSHEETPAGE hpsp = ::CreatePropertySheetPage(&psp);
+ PropSheet_AddPage(m_hWnd, hpsp);
+ }
+
+ m_PSH.nPages = (int)m_vPages.size();
+
+ return pPage;
+ }
+
+ inline void CPropertySheet::BuildPageArray()
+ // Builds the PROPSHEETPAGE array
+ {
+ m_vPSP.clear();
+ std::vector<PropertyPagePtr>::iterator iter;
+ for (iter = m_vPages.begin(); iter < m_vPages.end(); ++iter)
+ m_vPSP.push_back((*iter)->GetPSP());
+
+ PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); // Array of PROPSHEETPAGE
+ m_PSH.ppsp = pPSPArray;
+ }
+
+ inline void CALLBACK CPropertySheet::Callback(HWND hwnd, UINT uMsg, LPARAM lParam)
+ {
+ assert( GetApp() );
+
+ switch(uMsg)
+ {
+ //called before the dialog is created, hwnd = NULL, lParam points to dialog resource
+ case PSCB_PRECREATE:
+ {
+ LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)lParam;
+
+ if(!(lpTemplate->style & WS_SYSMENU))
+ {
+ lpTemplate->style |= WS_SYSMENU;
+ }
+ }
+ break;
+
+ //called after the dialog is created
+ case PSCB_INITIALIZED:
+ {
+ // Retrieve pointer to CWnd object from Thread Local Storage
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ assert(pTLSData);
+
+ CPropertySheet* w = (CPropertySheet*)pTLSData->pCWnd;
+ assert(w);
+
+ w->Attach(hwnd);
+ w->OnCreate();
+ }
+ break;
+ }
+ }
+
+
+ inline HWND CPropertySheet::Create(CWnd* pParent /*= 0*/)
+ // Creates a modeless Property Sheet
+ {
+ assert( GetApp() );
+
+ if (pParent)
+ {
+ m_PSH.hwndParent = pParent->GetHwnd();
+ }
+
+ BuildPageArray();
+ PROPSHEETPAGE* pPSPArray = &m_vPSP.front();
+ m_PSH.ppsp = pPSPArray;
+
+ // Create a modeless Property Sheet
+ m_PSH.dwFlags &= ~PSH_WIZARD;
+ m_PSH.dwFlags |= PSH_MODELESS;
+ HWND hWnd = (HWND)CreatePropertySheet(&m_PSH);
+
+ return hWnd;
+ }
+
+ inline INT_PTR CPropertySheet::CreatePropertySheet(LPCPROPSHEETHEADER ppsph)
+ {
+ assert( GetApp() );
+
+ INT_PTR ipResult = 0;
+
+ // Only one window per CWnd instance allowed
+ assert(!::IsWindow(m_hWnd));
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+
+ // Store the 'this' pointer in Thread Local Storage
+ pTLSData->pCWnd = this;
+
+ // Create the property sheet
+ ipResult = PropertySheet(ppsph);
+
+ return ipResult;
+ }
+
+ inline void CPropertySheet::DestroyButton(int IDButton)
+ {
+ assert(::IsWindow(m_hWnd));
+
+ HWND hwndButton = ::GetDlgItem(m_hWnd, IDButton);
+ if (hwndButton != NULL)
+ {
+ // Hide and disable the button
+ ::ShowWindow(hwndButton, SW_HIDE);
+ ::EnableWindow(hwndButton, FALSE);
+ }
+ }
+
+ inline void CPropertySheet::Destroy()
+ {
+ CWnd::Destroy();
+ m_vPages.clear();
+ }
+
+ inline int CPropertySheet::DoModal()
+ {
+ assert( GetApp() );
+
+ BuildPageArray();
+ PROPSHEETPAGE* pPSPArray = &m_vPSP.front();
+ m_PSH.ppsp = pPSPArray;
+
+ // Create the Property Sheet
+ int nResult = (int)CreatePropertySheet(&m_PSH);
+
+ m_vPages.clear();
+
+ return nResult;
+ }
+
+ inline CPropertyPage* CPropertySheet::GetActivePage() const
+ {
+ assert(::IsWindow(m_hWnd));
+
+ CPropertyPage* pPage = NULL;
+ if (m_hWnd != NULL)
+ {
+ HWND hPage = (HWND)SendMessage(PSM_GETCURRENTPAGEHWND, 0L, 0L);
+ pPage = (CPropertyPage*)FromHandle(hPage);
+ }
+
+ return pPage;
+ }
+
+ inline int CPropertySheet::GetPageCount() const
+ // Returns the number of Property Pages in this Property Sheet
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)m_vPages.size();
+ }
+
+ inline int CPropertySheet::GetPageIndex(CPropertyPage* pPage) const
+ {
+ assert(::IsWindow(m_hWnd));
+
+ for (int i = 0; i < GetPageCount(); i++)
+ {
+ if (m_vPages[i].get() == pPage)
+ return i;
+ }
+ return -1;
+ }
+
+ inline HWND CPropertySheet::GetTabControl() const
+ // Returns the handle to the Property Sheet's tab control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HWND)SendMessage(PSM_GETTABCONTROL, 0L, 0L);
+ }
+
+ inline BOOL CPropertySheet::IsModeless() const
+ {
+ return (m_PSH.dwFlags & PSH_MODELESS);
+ }
+
+ inline BOOL CPropertySheet::IsWizard() const
+ {
+ return (m_PSH.dwFlags & PSH_WIZARD);
+ }
+
+ inline void CPropertySheet::RemovePage(CPropertyPage* pPage)
+ // Removes a Property Page from the Property Sheet
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int nPage = GetPageIndex(pPage);
+ if (m_hWnd != NULL)
+ SendMessage(m_hWnd, PSM_REMOVEPAGE, nPage, 0L);
+
+ m_vPages.erase(m_vPages.begin() + nPage, m_vPages.begin() + nPage+1);
+ m_PSH.nPages = (int)m_vPages.size();
+ }
+
+ inline BOOL CPropertySheet::PreTranslateMessage(MSG* pMsg)
+ {
+ // allow sheet to translate Ctrl+Tab, Shift+Ctrl+Tab, Ctrl+PageUp, and Ctrl+PageDown
+ if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 &&
+ (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT))
+ {
+ if (SendMessage(PSM_ISDIALOGMESSAGE, 0L, (LPARAM)pMsg))
+ return TRUE;
+ }
+
+ // allow the dialog to translate keyboard input
+ if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST))
+ {
+ return GetActivePage()->PreTranslateMessage(pMsg);
+ }
+
+ return CWnd::PreTranslateMessage(pMsg);
+ }
+
+ inline BOOL CPropertySheet::SetActivePage(int nPage)
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(m_hWnd, PSM_SETCURSEL, nPage, 0L);
+ }
+
+ inline BOOL CPropertySheet::SetActivePage(CPropertyPage* pPage)
+ {
+ assert(::IsWindow(m_hWnd));
+ int nPage = GetPageIndex(pPage);
+ if ((nPage >= 0))
+ return SetActivePage(nPage);
+
+ return FALSE;
+ }
+
+ inline void CPropertySheet::SetIcon(UINT idIcon)
+ {
+ m_PSH.pszIcon = MAKEINTRESOURCE(idIcon);
+ m_PSH.dwFlags |= PSH_USEICONID;
+ }
+
+ inline void CPropertySheet::SetTitle(LPCTSTR szTitle)
+ {
+ if (szTitle)
+ m_Title = szTitle;
+ else
+ m_Title.erase();
+
+ m_PSH.pszCaption = m_Title.c_str();
+ }
+
+ inline void CPropertySheet::SetWizardMode(BOOL bWizard)
+ {
+ if (bWizard)
+ m_PSH.dwFlags |= PSH_WIZARD;
+ else
+ m_PSH.dwFlags &= ~PSH_WIZARD;
+ }
+
+ inline LRESULT CPropertySheet::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ LPWINDOWPOS lpWinPos = (LPWINDOWPOS)lParam;
+ if (lpWinPos->flags & SWP_SHOWWINDOW)
+ {
+ if (!m_bInitialUpdate)
+ // The first window positioning with the window visible
+ OnInitialUpdate();
+ m_bInitialUpdate = TRUE;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ m_bInitialUpdate = FALSE;
+ break;
+
+ case WM_SYSCOMMAND:
+ if ((SC_CLOSE == wParam) && (m_PSH.dwFlags & PSH_MODELESS))
+ {
+ Destroy();
+ return 0L;
+ }
+ break;
+ }
+
+ // pass unhandled messages on for default processing
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+}
+
+#endif // _WIN32XX_PROPERTYSHEET_H_
diff --git a/mmc_updater/depends/win32cpp/rebar.h b/mmc_updater/depends/win32cpp/rebar.h
new file mode 100644
index 00000000..1a339dca
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/rebar.h
@@ -0,0 +1,709 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+#ifndef _WIN32XX_REBAR_H_
+#define _WIN32XX_REBAR_H_
+
+#include "wincore.h"
+#include "gdi.h"
+
+
+namespace Win32xx
+{
+
+ struct ReBarTheme
+ {
+ BOOL UseThemes; // TRUE if themes are used
+ COLORREF clrBkgnd1; // Colour 1 for rebar background
+ COLORREF clrBkgnd2; // Colour 2 for rebar background
+ COLORREF clrBand1; // Colour 1 for rebar band background. Use NULL if not required
+ COLORREF clrBand2; // Colour 2 for rebar band background. Use NULL if not required
+ BOOL FlatStyle; // Bands are rendered with flat rather than raised style
+ BOOL BandsLeft; // Position bands left on rearrange
+ BOOL LockMenuBand; // Lock MenuBar's band in dedicated top row, without gripper
+ BOOL RoundBorders; // Use rounded band borders
+ BOOL ShortBands; // Allows bands to be shorter than maximum available width
+ BOOL UseLines; // Displays horizontal lines between bands
+ };
+
+ ////////////////////////////////////
+ // Declaration of the CReBar class
+ //
+ class CReBar : public CWnd
+ {
+ public:
+ CReBar();
+ virtual ~CReBar();
+
+ // Operations
+ BOOL DeleteBand(const int nBand) const;
+ int HitTest(RBHITTESTINFO& rbht);
+ HWND HitTest(POINT pt);
+ int IDToIndex(UINT uBandID) const;
+ BOOL InsertBand(const int nBand, REBARBANDINFO& rbbi) const;
+ BOOL IsBandVisible(int nBand) const;
+ void MaximizeBand(UINT uBand, BOOL fIdeal = FALSE);
+ void MinimizeBand(UINT uBand);
+ BOOL MoveBand(UINT uFrom, UINT uTo);
+ void MoveBandsLeft();
+ BOOL ResizeBand(const int nBand, const CSize& sz) const;
+ BOOL ShowGripper(int nBand, BOOL fShow) const;
+ BOOL ShowBand(int nBand, BOOL fShow) const;
+ BOOL SizeToRect(CRect& rect) const;
+
+ // Attributes
+ int GetBand(const HWND hWnd) const;
+ CRect GetBandBorders(int nBand) const;
+ int GetBandCount() const;
+ BOOL GetBandInfo(const int nBand, REBARBANDINFO& rbbi) const;
+ CRect GetBandRect(int i) const;
+ UINT GetBarHeight() const;
+ BOOL GetBarInfo(REBARINFO& rbi) const;
+ HWND GetMenuBar() {return m_hMenuBar;}
+ ReBarTheme& GetReBarTheme() {return m_Theme;}
+ UINT GetRowCount() const;
+ int GetRowHeight(int nRow) const;
+ UINT GetSizeofRBBI() const;
+ HWND GetToolTips() const;
+ BOOL SetBandBitmap(const int nBand, const CBitmap* pBackground) const;
+ BOOL SetBandColor(const int nBand, const COLORREF clrFore, const COLORREF clrBack) const;
+ BOOL SetBandInfo(const int nBand, REBARBANDINFO& rbbi) const;
+ BOOL SetBarInfo(REBARINFO& rbi) const;
+ void SetMenuBar(HWND hMenuBar) {m_hMenuBar = hMenuBar;}
+ void SetReBarTheme(ReBarTheme& Theme);
+
+ protected:
+ //Overridables
+ virtual BOOL OnEraseBkgnd(CDC* pDC);
+ virtual void PreCreate(CREATESTRUCT& cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CReBar(const CReBar&); // Disable copy construction
+ CReBar& operator = (const CReBar&); // Disable assignment operator
+
+ ReBarTheme m_Theme;
+ BOOL m_bIsDragging;
+ HWND m_hMenuBar;
+ LPARAM m_Orig_lParam;
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ ///////////////////////////////////
+ // Definitions for the CReBar class
+ //
+ inline CReBar::CReBar() : m_bIsDragging(FALSE), m_hMenuBar(0), m_Orig_lParam(0L)
+ {
+ ZeroMemory(&m_Theme, sizeof(ReBarTheme));
+ }
+
+ inline CReBar::~CReBar()
+ {
+ }
+
+ inline BOOL CReBar::DeleteBand(int nBand) const
+ // Deletes a band from a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(RB_DELETEBAND, nBand, 0L);
+ }
+
+ inline int CReBar::GetBand(HWND hWnd) const
+ // Returns the zero based band number for this window handle
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int nResult = -1;
+ if (NULL == hWnd) return nResult;
+
+ for (int nBand = 0; nBand < GetBandCount(); ++nBand)
+ {
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_CHILD;
+ GetBandInfo(nBand, rbbi);
+ if (rbbi.hwndChild == hWnd)
+ nResult = nBand;
+ }
+
+ return nResult;
+ }
+
+ inline CRect CReBar::GetBandBorders(int nBand) const
+ // Retrieves the borders of a band.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ CRect rc;
+ SendMessage(RB_GETBANDBORDERS, nBand, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline int CReBar::GetBandCount() const
+ // Retrieves the count of bands currently in the rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(RB_GETBANDCOUNT, 0L, 0L);
+ }
+
+ inline BOOL CReBar::GetBandInfo(int nBand, REBARBANDINFO& rbbi) const
+ // Retrieves information about a specified band in a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(nBand >= 0);
+
+ // REBARBANDINFO describes individual BAND characteristics
+ rbbi.cbSize = GetSizeofRBBI();
+ return (BOOL)SendMessage(RB_GETBANDINFO, nBand, (LPARAM)&rbbi);
+ }
+
+ inline CRect CReBar::GetBandRect(int i) const
+ // Retrieves the bounding rectangle for a given band in a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ CRect rc;
+ SendMessage(RB_GETRECT, i, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline UINT CReBar::GetBarHeight() const
+ // Retrieves the height of the rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)SendMessage(RB_GETBARHEIGHT, 0L, 0L);
+ }
+
+ inline BOOL CReBar::GetBarInfo(REBARINFO& rbi) const
+ // Retrieves information about the rebar control and the image list it uses.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ // REBARINFO describes overall rebar control characteristics
+ rbi.cbSize = GetSizeofRBBI();
+ return (BOOL)SendMessage(RB_GETBARINFO, 0L, (LPARAM)&rbi);
+ }
+
+ inline UINT CReBar::GetRowCount() const
+ // Retrieves the number of rows of bands in a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)SendMessage(RB_GETROWCOUNT, 0L, 0L);
+ }
+
+ inline int CReBar::GetRowHeight(int nRow) const
+ // Retrieves the height of a specified row in a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(RB_GETROWHEIGHT, nRow, 0L);
+ }
+
+ inline UINT CReBar::GetSizeofRBBI() const
+ // The size of the REBARBANDINFO struct changes according to _WIN32_WINNT
+ // sizeof(REBARBANDINFO) can report an incorrect size for older Window versions,
+ // or newer Window version without XP themes enabled.
+ // Use this function to get a safe size for REBARBANDINFO.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ UINT uSizeof = sizeof(REBARBANDINFO);
+
+ #if defined REBARBANDINFO_V6_SIZE // only defined for VS2008 or higher
+ #if !defined (_WIN32_WINNT) || _WIN32_WINNT >= 0x0600
+ if ((GetWinVersion() < 2600) || (GetComCtlVersion() < 610)) // Vista and Vista themes?
+ uSizeof = REBARBANDINFO_V6_SIZE;
+ #endif
+ #endif
+
+ return uSizeof;
+ }
+
+ inline HWND CReBar::GetToolTips() const
+ // Retrieves the handle to any ToolTip control associated with the rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HWND)SendMessage(RB_GETTOOLTIPS, 0L, 0L);
+ }
+
+ inline int CReBar::HitTest(RBHITTESTINFO& rbht)
+ // Determines which portion of a rebar band is at a given point on the screen,
+ // if a rebar band exists at that point.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(RB_HITTEST, 0L, (LPARAM)&rbht);
+ }
+
+ inline HWND CReBar::HitTest(POINT pt)
+ // Return the child HWND at the given point
+ {
+ assert(::IsWindow(m_hWnd));
+
+ // Convert the point to client co-ordinates
+ ScreenToClient(pt);
+
+ // Get the rebar band with the point
+ RBHITTESTINFO rbhti = {0};
+ rbhti.pt = pt;
+ int iBand = HitTest(rbhti);
+
+ if (iBand >= 0)
+ {
+ // Get the rebar band's hWnd
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_CHILD;
+ GetBandInfo(iBand, rbbi);
+
+ return rbbi.hwndChild;
+ }
+ else
+ return NULL;
+ }
+
+ inline int CReBar::IDToIndex(UINT uBandID) const
+ // Converts a band identifier to a band index in a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(RB_IDTOINDEX, (WPARAM)uBandID, 0L);
+ }
+
+ inline BOOL CReBar::InsertBand(int nBand, REBARBANDINFO& rbbi) const
+ // Inserts a new band in a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ rbbi.cbSize = GetSizeofRBBI();
+ return (BOOL)SendMessage(RB_INSERTBAND, nBand, (LPARAM)&rbbi);
+ }
+
+ inline BOOL CReBar::IsBandVisible(int nBand) const
+ // Returns true if the band is visible
+ {
+ assert(::IsWindow(m_hWnd));
+
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_STYLE;
+ GetBandInfo(nBand, rbbi);
+
+ return !(rbbi.fStyle & RBBS_HIDDEN);
+ }
+
+ inline BOOL CReBar::OnEraseBkgnd(CDC* pDC)
+ {
+ BOOL Erase = TRUE;
+ if (!m_Theme.UseThemes)
+ Erase = FALSE;
+
+ if (!m_Theme.clrBkgnd1 && !m_Theme.clrBkgnd2 && !m_Theme.clrBand1 && !m_Theme.clrBand2)
+ Erase = FALSE;
+
+ if (Erase)
+ {
+ CRect rcReBar = GetClientRect();
+ int BarWidth = rcReBar.Width();
+ int BarHeight = rcReBar.Height();
+
+ // Create and set up our memory DC
+ CMemDC MemDC(pDC);
+ MemDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight);
+
+ // Draw to ReBar background to the memory DC
+ rcReBar.right = 600;
+ MemDC.GradientFill(m_Theme.clrBkgnd1, m_Theme.clrBkgnd2, rcReBar, TRUE);
+ if (BarWidth >= 600)
+ {
+ rcReBar.left = 600;
+ rcReBar.right = BarWidth;
+ MemDC.SolidFill(m_Theme.clrBkgnd2, rcReBar);
+ }
+
+ if (m_Theme.clrBand1 || m_Theme.clrBand2)
+ {
+ // Draw the individual band backgrounds
+ for (int nBand = 0 ; nBand < GetBandCount(); ++nBand)
+ {
+ if (IsBandVisible(nBand))
+ {
+ if (nBand != GetBand(m_hMenuBar))
+ {
+ // Determine the size of this band
+ CRect rcBand = GetBandRect(nBand);
+
+ // Determine the size of the child window
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_CHILD ;
+ GetBandInfo(nBand, rbbi);
+ CRect rcChild;
+ ::GetWindowRect(rbbi.hwndChild, &rcChild);
+ int ChildWidth = rcChild.right - rcChild.left;
+
+ // Determine our drawing rectangle
+ CRect rcDraw = rcBand;
+ rcDraw.bottom = rcDraw.top + (rcBand.bottom - rcBand.top)/2;
+ int xPad = IsXPThemed()? 2: 0;
+ rcDraw.left -= xPad;
+
+ // Fill the Source CDC with the band's background
+ CMemDC SourceDC(pDC);
+ SourceDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight);
+ CRect rcBorder = GetBandBorders(nBand);
+ rcDraw.right = rcBand.left + ChildWidth + rcBorder.left;
+ SourceDC.SolidFill(m_Theme.clrBand1, rcDraw);
+ rcDraw.top = rcDraw.bottom;
+ rcDraw.bottom = rcBand.bottom;
+ SourceDC.GradientFill(m_Theme.clrBand1, m_Theme.clrBand2, rcDraw, FALSE);
+
+ // Set Curve amount for rounded edges
+ int Curve = m_Theme.RoundBorders? 12 : 0;
+
+ // Create our mask for rounded edges using RoundRect
+ CMemDC MaskDC(pDC);
+ MaskDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight);
+
+ rcDraw.top = rcBand.top;
+ if (!m_Theme.FlatStyle)
+ ::InflateRect(&rcDraw, 1, 1);
+
+ int left = rcDraw.left;
+ int right = rcDraw.right;
+ int top = rcDraw.top;
+ int bottom = rcDraw.bottom;
+ int cx = rcDraw.right - rcBand.left + xPad;
+ int cy = rcDraw.bottom - rcBand.top;
+
+ if (m_Theme.FlatStyle)
+ {
+ MaskDC.SolidFill(RGB(0,0,0), rcDraw);
+ MaskDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, PATINVERT);
+ MaskDC.RoundRect(left, top, right, bottom, Curve, Curve);
+ }
+ else
+ {
+ MaskDC.SolidFill(RGB(0,0,0), rcDraw);
+ MaskDC.RoundRect(left, top, right, bottom, Curve, Curve);
+ MaskDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, PATINVERT);
+ }
+
+ // Copy Source DC to Memory DC using the RoundRect mask
+ MemDC.BitBlt(left, top, cx, cy, &SourceDC, left, top, SRCINVERT);
+ MemDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, SRCAND);
+ MemDC.BitBlt(left, top, cx, cy, &SourceDC, left, top, SRCINVERT);
+
+ // Extra drawing to prevent jagged edge while moving bands
+ if (m_bIsDragging)
+ {
+ CClientDC ReBarDC(this);
+ ReBarDC.BitBlt(rcDraw.right - ChildWidth, rcDraw.top, ChildWidth, cy, &MemDC, rcDraw.right - ChildWidth, rcDraw.top, SRCCOPY);
+ }
+ }
+ }
+ }
+ }
+
+ if (m_Theme.UseLines)
+ {
+ // Draw lines between bands
+ for (int j = 0; j < GetBandCount()-1; ++j)
+ {
+ rcReBar = GetBandRect(j);
+ rcReBar.left = MAX(0, rcReBar.left - 4);
+ rcReBar.bottom +=2;
+ MemDC.DrawEdge(rcReBar, EDGE_ETCHED, BF_BOTTOM | BF_ADJUST);
+ }
+ }
+
+ // Copy the Memory DC to the window's DC
+ pDC->BitBlt(0, 0, BarWidth, BarHeight, &MemDC, 0, 0, SRCCOPY);
+ }
+
+ return Erase;
+ }
+
+ inline void CReBar::PreCreate(CREATESTRUCT &cs)
+ // Sets the CREATESTRUCT paramaters prior to window creation
+ {
+ cs.style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
+ CCS_NODIVIDER | RBS_VARHEIGHT | RBS_BANDBORDERS ;
+
+ cs.cy = 100;
+ }
+
+ inline void CReBar::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = REBARCLASSNAME;
+ }
+
+ inline void CReBar::MaximizeBand(UINT uBand, BOOL fIdeal /*= FALSE*/)
+ // Resizes a band in a rebar control to either its ideal or largest size.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(RB_MAXIMIZEBAND, (WPARAM)uBand, (LPARAM)fIdeal);
+ }
+
+ inline void CReBar::MinimizeBand(UINT uBand)
+ // Resizes a band in a rebar control to its smallest size.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(RB_MINIMIZEBAND, (WPARAM)uBand, 0L);
+ }
+
+ inline BOOL CReBar::MoveBand(UINT uFrom, UINT uTo)
+ // Moves a band from one index to another.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(RB_MOVEBAND, (WPARAM)uFrom, (LPARAM)uTo);
+ }
+
+ inline void CReBar::MoveBandsLeft()
+ // Repositions the bands so they are left justified
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int OldrcTop = -1;
+ for (int nBand = GetBandCount() -1; nBand >= 0; --nBand)
+ {
+ CRect rc = GetBandRect(nBand);
+ if (rc.top != OldrcTop)
+ {
+ // Maximize the last band on each row
+ if (IsBandVisible(nBand))
+ {
+ ::SendMessage(GetHwnd(), RB_MAXIMIZEBAND, nBand, 0L);
+ OldrcTop = rc.top;
+ }
+ }
+ }
+ }
+
+ inline BOOL CReBar::ResizeBand(int nBand, const CSize& sz) const
+ // Sets a band's size
+ {
+ assert(::IsWindow(m_hWnd));
+
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_SIZE;
+
+ GetBandInfo(nBand, rbbi);
+ rbbi.cx = sz.cx + 2;
+ rbbi.cxMinChild = sz.cx + 2;
+ rbbi.cyMinChild = sz.cy;
+ rbbi.cyMaxChild = sz.cy;
+
+ return SetBandInfo(nBand, rbbi );
+ }
+
+ inline BOOL CReBar::SetBandBitmap(int nBand, const CBitmap* pBackground) const
+ // Sets the band's bitmaps
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pBackground);
+
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_STYLE;
+ GetBandInfo(nBand, rbbi);
+ rbbi.fMask |= RBBIM_BACKGROUND;
+ rbbi.hbmBack = *pBackground;
+
+ return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi);
+ }
+
+ inline BOOL CReBar::SetBandColor(int nBand, COLORREF clrFore, COLORREF clrBack) const
+ // Sets the band's color
+ // Note: No effect with XP themes enabled
+ // No effect if a bitmap has been set
+ {
+ assert(::IsWindow(m_hWnd));
+
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_COLORS;
+ rbbi.clrFore = clrFore;
+ rbbi.clrBack = clrBack;
+
+ return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi);
+ }
+
+ inline BOOL CReBar::SetBandInfo(int nBand, REBARBANDINFO& rbbi) const
+ // Sets the characteristics of a rebar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(nBand >= 0);
+
+ // REBARBANDINFO describes individual BAND characteristics0
+ rbbi.cbSize = GetSizeofRBBI();
+ return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi);
+ }
+
+ inline BOOL CReBar::SetBarInfo(REBARINFO& rbi) const
+ // REBARINFO associates an image list with the rebar
+ // A band will also need to set RBBIM_IMAGE
+ {
+ assert(::IsWindow(m_hWnd));
+
+ rbi.cbSize = GetSizeofRBBI();
+ return (BOOL)SendMessage(RB_SETBARINFO, 0L, (LPARAM)&rbi);
+ }
+
+ inline void CReBar::SetReBarTheme(ReBarTheme& Theme)
+ {
+ m_Theme.UseThemes = Theme.UseThemes;
+ m_Theme.clrBkgnd1 = Theme.clrBkgnd1;
+ m_Theme.clrBkgnd2 = Theme.clrBkgnd2;
+ m_Theme.clrBand1 = Theme.clrBand1;
+ m_Theme.clrBand2 = Theme.clrBand2;
+ m_Theme.BandsLeft = Theme.BandsLeft;
+ m_Theme.LockMenuBand = Theme.LockMenuBand;
+ m_Theme.ShortBands = Theme.ShortBands;
+ m_Theme.UseLines = Theme.UseLines;
+ m_Theme.FlatStyle = Theme.FlatStyle;
+ m_Theme.RoundBorders = Theme.RoundBorders;
+
+ if (IsWindow())
+ {
+ if (m_Theme.LockMenuBand)
+ ShowGripper(GetBand(m_hMenuBar), FALSE);
+ else
+ ShowGripper(GetBand(m_hMenuBar), TRUE);
+
+ Invalidate();
+ }
+ }
+
+ inline BOOL CReBar::ShowBand(int nBand, BOOL fShow) const
+ // Show or hide a band
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(RB_SHOWBAND, (WPARAM)nBand, (LPARAM)fShow);
+ }
+
+ inline BOOL CReBar::ShowGripper(int nBand, BOOL fShow) const
+ // Show or hide the band's gripper
+ {
+ assert(::IsWindow(m_hWnd));
+
+ REBARBANDINFO rbbi = {0};
+ rbbi.cbSize = GetSizeofRBBI();
+ rbbi.fMask = RBBIM_STYLE;
+ GetBandInfo(nBand, rbbi);
+ if (fShow)
+ {
+ rbbi.fStyle |= RBBS_GRIPPERALWAYS;
+ rbbi.fStyle &= ~RBBS_NOGRIPPER;
+ }
+ else
+ {
+ rbbi.fStyle &= ~RBBS_GRIPPERALWAYS;
+ rbbi.fStyle |= RBBS_NOGRIPPER;
+ }
+
+ return SetBandInfo(nBand, rbbi);
+ }
+
+ inline BOOL CReBar::SizeToRect(CRect& rect) const
+ // Attempts to find the best layout of the bands for the given rectangle.
+ // The rebar bands will be arranged and wrapped as necessary to fit the rectangle.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(RB_SIZETORECT, 0, (LPARAM) (LPRECT)rect);
+ }
+
+ inline LRESULT CReBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_MOUSEMOVE:
+ if (m_Theme.UseThemes && m_Theme.LockMenuBand)
+ {
+ // We want to lock the first row in place, but allow other bands to move!
+ // Use move messages to limit the resizing of bands
+ int y = GET_Y_LPARAM(lParam);
+
+ if (y <= GetRowHeight(0))
+ return 0L; // throw this message away
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ m_Orig_lParam = lParam; // Store the x,y position
+ m_bIsDragging = TRUE;
+ break;
+ case WM_LBUTTONUP:
+ if (m_Theme.UseThemes && m_Theme.LockMenuBand)
+ {
+ // Use move messages to limit the resizing of bands
+ int y = GET_Y_LPARAM(lParam);
+
+ if (y <= GetRowHeight(0))
+ {
+ // Use x,y from WM_LBUTTONDOWN for WM_LBUTTONUP position
+ lParam = m_Orig_lParam;
+ }
+ }
+ m_bIsDragging = FALSE;
+ break;
+ case UWM_GETREBARTHEME:
+ {
+ ReBarTheme& rm = GetReBarTheme();
+ return (LRESULT)&rm;
+ }
+ case UWM_TOOLBAR_RESIZE:
+ {
+ HWND hToolBar = (HWND)wParam;
+ LPSIZE pToolBarSize = (LPSIZE)lParam;
+ ResizeBand(GetBand(hToolBar), *pToolBarSize);
+ }
+ break;
+ }
+
+ // pass unhandled messages on for default processing
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+} // namespace Win32xx
+
+#endif // #ifndef _WIN32XX_REBAR_H_
diff --git a/mmc_updater/depends/win32cpp/release notes.txt b/mmc_updater/depends/win32cpp/release notes.txt
new file mode 100644
index 00000000..bc3114da
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/release notes.txt
@@ -0,0 +1,116 @@
+About Win32++
+-------------
+Win32++ is simple and easy to understand framework for developing Win32
+applications using C++. It brings an object oriented approach to programming
+directly with the Win32 API. Each window created is a C++ class object capable
+of having its own window procedure for routing messages.
+
+Win32++ supports the following compilers and development environments:
+* Borland C++ Compiler 5.5
+* Borland Developer Studio 2006
+* Dev-C++
+* Microsoft Visual C++ Toolkit 2003
+* Microsoft Visual C++ 2005 Express Edition
+* Microsoft Visual C++ 2008 Express Edition
+* Microsoft Visual C++ 2010 Express Edition
+* Microsoft Visual Studio 6.0
+* Microsoft Visual Studio.net 2003
+* Microsoft Visual Studio.net 2005
+* Microsoft Visual Studio.net 2008
+* Microsoft Visual Studio.net 2010
+* MinGW compiler
+
+Win32++ supports the following operating systems
+* Windows 95
+* Windows 98
+* Windows ME
+* Windows NT 4
+* Windows 2000
+* Windows XP (32bit and 64bit)
+* Windows 2003 Server (32bit and 64bit)
+* Windows Vista (32bit and 64bit)
+* Windows 2008 Server (32bit and 64bit)
+* Windows 7 (32 bit and 64 bit)
+* Windows CE from WCE400 (Windows mobile 2003) to WCE600 (Windows mobile 6)
+
+
+Features
+--------
+Win32++ code has the following features
+ * Object Orientated
+ * Subclassing support
+ * Notification reflection and message reflection
+ * Unicode compliant, with multilingual support
+ * Multi-threaded support.
+ * Tracing
+ * 64 bit support
+ * Windows 7 ribbon support
+ * Themes support
+ * Network support (including IP version 6)
+ * Docking windows
+ * Tabbed MDIs
+
+Frames produced by Win32++ include the following:
+ * Rebar
+ * Menubar
+ * Toolbar
+ * Status bar
+ * Tool tips
+
+About the file downloads
+------------------------
+The file download from Sourceforge includes the following:
+ * The Win32++ library itself
+ * Help for the library
+ * A set of tutorials
+ * A collection of sample applications
+
+The sample applications include:
+ * Browser - An Internet browser application with an event sink.
+ * Dialog - An example of a simple dialog application.
+ * DialogDemo - An interative dialog application demonstrating slider controls and progress bars.
+ * DialogResizing - An example of a resizable dialog.
+ * DialogTab - A dialog application with a tab control.
+ * DirectX - A simple DirectX application.
+ * DLL - Shows how to run Win32++ from within a DLL
+ * Dock - An example of a simple docking application.
+ * DockContainer - An example of a docking application which incorporates containers.
+ * DockTabbedMDI - An example of a docking application with containers and a tabbed MDI.
+ * Explorer - A Windows Explorer-like application.
+ * FastGDI - An application which demonstrates direct manipulation of a bitmap's colour.
+ * FormDemo - An example of a modeless dialog within a frame.
+ * Frame - A simple frame application.
+ * GDIPlus - Demonstrates how to use GDI+ with Win32++.
+ * MDIFrame - A simple MDI frame application.
+ * MDIFrameDemo - Demonstrates some additional features of MDI frames.
+ * MDIFrameSplitter - Demonstrates how to implement splitter windows in MDI Child windows.
+ * Networking - Demonstrates the use of networking.
+ * Notepad - A simple text editor with printing.
+ * Performance - Measures Win32++'s message handling speed.
+ * Picture - A simple picture rendering application.
+ * PropertySheets - A demonstration of property sheets.
+ * RibbonFrame - Demonstrates how to use the Windows 7 ribbon with a frame.
+ * RibbonSimple - Demonstrates how to use the Windwos 7 ribbon with a simple window.
+ * Scribble - A simple drawing application.
+ * Simple - Creates a simple window.
+ * Splitter - Demonstrates how to use dockers to create splitter windows.
+ * TabDemo - Demonstrates the use of a CTab control in a frame.
+ * TaskDialog - Demonstrates the use of task dialogs (available on Vista and above).
+ * Themes - Demonstrates how to customise the colours for rebar and toolbar controls.
+ * Threads - Demonstrates multi-threaded Windows.
+ * Tray - Demonstrates how to "minimise" an application to the system tray.
+ * WinCE samples - A small collection of samples for Windows CE
+
+Getting Started
+---------------
+Each file download includes the project files for Dev-C++, CodeBlocks and the
+various compilers from Microsoft. CodeBlocks is an IDE (Integrated Development
+Environment) that supports GNU GCC, Borland Developer Studio 2006 and Microsoft
+C++ Toolkit 2003.
+
+You can start with one of the sample programs, and add your code. Alternatively
+you can start with the projects and sample code provided in the "new projects"
+folder.
+
+For additional information on getting started, refer to the help included
+in the documentation. \ No newline at end of file
diff --git a/mmc_updater/depends/win32cpp/ribbon.h b/mmc_updater/depends/win32cpp/ribbon.h
new file mode 100644
index 00000000..9f6dac5c
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/ribbon.h
@@ -0,0 +1,527 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////
+// ribbon.h
+// Declaration of the following classes:
+// CRibbon and CRibbonFrame
+//
+
+#ifndef _WIN32XX_RIBBON_H_
+#define _WIN32XX_RIBBON_H_
+
+
+// Notes: 1) The Windows 7 SDK must be installed and its directories added to the IDE
+// 2) The ribbon only works on OS Windows 7 and above
+
+//#include <strsafe.h>
+#include <UIRibbon.h> // Contained within the Windows 7 SDK
+#include <UIRibbonPropertyHelpers.h>
+
+namespace Win32xx
+{
+ // Defines the callback entry-point methods for the Ribbon framework.
+ class CRibbon : public IUICommandHandler, public IUIApplication
+ {
+ public:
+ CRibbon() : m_cRef(1), m_pRibbonFramework(NULL) {}
+ ~CRibbon();
+
+ // IUnknown methods.
+ STDMETHOD_(ULONG, AddRef());
+ STDMETHOD_(ULONG, Release());
+ STDMETHOD(QueryInterface(REFIID iid, void** ppv));
+
+ // IUIApplication methods
+ STDMETHOD(OnCreateUICommand)(UINT nCmdID, __in UI_COMMANDTYPE typeID,
+ __deref_out IUICommandHandler** ppCommandHandler);
+
+ STDMETHOD(OnDestroyUICommand)(UINT32 commandId, __in UI_COMMANDTYPE typeID,
+ __in_opt IUICommandHandler* commandHandler);
+
+ STDMETHOD(OnViewChanged)(UINT viewId, __in UI_VIEWTYPE typeId, __in IUnknown* pView,
+ UI_VIEWVERB verb, INT uReasonCode);
+
+ // IUICommandHandle methods
+ STDMETHODIMP Execute(UINT nCmdID, UI_EXECUTIONVERB verb, __in_opt const PROPERTYKEY* key, __in_opt const PROPVARIANT* ppropvarValue,
+ __in_opt IUISimplePropertySet* pCommandExecutionProperties);
+
+ STDMETHODIMP UpdateProperty(UINT nCmdID, __in REFPROPERTYKEY key, __in_opt const PROPVARIANT* ppropvarCurrentValue,
+ __out PROPVARIANT* ppropvarNewValue);
+
+ bool virtual CreateRibbon(CWnd* pWnd);
+ void virtual DestroyRibbon();
+ IUIFramework* GetRibbonFramework() { return m_pRibbonFramework; }
+
+ private:
+ IUIFramework* m_pRibbonFramework;
+ LONG m_cRef; // Reference count.
+
+ };
+
+
+ class CRibbonFrame : public CFrame, public CRibbon
+ {
+ public:
+ // A nested class for the MRU item properties
+ class CRecentFiles : public IUISimplePropertySet
+ {
+ public:
+ CRecentFiles(PWSTR wszFullPath);
+ ~CRecentFiles() {}
+
+ // IUnknown methods.
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
+
+ // IUISimplePropertySet methods
+ STDMETHODIMP GetValue(__in REFPROPERTYKEY key, __out PROPVARIANT *value);
+
+ private:
+ LONG m_cRef; // Reference count.
+ WCHAR m_wszDisplayName[MAX_PATH];
+ WCHAR m_wszFullPath[MAX_PATH];
+ };
+
+ typedef Shared_Ptr<CRecentFiles> RecentFilesPtr;
+
+ CRibbonFrame() : m_uRibbonHeight(0) {}
+ virtual ~CRibbonFrame() {}
+ virtual CRect GetViewRect() const;
+ virtual void OnCreate();
+ virtual void OnDestroy();
+ virtual STDMETHODIMP OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeId, IUnknown* pView, UI_VIEWVERB verb, INT32 uReasonCode);
+ virtual HRESULT PopulateRibbonRecentItems(__deref_out PROPVARIANT* pvarValue);
+ virtual void UpdateMRUMenu();
+
+ UINT GetRibbonHeight() const { return m_uRibbonHeight; }
+
+ private:
+ std::vector<RecentFilesPtr> m_vRecentFiles;
+ void SetRibbonHeight(UINT uRibbonHeight) { m_uRibbonHeight = uRibbonHeight; }
+ UINT m_uRibbonHeight;
+ };
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+ //////////////////////////////////////////////
+ // Definitions for the CRibbon class
+ //
+
+ inline CRibbon::~CRibbon()
+ {
+ // Reference count must be 1 or we have a leak!
+ assert(m_cRef == 1);
+ }
+
+ // IUnknown method implementations.
+ inline STDMETHODIMP_(ULONG) CRibbon::AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ inline STDMETHODIMP_(ULONG) CRibbon::Release()
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ return cRef;
+ }
+
+ inline STDMETHODIMP CRibbon::Execute(UINT nCmdID, UI_EXECUTIONVERB verb, __in_opt const PROPERTYKEY* key, __in_opt const PROPVARIANT* ppropvarValue,
+ __in_opt IUISimplePropertySet* pCommandExecutionProperties)
+ {
+ UNREFERENCED_PARAMETER (nCmdID);
+ UNREFERENCED_PARAMETER (verb);
+ UNREFERENCED_PARAMETER (key);
+ UNREFERENCED_PARAMETER (ppropvarValue);
+ UNREFERENCED_PARAMETER (pCommandExecutionProperties);
+
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CRibbon::QueryInterface(REFIID iid, void** ppv)
+ {
+ if (iid == __uuidof(IUnknown))
+ {
+ *ppv = static_cast<IUnknown*>(static_cast<IUIApplication*>(this));
+ }
+ else if (iid == __uuidof(IUICommandHandler))
+ {
+ *ppv = static_cast<IUICommandHandler*>(this);
+ }
+ else if (iid == __uuidof(IUIApplication))
+ {
+ *ppv = static_cast<IUIApplication*>(this);
+ }
+ else
+ {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ // Called by the Ribbon framework for each command specified in markup, to bind the Command to an IUICommandHandler.
+ inline STDMETHODIMP CRibbon::OnCreateUICommand(UINT nCmdID, __in UI_COMMANDTYPE typeID,
+ __deref_out IUICommandHandler** ppCommandHandler)
+ {
+ UNREFERENCED_PARAMETER(typeID);
+ UNREFERENCED_PARAMETER(nCmdID);
+
+ // By default we use the single command handler provided as part of CRibbon.
+ // Override this function to account for multiple command handlers.
+
+ return QueryInterface(IID_PPV_ARGS(ppCommandHandler));
+ }
+
+ // Called when the state of the Ribbon changes, for example, created, destroyed, or resized.
+ inline STDMETHODIMP CRibbon::OnViewChanged(UINT viewId, __in UI_VIEWTYPE typeId, __in IUnknown* pView,
+ UI_VIEWVERB verb, INT uReasonCode)
+ {
+ UNREFERENCED_PARAMETER(viewId);
+ UNREFERENCED_PARAMETER(typeId);
+ UNREFERENCED_PARAMETER(pView);
+ UNREFERENCED_PARAMETER(verb);
+ UNREFERENCED_PARAMETER(uReasonCode);
+
+
+ return E_NOTIMPL;
+ }
+
+ // Called by the Ribbon framework for each command at the time of ribbon destruction.
+ inline STDMETHODIMP CRibbon::OnDestroyUICommand(UINT32 nCmdID, __in UI_COMMANDTYPE typeID,
+ __in_opt IUICommandHandler* commandHandler)
+ {
+ UNREFERENCED_PARAMETER(commandHandler);
+ UNREFERENCED_PARAMETER(typeID);
+ UNREFERENCED_PARAMETER(nCmdID);
+
+ return E_NOTIMPL;
+ }
+
+ // Called by the Ribbon framework when a command property (PKEY) needs to be updated.
+ inline STDMETHODIMP CRibbon::UpdateProperty(UINT nCmdID, __in REFPROPERTYKEY key, __in_opt const PROPVARIANT* ppropvarCurrentValue,
+ __out PROPVARIANT* ppropvarNewValue)
+ {
+ UNREFERENCED_PARAMETER(nCmdID);
+ UNREFERENCED_PARAMETER(key);
+ UNREFERENCED_PARAMETER(ppropvarCurrentValue);
+ UNREFERENCED_PARAMETER(ppropvarNewValue);
+
+ return E_NOTIMPL;
+ }
+
+ inline bool CRibbon::CreateRibbon(CWnd* pWnd)
+ {
+ ::CoInitialize(NULL);
+
+ // Instantiate the Ribbon framework object.
+ ::CoCreateInstance(CLSID_UIRibbonFramework, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pRibbonFramework));
+
+ // Connect the host application to the Ribbon framework.
+ HRESULT hr = m_pRibbonFramework->Initialize(pWnd->GetHwnd(), this);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ // Load the binary markup. APPLICATION_RIBBON is the default name generated by uicc.
+ hr = m_pRibbonFramework->LoadUI(GetModuleHandle(NULL), L"APPLICATION_RIBBON");
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ inline void CRibbon::DestroyRibbon()
+ {
+ if (m_pRibbonFramework)
+ {
+ m_pRibbonFramework->Destroy();
+ m_pRibbonFramework->Release();
+ m_pRibbonFramework = NULL;
+ }
+ }
+
+
+ //////////////////////////////////////////////
+ // Definitions for the CRibbonFrame class
+ //
+
+ inline CRect CRibbonFrame::GetViewRect() const
+ {
+ // Get the frame's client area
+ CRect rcFrame = GetClientRect();
+
+ // Get the statusbar's window area
+ CRect rcStatus;
+ if (GetStatusBar().IsWindowVisible() || !IsWindowVisible())
+ rcStatus = GetStatusBar().GetWindowRect();
+
+ // Get the top rebar or toolbar's window area
+ CRect rcTop;
+ if (IsReBarSupported() && m_bUseReBar)
+ rcTop = GetReBar().GetWindowRect();
+ else
+ if (m_bUseToolBar && GetToolBar().IsWindowVisible())
+ rcTop = GetToolBar().GetWindowRect();
+
+ // Return client size less the rebar and status windows
+ int top = rcFrame.top + rcTop.Height() + m_uRibbonHeight;
+ int left = rcFrame.left;
+ int right = rcFrame.right;
+ int bottom = rcFrame.Height() - (rcStatus.Height());
+ if ((bottom <= top) ||( right <= left))
+ top = left = right = bottom = 0;
+
+ CRect rcView(left, top, right, bottom);
+ return rcView;
+ }
+
+ inline void CRibbonFrame::OnCreate()
+ {
+ // OnCreate is called automatically during window creation when a
+ // WM_CREATE message received.
+
+ // Tasks such as setting the icon, creating child windows, or anything
+ // associated with creating windows are normally performed here.
+
+ if (GetWinVersion() >= 2601) // WinVersion >= Windows 7
+ {
+ m_bUseReBar = FALSE; // Don't use rebars
+ m_bUseToolBar = FALSE; // Don't use a toolbar
+
+ CFrame::OnCreate();
+
+ if (CreateRibbon(this))
+ TRACE(_T("Ribbon Created Succesfully\n"));
+ else
+ throw CWinException(_T("Failed to create ribbon"));
+ }
+ else
+ {
+ CFrame::OnCreate();
+ }
+ }
+
+ inline void CRibbonFrame::OnDestroy()
+ {
+ DestroyRibbon();
+ CFrame::OnDestroy();
+ }
+
+ inline STDMETHODIMP CRibbonFrame::OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeId, IUnknown* pView, UI_VIEWVERB verb, INT32 uReasonCode)
+ {
+ UNREFERENCED_PARAMETER(viewId);
+ UNREFERENCED_PARAMETER(uReasonCode);
+
+ HRESULT hr = E_NOTIMPL;
+
+ // Checks to see if the view that was changed was a Ribbon view.
+ if (UI_VIEWTYPE_RIBBON == typeId)
+ {
+ switch (verb)
+ {
+ // The view was newly created.
+ case UI_VIEWVERB_CREATE:
+ hr = S_OK;
+ break;
+
+ // The view has been resized. For the Ribbon view, the application should
+ // call GetHeight to determine the height of the ribbon.
+ case UI_VIEWVERB_SIZE:
+ {
+ IUIRibbon* pRibbon = NULL;
+ UINT uRibbonHeight;
+
+ hr = pView->QueryInterface(IID_PPV_ARGS(&pRibbon));
+ if (SUCCEEDED(hr))
+ {
+ // Call to the framework to determine the desired height of the Ribbon.
+ hr = pRibbon->GetHeight(&uRibbonHeight);
+ SetRibbonHeight(uRibbonHeight);
+ pRibbon->Release();
+
+ RecalcLayout();
+ // Use the ribbon height to position controls in the client area of the window.
+ }
+ }
+ break;
+ // The view was destroyed.
+ case UI_VIEWVERB_DESTROY:
+ hr = S_OK;
+ break;
+ }
+ }
+
+ return hr;
+ }
+
+ inline HRESULT CRibbonFrame::PopulateRibbonRecentItems(__deref_out PROPVARIANT* pvarValue)
+ {
+ LONG iCurrentFile = 0;
+ std::vector<tString> FileNames = GetMRUEntries();
+ std::vector<tString>::iterator iter;
+ int iFileCount = FileNames.size();
+ HRESULT hr = E_FAIL;
+ SAFEARRAY* psa = SafeArrayCreateVector(VT_UNKNOWN, 0, iFileCount);
+ m_vRecentFiles.clear();
+
+ if (psa != NULL)
+ {
+ for (iter = FileNames.begin(); iter < FileNames.end(); ++iter)
+ {
+ tString strCurrentFile = (*iter);
+ WCHAR wszCurrentFile[MAX_PATH] = {0L};
+ lstrcpynW(wszCurrentFile, T2W(strCurrentFile.c_str()), MAX_PATH);
+
+ CRecentFiles* pRecentFiles = new CRecentFiles(wszCurrentFile);
+ m_vRecentFiles.push_back(RecentFilesPtr(pRecentFiles));
+ hr = SafeArrayPutElement(psa, &iCurrentFile, static_cast<void*>(pRecentFiles));
+ ++iCurrentFile;
+ }
+
+ SAFEARRAYBOUND sab = {iCurrentFile,0};
+ SafeArrayRedim(psa, &sab);
+ hr = UIInitPropertyFromIUnknownArray(UI_PKEY_RecentItems, psa, pvarValue);
+
+ SafeArrayDestroy(psa); // Calls release for each element in the array
+ }
+
+ return hr;
+ }
+
+ inline void CRibbonFrame::UpdateMRUMenu()
+ {
+ // Suppress UpdateMRUMenu when ribbon is used
+ if (0 != GetRibbonFramework()) return;
+
+ CFrame::UpdateMRUMenu();
+ }
+
+
+ ////////////////////////////////////////////////////////
+ // Declaration of the nested CRecentFiles class
+ //
+ inline CRibbonFrame::CRecentFiles::CRecentFiles(PWSTR wszFullPath) : m_cRef(1)
+ {
+ SHFILEINFOW sfi;
+ DWORD_PTR dwPtr = NULL;
+ m_wszFullPath[0] = L'\0';
+ m_wszDisplayName[0] = L'\0';
+
+ if (NULL != lstrcpynW(m_wszFullPath, wszFullPath, MAX_PATH))
+ {
+ dwPtr = ::SHGetFileInfoW(wszFullPath, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
+
+ if (dwPtr != NULL)
+ {
+ lstrcpynW(m_wszDisplayName, sfi.szDisplayName, MAX_PATH);
+ }
+ else // Provide a reasonable fallback.
+ {
+ lstrcpynW(m_wszDisplayName, m_wszFullPath, MAX_PATH);
+ }
+ }
+ }
+
+ inline STDMETHODIMP_(ULONG) CRibbonFrame::CRecentFiles::AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ inline STDMETHODIMP_(ULONG) CRibbonFrame::CRecentFiles::Release()
+ {
+ return InterlockedDecrement(&m_cRef);
+ }
+
+ inline STDMETHODIMP CRibbonFrame::CRecentFiles::QueryInterface(REFIID iid, void** ppv)
+ {
+ if (!ppv)
+ {
+ return E_POINTER;
+ }
+
+ if (iid == __uuidof(IUnknown))
+ {
+ *ppv = static_cast<IUnknown*>(this);
+ }
+ else if (iid == __uuidof(IUISimplePropertySet))
+ {
+ *ppv = static_cast<IUISimplePropertySet*>(this);
+ }
+ else
+ {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ // IUISimplePropertySet methods.
+ inline STDMETHODIMP CRibbonFrame::CRecentFiles::GetValue(__in REFPROPERTYKEY key, __out PROPVARIANT *ppropvar)
+ {
+ HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+
+ if (key == UI_PKEY_Label)
+ {
+ hr = UIInitPropertyFromString(key, m_wszDisplayName, ppropvar);
+ }
+ else if (key == UI_PKEY_LabelDescription)
+ {
+ hr = UIInitPropertyFromString(key, m_wszDisplayName, ppropvar);
+ }
+
+ return hr;
+ }
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_RIBBON_H_
+
diff --git a/mmc_updater/depends/win32cpp/shared_ptr.h b/mmc_updater/depends/win32cpp/shared_ptr.h
new file mode 100644
index 00000000..0d2f8b0c
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/shared_ptr.h
@@ -0,0 +1,199 @@
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// This software was developed from code available in the public domain
+// and has no copyright.
+
+
+// About Shared_Ptr:
+// Shared_Ptr wraps a reference-counted smart pointer around a dynamically
+// allocated object. Unlike auto_ptr, the Shared_Ptr can be used as a smart
+// pointer for objects stored in containers like std::vector. Do not use
+// Shared_Ptr (or shared_ptr or auto_ptr) for dynamically allocated arrays.
+// See below for advice on how to wrap dynamically allocated arrays in a
+// vector.
+//
+// The next standard of C++ will also contain a shared_ptr. Some modern
+// compilers already have a shared_ptr available as std::tr1::shared_ptr. If
+// your compiler already provides a shared_ptr, or if you have Boost, you
+// should use that smart pointer instead. This class has been provided for
+// those users who don't have easy access to an "official" shared_ptr.
+// Note that this class is "Shared_Ptr", a slightly different name to the
+// future "shared_ptr" to avoid naming conflicts.
+
+// Advantages of Shared_Ptr (or shared_ptr where available):
+// - Shared_Ptr can be safely copied. This makes then suitable for containers.
+// - Shared_Ptr automatically calls delete for the wrapped pointer when
+// its last copy goes out of scope.
+// - Shared_Ptr simplifies exception safety.
+//
+// Without smart pointers, it can be quite challenging to ensure that every
+// dynamically allocated pointer (i.e. use of new) is deleted in the event of
+// all possible exceptions. In addition to the exceptions we throw ourselves,
+// "new" itself will throw an exception it it fails, as does the STL (Standard
+// Template Library which includes vector and string). Without smart pointers
+// we often need to resort to additional try/catch blocks simply to avoid
+// memory leaks when exceptions occur.
+
+// Examples:
+// Shared_Ptr<CWnd> w1(new CWnd);
+// or
+// Shared_Ptr<CWnd> w1 = new CWnd;
+// or
+// typedef Shared_Ptr<CWnd> CWndPtr;
+// CWndPtr w1 = new CWnd;
+// or
+// typedef Shared_Ptr<CWnd> CWndPtr;
+// CWndPtr w1(new CWnd);
+//
+// And with a vector
+// typedef Shared_Ptr<CWnd> CWndPtr;
+// std::vector<CWndPtr> MyVector;
+// MyVector.push_back(new CWnd);
+// or
+// typedef Shared_Ptr<CWnd> CWndPtr;
+// CWnd* pWnd = new CWnd;
+// std::vector<CWndPtr> MyVector;
+// MyVector.push_back(pWnd);
+//
+
+// How to handle dynamically allocated arrays:
+// While we could create a smart pointer for arrays, we don't need to because
+// std::vector already handles this for us. Consider the following example:
+// int nLength = ::GetWindowTextLength(m_hWnd);
+// pTChar = new TCHAR[nLength+1];
+// memset(pTChar, 0, (nLength+1)*sizeof(TCHAR));
+// ::GetWindowText(m_hWnd, m_pTChar, nLength+1);
+// ....
+// delete[] pTChar;
+//
+// This can be improved by using a vector instead of an array
+// int nLength = ::GetWindowTextLength(m_hWnd);
+// std::vector<TCHAR> vTChar( nLength+1, _T('\0') );
+// TCHAR* pTCharArray = &vTChar.front();
+// ::GetWindowText(m_hWnd, pTCharArray, nLength+1);
+//
+// This works because the memory in a vector is always contiguous. Note that
+// this is NOT always true of std::string.
+
+
+// Summing up:
+// In my opinion, "naked" pointers for dynamically created objects should be
+// avoided in modern C++ code. That's to say that calls to "new" should be
+// wrapped in some sort of smart pointer wherever possible. This eliminates
+// the possibility of memory leaks (particularly in the event of exceptions).
+// It also elminiates the need for delete in user's code.
+
+#ifndef _WIN32XX_SHARED_PTR_
+#define _WIN32XX_SHARED_PTR_
+
+namespace Win32xx
+{
+
+ template <class T1>
+ class Shared_Ptr
+ {
+ public:
+ Shared_Ptr() : m_ptr(NULL), m_count(NULL) { }
+ Shared_Ptr(T1 * p) : m_ptr(p), m_count(NULL)
+ {
+ try
+ {
+ if (m_ptr) m_count = new long(0);
+ inc_ref();
+ }
+ // catch the unlikely event of 'new long(0)' throwing an exception
+ catch (const std::bad_alloc&)
+ {
+ delete m_ptr;
+ throw;
+ }
+ }
+ Shared_Ptr(const Shared_Ptr& rhs) : m_ptr(rhs.m_ptr), m_count(rhs.m_count) { inc_ref(); }
+ ~Shared_Ptr()
+ {
+ if(m_count && 0 == dec_ref())
+ {
+ // Note: This code doesn't handle a pointer to an array.
+ // We would need delete[] m_ptr to handle that.
+ delete m_ptr;
+ delete m_count;
+ }
+ }
+
+ T1* get() const { return m_ptr; }
+ long use_count() const { return m_count? *m_count : 0; }
+ bool unique() const { return (m_count && (*m_count == 1)); }
+
+ void swap(Shared_Ptr& rhs)
+ {
+ std::swap(m_ptr, rhs.m_ptr);
+ std::swap(m_count, rhs.m_count);
+ }
+
+ Shared_Ptr& operator=(const Shared_Ptr& rhs)
+ {
+ Shared_Ptr tmp(rhs);
+ this->swap(tmp);
+ return *this;
+ }
+
+ T1* operator->() const
+ {
+ assert(m_ptr);
+ return m_ptr;
+ }
+
+ T1& operator*() const
+ {
+ assert (m_ptr);
+ return *m_ptr;
+ }
+
+ bool operator== (const Shared_Ptr& rhs) const
+ {
+ return ( *m_ptr == *rhs.m_ptr);
+ }
+
+ bool operator!= (const Shared_Ptr& rhs) const
+ {
+ return ( *m_ptr != *rhs.m_ptr);
+ }
+
+ bool operator< (const Shared_Ptr& rhs) const
+ {
+ return ( *m_ptr < *rhs.m_ptr );
+ }
+
+ bool operator> (const Shared_Ptr& rhs) const
+ {
+ return ( *m_ptr > *rhs.m_ptr );
+ }
+
+ private:
+ void inc_ref()
+ {
+ if(m_count)
+ InterlockedIncrement(m_count);
+ }
+
+ int dec_ref()
+ {
+ assert (m_count);
+ return InterlockedDecrement(m_count);
+ }
+
+ T1* m_ptr;
+ long* m_count;
+ };
+
+}
+
+#endif // _WIN32XX_SHARED_PTR_
diff --git a/mmc_updater/depends/win32cpp/socket.h b/mmc_updater/depends/win32cpp/socket.h
new file mode 100644
index 00000000..63a7770f
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/socket.h
@@ -0,0 +1,778 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// socket.h
+// Declaration of the CSocket class
+//
+// The CSocket class represents a network socket. It encapsualtes many of
+// the Windows Socket SPI fuctions, providing an object-oriented approach
+// to network programming. After StartEvents is called, CSocket monitors
+// the socket and responds automatically to network events. This event
+// monitoring, for example, automatically calls OnReceive when there is
+// data on the socket to be read, and OnAccept when a server should accept
+// a connection from a client.
+
+// Users of this class should be aware that functions like OnReceive,
+// OnAccept, etc. are called on a different thread from the one CSocket is
+// instanciated on. The thread for these functions needs to respond quickly
+// to other network events, so it shouldn't be delayed. It also doesn't run
+// a message loop, so it can't be used to create windows. For these reasons
+// it might be best to use PostMessage in response to these functions in a
+// windows environment.
+
+// Refer to the network samples for an example of how to use this class to
+// create a TCP client & server, and a UDP client and server.
+
+// To compile programs with CSocket, link with ws3_32.lib for Win32,
+// and ws2.lib for Windows CE. Windows 95 systems will need to install the
+// "Windows Sockets 2.0 for Windows 95". It's available from:
+// http://support.microsoft.com/kb/182108/EN-US/
+
+// For a TCP server, inherit a class from CSocket and override OnAccept, OnDisconnect
+// and OnRecieve. Create one instance of this class and use it as a listening socket.
+// The purpose of the listening socket is to detect connections from clients and accept them.
+// For the listening socket, we do the following:
+// 1) Create the socket.
+// 2) Bind an IP address to the socket.
+// 3) Listen on the socket for incoming connection requests.
+// 4) Use StartNotifyRevents to receive notification of network events.
+// 5) Override OnAccept to accept requests on a newly created data CSocket object.
+// 6) Create a new data socket for each client connection accepted.
+// 7) The server socket uses the 'accept' function to accept an incoming connection
+// from this new data socket.
+
+// The purpose of the data socket is to send data to, and recieve data from the client.
+// There will be one data socket for each client accepted by the server.
+// To use it we do the following:
+// * To recieve data from the client, override OnReceive and use Receive.
+// * To send data to use Send.
+// * OnDisconnect can be used to detect when the client is disconnected.
+
+// For a TCP client, inherit from CSocket and override OnReceive and OnDisconnect.
+// Create an instance of this inherited class, and perform the following steps:
+// 1) Create the socket.
+// 2) Connect to the server.
+// 3) Use StartNotifyRevents to receive notification of network events.
+// We are now ready to send and recieve data from the server.
+// * Use Send to send data to the server.
+// * Override OnReceive and use Recieve to receive data from the server
+// * OnDisconnect can be used to detect when the client is disconnected from the server.
+
+// Notes regarding IPv6 support
+// * IPv6 is supported on Windows Vista and above. Windows XP with SP2 provides
+// "experimental" support, which can be enabled by entering "ipv6 install"
+// at a command prompt.
+// * IPv6 is not supported by all compilters and devlopment environments. In
+// particular, it is not supported by Dev-C++ or Borland 5.5. A modern
+// Platform SDK needs to be added to Visual Studio 6 for it to support IPv6.
+// * IsIPV6Supported returns false if either the operating system or the
+// development environment fails to support IPv6.
+//
+
+#ifndef _WIN32XX_SOCKET_H_
+#define _WIN32XX_SOCKET_H_
+
+
+#include "wincore.h"
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <process.h>
+
+
+#define THREAD_TIMEOUT 100
+
+
+namespace Win32xx
+{
+
+ typedef int WINAPI GETADDRINFO(LPCSTR, LPCSTR, const struct addrinfo*, struct addrinfo**);
+ typedef void WINAPI FREEADDRINFO(struct addrinfo*);
+
+ class CSocket
+ {
+ public:
+ CSocket();
+ virtual ~CSocket();
+
+ // Operations
+ virtual void Accept(CSocket& rClientSock, struct sockaddr* addr, int* addrlen);
+ virtual int Bind(LPCTSTR addr, LPCTSTR port);
+ virtual int Bind(const struct sockaddr* name, int namelen);
+ virtual int Connect(LPCTSTR addr, LPCTSTR port);
+ virtual int Connect(const struct sockaddr* name, int namelen);
+ virtual BOOL Create( int family, int type, int protocol = IPPROTO_IP);
+ virtual void Disconnect();
+ virtual void FreeAddrInfo( struct addrinfo* ai );
+ virtual int GetAddrInfo( LPCTSTR nodename, LPCTSTR servname, const struct addrinfo* hints, struct addrinfo** res);
+ virtual LPCTSTR GetLastError();
+ virtual int ioCtlSocket(long cmd, u_long* argp);
+ virtual BOOL IsIPV6Supported();
+ virtual int Listen(int backlog = SOMAXCONN);
+ virtual int Receive(TCHAR* buf, int len, int flags);
+ virtual int ReceiveFrom(TCHAR* buf, int len, int flags, struct sockaddr* from, int* fromlen);
+ virtual int Send(LPCTSTR buf, int len, int flags);
+ virtual int SendTo(LPCTSTR send, int len, int flags, LPCTSTR addr, LPCTSTR port);
+ virtual int SendTo(LPCTSTR buf, int len, int flags, const struct sockaddr* to, int tolen);
+
+ virtual void StartEvents();
+ virtual void StopEvents();
+
+ // Attributes
+ virtual int GetPeerName(struct sockaddr* name, int* namelen);
+ virtual int GetSockName(struct sockaddr* name, int* namelen);
+ SOCKET& GetSocket() { return m_Socket; }
+ virtual int GetSockOpt(int level, int optname, char* optval, int* optlen);
+ virtual int SetSockOpt(int level, int optname, const char* optval, int optlen);
+
+ // Override these functions to monitor events
+ virtual void OnAccept() {}
+ virtual void OnAddresListChange() {}
+ virtual void OnDisconnect() {}
+ virtual void OnConnect() {}
+ virtual void OnOutOfBand() {}
+ virtual void OnQualityOfService() {}
+ virtual void OnReceive() {}
+ virtual void OnRoutingChange() {}
+ virtual void OnSend() {}
+
+
+
+ // Allow CSocket to be used as a SOCKET
+ operator SOCKET() const {return m_Socket;}
+
+ private:
+ CSocket(const CSocket&); // Disable copy construction
+ CSocket& operator = (const CSocket&); // Disable assignment operator
+ static UINT WINAPI EventThread(LPVOID thread_data);
+
+ tString m_tsErrorMessage;
+ SOCKET m_Socket;
+ HMODULE m_hWS2_32;
+ HANDLE m_hEventThread; // Handle to the thread
+ HANDLE m_StopRequest; // An event to signal the event thread should stop
+ HANDLE m_Stopped; // An event to signal the event thread is stopped
+
+ GETADDRINFO* m_pfnGetAddrInfo; // pointer for the GetAddrInfo function
+ FREEADDRINFO* m_pfnFreeAddrInfo; // pointer for the FreeAddrInfo function
+ };
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+
+ inline CSocket::CSocket() : m_Socket(INVALID_SOCKET), m_hEventThread(0)
+ {
+ // Initialise the Windows Socket services
+ WSADATA wsaData;
+
+ if (0 != ::WSAStartup(MAKEWORD(2,2), &wsaData))
+ throw CWinException(_T("WSAStartup failed"));
+
+ m_hWS2_32 = ::LoadLibrary(_T("WS2_32.dll"));
+ if (0 == m_hWS2_32)
+ throw CWinException(_T("Failed to load WS2_2.dll"));
+
+ m_pfnGetAddrInfo = (GETADDRINFO*) GetProcAddress(m_hWS2_32, "getaddrinfo");
+ m_pfnFreeAddrInfo = (FREEADDRINFO*) GetProcAddress(m_hWS2_32, "freeaddrinfo");
+
+ m_StopRequest = ::CreateEvent(0, TRUE, FALSE, 0);
+ m_Stopped = ::CreateEvent(0, TRUE, FALSE, 0);
+ }
+
+ inline CSocket::~CSocket()
+ {
+ Disconnect();
+
+ // Close handles
+ ::CloseHandle(m_StopRequest);
+ ::CloseHandle(m_Stopped);
+
+ // Terminate the Windows Socket services
+ ::WSACleanup();
+
+ ::FreeLibrary(m_hWS2_32);
+ }
+
+ inline void CSocket::Accept(CSocket& rClientSock, struct sockaddr* addr, int* addrlen)
+ {
+ // The accept function permits an incoming connection attempt on the socket.
+
+ rClientSock.m_Socket = ::accept(m_Socket, addr, addrlen);
+ if (INVALID_SOCKET == rClientSock.GetSocket())
+ TRACE(_T("Accept failed\n"));
+ }
+
+ inline int CSocket::Bind(LPCTSTR addr, LPCTSTR port)
+ // The bind function associates a local address with the socket.
+ {
+ int RetVal = 0;
+
+ if (IsIPV6Supported())
+ {
+
+#ifdef GetAddrInfo // Skip the following code block for older development environments
+
+ ADDRINFO Hints= {0};
+ Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+ ADDRINFO *AddrInfo;
+
+ RetVal = GetAddrInfo(addr, port, &Hints, &AddrInfo);
+ if (RetVal != 0)
+ {
+ TRACE( _T("GetAddrInfo failed\n"));
+ return RetVal;
+ }
+
+ // Bind the IP address to the listening socket
+ RetVal = ::bind( m_Socket, AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen );
+ if ( RetVal == SOCKET_ERROR )
+ {
+ TRACE(_T("Bind failed\n"));
+ return RetVal;
+ }
+
+ // Free the address information allocated by GetAddrInfo
+ FreeAddrInfo(AddrInfo);
+
+#endif
+
+ }
+ else
+ {
+ sockaddr_in clientService;
+ clientService.sin_family = AF_INET;
+ clientService.sin_addr.s_addr = inet_addr( T2A(addr) );
+ int nPort = -1;
+ nPort = atoi( T2A(port) );
+ if (-1 == nPort)
+ {
+ TRACE(_T("Invalid port number\n"));
+ return SOCKET_ERROR;
+ }
+ clientService.sin_port = htons( (u_short)nPort );
+
+ RetVal = ::bind( m_Socket, (SOCKADDR*) &clientService, sizeof(clientService) );
+ if ( 0 != RetVal )
+ TRACE(_T("Bind failed\n"));
+ }
+
+ return RetVal;
+ }
+
+ inline int CSocket::Bind(const struct sockaddr* name, int namelen)
+ {
+ // The bind function associates a local address with the socket.
+
+ int Result = ::bind (m_Socket, name, namelen);
+ if ( 0 != Result )
+ TRACE(_T("Bind failed\n"));
+ return Result;
+ }
+
+ inline int CSocket::Connect(LPCTSTR addr, LPCTSTR port)
+ // The Connect function establishes a connection to the socket.
+ {
+ int RetVal = 0;
+
+ if (IsIPV6Supported())
+ {
+
+#ifdef GetAddrInfo // Skip the following code block for older development environments
+
+ ADDRINFO Hints= {0};
+ Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+ ADDRINFO *AddrInfo;
+
+ RetVal = GetAddrInfo(addr, port, &Hints, &AddrInfo);
+ if (RetVal != 0)
+ {
+ TRACE( _T("getaddrinfo failed\n"));
+ return SOCKET_ERROR;
+ }
+
+ // Bind the IP address to the listening socket
+ RetVal = Connect( AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen );
+ if ( RetVal == SOCKET_ERROR )
+ {
+ TRACE(_T("Connect failed\n"));
+ return RetVal;
+ }
+
+ // Free the address information allocatied by GetAddrInfo
+ FreeAddrInfo(AddrInfo);
+
+#endif
+
+ }
+ else
+ {
+ sockaddr_in clientService;
+ clientService.sin_family = AF_INET;
+ clientService.sin_addr.s_addr = inet_addr( T2A(addr) );
+ int nPort = -1;
+ nPort = atoi( T2A(port) );
+ if (-1 == nPort)
+ {
+ TRACE(_T("Invalid port number\n"));
+ return SOCKET_ERROR;
+ }
+ clientService.sin_port = htons( (u_short)nPort );
+
+ RetVal = ::connect( m_Socket, (SOCKADDR*) &clientService, sizeof(clientService) );
+ if ( 0 != RetVal )
+ TRACE(_T("Connect failed\n"));
+ }
+
+ return RetVal;
+ }
+
+ inline int CSocket::Connect(const struct sockaddr* name, int namelen)
+ {
+ // The Connect function establishes a connection to the socket.
+
+ int Result = ::connect( m_Socket, name, namelen );
+ if ( 0 != Result )
+ TRACE(_T("Connect failed\n"));
+
+ return Result;
+ }
+
+ inline BOOL CSocket::Create( int family, int type, int protocol /*= IPPROTO_IP*/)
+ {
+ // Creates the socket
+
+ // Valid values:
+ // family: AF_INET or AF_INET6
+ // type: SOCK_DGRAM, SOCK_SEQPACKET, SOCK_STREAM, SOCK_RAW
+ // protocol: IPPROTO_IP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6
+
+ m_Socket = socket(family, type, protocol);
+ if(m_Socket == INVALID_SOCKET)
+ {
+ TRACE(_T("Failed to create socket\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ inline void CSocket::Disconnect()
+ {
+ ::shutdown(m_Socket, SD_BOTH);
+ StopEvents();
+ ::closesocket(m_Socket);
+ m_Socket = INVALID_SOCKET;
+ }
+
+ inline UINT WINAPI CSocket::EventThread(LPVOID thread_data)
+ {
+ // These are the possible network event notifications:
+ // FD_READ Notification of readiness for reading.
+ // FD_WRITE Motification of readiness for writing.
+ // FD_OOB Notification of the arrival of Out Of Band data.
+ // FD_ACCEPT Notification of incoming connections.
+ // FD_CONNECT Notification of completed connection or multipoint join operation.
+ // FD_CLOSE Notification of socket closure.
+ // FD_QOS Notification of socket Quality Of Service changes
+ // FD_ROUTING_INTERFACE_CHANGE Notification of routing interface changes for the specified destination.
+ // FD_ADDRESS_LIST_CHANGE Notification of local address list changes for the address family of the socket.
+
+ WSANETWORKEVENTS NetworkEvents;
+ CSocket* pSocket = (CSocket*)thread_data;
+ SOCKET sClient = pSocket->m_Socket;
+
+ WSAEVENT AllEvents[2];
+ AllEvents[0] = ::WSACreateEvent();
+ AllEvents[1] = (WSAEVENT)pSocket->m_StopRequest;
+ long Events = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE |
+ FD_QOS | FD_ROUTING_INTERFACE_CHANGE | FD_ADDRESS_LIST_CHANGE;
+
+ // Associate the network event object (hNetworkEvents) with the
+ // specified network events (Events) on socket sClient.
+ if( SOCKET_ERROR == WSAEventSelect(sClient, AllEvents[0], Events))
+ {
+ TRACE(_T("Error in Event Select\n"));
+ ::SetEvent(pSocket->m_Stopped);
+ ::WSACloseEvent(AllEvents[0]);
+ return 0;
+ }
+
+ // loop until the stop event is set
+ for (;;) // infinite loop
+ {
+ // Wait 100 ms for a network event
+ DWORD dwResult = ::WSAWaitForMultipleEvents(2, AllEvents, FALSE, THREAD_TIMEOUT, FALSE);
+
+ // Check event for stop thread
+ if(::WaitForSingleObject(pSocket->m_StopRequest, 0) == WAIT_OBJECT_0)
+ {
+ ::WSACloseEvent(AllEvents[0]);
+ ::SetEvent(pSocket->m_Stopped);
+ return 0;
+ }
+
+ if (WSA_WAIT_FAILED == dwResult)
+ {
+ TRACE(_T("WSAWaitForMultipleEvents failed\n"));
+ ::WSACloseEvent(AllEvents[0]);
+ ::SetEvent(pSocket->m_Stopped);
+ return 0;
+ }
+
+ // Proceed if a network event occurred
+ if (WSA_WAIT_TIMEOUT != dwResult)
+ {
+
+ if ( SOCKET_ERROR == ::WSAEnumNetworkEvents(sClient, AllEvents[0], &NetworkEvents) )
+ {
+ TRACE(_T("WSAEnumNetworkEvents failed\n"));
+ ::WSACloseEvent(AllEvents[0]);
+ ::SetEvent(pSocket->m_Stopped);
+ return 0;
+ }
+
+ if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
+ pSocket->OnAccept();
+
+ if (NetworkEvents.lNetworkEvents & FD_READ)
+ pSocket->OnReceive();
+
+ if (NetworkEvents.lNetworkEvents & FD_WRITE)
+ pSocket->OnSend();
+
+ if (NetworkEvents.lNetworkEvents & FD_OOB)
+ pSocket->OnOutOfBand();
+
+ if (NetworkEvents.lNetworkEvents & FD_QOS)
+ pSocket->OnQualityOfService();
+
+ if (NetworkEvents.lNetworkEvents & FD_CONNECT)
+ pSocket->OnConnect();
+
+ if (NetworkEvents.lNetworkEvents & FD_ROUTING_INTERFACE_CHANGE)
+ pSocket->OnRoutingChange();
+
+ if (NetworkEvents.lNetworkEvents & FD_ADDRESS_LIST_CHANGE)
+ pSocket->OnAddresListChange();
+
+ if (NetworkEvents.lNetworkEvents & FD_CLOSE)
+ {
+ ::shutdown(sClient, SD_BOTH);
+ ::closesocket(sClient);
+ pSocket->OnDisconnect();
+ ::WSACloseEvent(AllEvents[0]);
+ ::SetEvent(pSocket->m_Stopped);
+ return 0;
+ }
+ }
+ }
+ }
+
+ inline int CSocket::GetAddrInfo( LPCTSTR nodename, LPCTSTR servname, const struct addrinfo* hints, struct addrinfo** res)
+ {
+
+#ifdef GetAddrInfo
+
+ std::string sNodeName = T2A(nodename);
+ std::string sServName = T2A(servname);
+ return (*m_pfnGetAddrInfo)(sNodeName.c_str(), sServName.c_str(), hints, res);
+
+#else
+
+ UNREFERENCED_PARAMETER(nodename);
+ UNREFERENCED_PARAMETER(servname);
+ UNREFERENCED_PARAMETER(hints);
+ UNREFERENCED_PARAMETER(res);
+
+ throw CWinException(_T("getaddrinfo is not supported"));
+
+#endif
+
+ }
+
+ inline LPCTSTR CSocket::GetLastError()
+ {
+ // Retrieves the most recent network error.
+
+ int ErrorCode = WSAGetLastError();
+ LPTSTR Message = NULL;
+ m_tsErrorMessage = _T("");
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&Message, 1024, NULL);
+
+ if (Message)
+ {
+ m_tsErrorMessage = Message;
+ ::LocalFree(Message);
+ }
+
+ return m_tsErrorMessage.c_str();
+ }
+
+ inline int CSocket::GetPeerName(struct sockaddr* name, int* namelen)
+ {
+ int Result = ::getpeername(m_Socket, name, namelen);
+ if (0 != Result)
+ TRACE(_T("GetPeerName failed\n"));
+
+ return Result;
+ }
+
+ inline int CSocket::GetSockName(struct sockaddr* name, int* namelen)
+ {
+ int Result = ::getsockname(m_Socket, name, namelen);
+ if (0 != Result)
+ TRACE(_T("GetSockName Failed\n"));
+
+ return Result;
+ }
+
+ inline int CSocket::GetSockOpt(int level, int optname, char* optval, int* optlen)
+ {
+ int Result = ::getsockopt(m_Socket, level, optname, optval, optlen);
+ if (0 != Result)
+ TRACE(_T("GetSockOpt Failed\n"));
+
+ return Result;
+ }
+
+ inline void CSocket::FreeAddrInfo( struct addrinfo* ai )
+ {
+
+#ifdef GetAddrInfo
+
+ (*m_pfnFreeAddrInfo)(ai);
+
+#else
+
+ UNREFERENCED_PARAMETER(ai);
+
+ throw CWinException(_T("getaddrinfo is not supported"));
+
+#endif
+
+ }
+
+ inline int CSocket::ioCtlSocket(long cmd, u_long* argp)
+ {
+ int Result = ::ioctlsocket(m_Socket, cmd, argp);
+ if (0 != Result)
+ TRACE(_T("ioCtlSocket Failed\n"));
+
+ return Result;
+ }
+
+ inline BOOL CSocket::IsIPV6Supported()
+ {
+ BOOL IsIPV6Supported = FALSE;
+
+#ifdef GetAddrInfo
+
+ if (m_pfnGetAddrInfo != 0 && m_pfnFreeAddrInfo != 0)
+ IsIPV6Supported = TRUE;
+
+#endif
+
+ return IsIPV6Supported;
+ }
+
+ inline int CSocket::Listen(int backlog /*= SOMAXCONN*/)
+ {
+ int Result = ::listen(m_Socket, backlog);
+ if (0 != Result)
+ TRACE(_T("Listen Failed\n"));
+
+ return Result;
+ }
+
+ inline int CSocket::Receive(TCHAR* buf, int len, int flags)
+ {
+ std::vector<char> vChar(len+1, '\0');
+ char* pCharArray = &vChar.front();
+ int Result = ::recv(m_Socket, pCharArray, len, flags);
+ if (SOCKET_ERROR == Result)
+ TRACE(_T("Receive failed\n"));
+
+ lstrcpyn(buf, A2T(pCharArray), len);
+
+ return Result;
+ }
+
+ inline int CSocket::ReceiveFrom(TCHAR* buf, int len, int flags, struct sockaddr* from, int* fromlen)
+ //The ReceiveFrom function receives a datagram and stores the source address.
+ {
+ std::vector<char> vChar(len+1, '\0');
+ char* pCharArray = &vChar.front();
+ int Result = ::recvfrom(m_Socket, pCharArray, len, flags, from, fromlen);
+ if (SOCKET_ERROR == Result)
+ TRACE(_T("ReceiveFrom failed\n"));
+
+ lstrcpyn(buf, A2T(pCharArray), len);
+
+ return Result;
+ }
+
+ inline int CSocket::Send(LPCTSTR buf, int len, int flags)
+ {
+ int Result = ::send(m_Socket, T2A(buf), len, flags);
+ if (SOCKET_ERROR == Result)
+ TRACE(_T("Send failed\n"));
+
+ return Result;
+ }
+
+ inline int CSocket::SendTo(LPCTSTR send, int len, int flags, LPCTSTR addr, LPCTSTR port)
+ // The sendto function sends data to a specific destination.
+ {
+ int RetVal = 0;
+
+ if (IsIPV6Supported())
+ {
+
+#ifdef GetAddrInfo // Skip the following code block for older development environments
+
+ ADDRINFO Hints= {0};
+ Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+ ADDRINFO *AddrInfo;
+
+ RetVal = GetAddrInfo(addr, port, &Hints, &AddrInfo);
+ if (RetVal != 0)
+ {
+ TRACE( _T("GetAddrInfo failed\n"));
+ return SOCKET_ERROR;
+ }
+
+ RetVal = ::sendto(m_Socket, T2A(send), len, flags, AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen );
+ if ( RetVal == SOCKET_ERROR )
+ {
+ TRACE(_T("SendTo failed\n"));
+ return RetVal;
+ }
+
+ // Free the address information allocatied by GetAddrInfo
+ FreeAddrInfo(AddrInfo);
+
+#endif
+
+ }
+ else
+ {
+ sockaddr_in clientService;
+ clientService.sin_family = AF_INET;
+ clientService.sin_addr.s_addr = inet_addr( T2A(addr) );
+ int nPort = -1;
+ nPort = atoi( T2A(port));
+ if (-1 == nPort)
+ {
+ TRACE(_T("Invalid port number\n"));
+ return SOCKET_ERROR;
+ }
+ clientService.sin_port = htons( (u_short)nPort );
+
+ RetVal = ::sendto( m_Socket, T2A(send), len, flags, (SOCKADDR*) &clientService, sizeof(clientService) );
+ if ( SOCKET_ERROR != RetVal )
+ TRACE(_T("SendTo failed\n"));
+ }
+
+ return RetVal;
+ }
+
+ inline int CSocket::SendTo(LPCTSTR buf, int len, int flags, const struct sockaddr* to, int tolen)
+ // The sendto function sends data to a specific destination.
+ {
+ int Result = ::sendto(m_Socket, T2A(buf), len, flags, to, tolen);
+ if (SOCKET_ERROR == Result)
+ TRACE(_T("SendTo failed\n"));
+
+ return Result;
+ }
+
+ inline int CSocket::SetSockOpt(int level, int optname, const char* optval, int optlen)
+ {
+ int Result = ::setsockopt(m_Socket, level, optname, optval, optlen);
+ if (0 != Result)
+ TRACE(_T("SetSockOpt failed\n"));
+
+ return Result;
+ }
+
+ inline void CSocket::StartEvents()
+ {
+ // This function starts the thread which monitors the socket for events.
+ StopEvents(); // Ensure the thread isn't already running
+ UINT ThreadID; // a return variable required for Win95, Win98, WinME
+ m_hEventThread = (HANDLE)::_beginthreadex(NULL, 0, CSocket::EventThread, (LPVOID) this, 0, &ThreadID);
+ }
+
+ inline void CSocket::StopEvents()
+ {
+ // Terminates the event thread gracefully (if possible)
+ if (m_hEventThread)
+ {
+ ::SetThreadPriority(m_hEventThread, THREAD_PRIORITY_HIGHEST);
+ ::SetEvent(m_StopRequest);
+
+ for (;;) // infinite loop
+ {
+ // wait for the Thread stopping event to be set
+ if ( WAIT_TIMEOUT == ::WaitForSingleObject(m_Stopped, THREAD_TIMEOUT * 10) )
+ {
+ // Note: An excessive delay in processing any of the notification functions
+ // can cause us to get here. (Yes one second is an excessive delay. Its a bug!)
+ TRACE(_T("*** Error: Event Thread won't die ***\n") );
+ }
+ else break;
+ }
+
+ ::CloseHandle(m_hEventThread);
+ m_hEventThread = 0;
+ }
+
+ ::ResetEvent(m_StopRequest);
+ ::ResetEvent(m_Stopped);
+ }
+}
+
+
+#endif // #ifndef _WIN32XX_SOCKET_H_
+
diff --git a/mmc_updater/depends/win32cpp/statusbar.h b/mmc_updater/depends/win32cpp/statusbar.h
new file mode 100644
index 00000000..ad9a007b
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/statusbar.h
@@ -0,0 +1,226 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+#ifndef _WIN32XX_STATUSBAR_H_
+#define _WIN32XX_STATUSBAR_H_
+
+#include "wincore.h"
+
+namespace Win32xx
+{
+
+ //////////////////////////////////////
+ // Declaration of the CStatusBar class
+ //
+ class CStatusBar : public CWnd
+ {
+ public:
+ CStatusBar();
+ virtual ~CStatusBar() {}
+
+ // Overridables
+ virtual void PreCreate(CREATESTRUCT& cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+
+ // Attributes
+ int GetParts();
+ HICON GetPartIcon(int iPart);
+ CRect GetPartRect(int iPart);
+ tString GetPartText(int iPart) const;
+ BOOL IsSimple();
+ BOOL SetPartIcon(int iPart, HICON hIcon);
+ BOOL SetPartText(int iPart, LPCTSTR szText, UINT Style = 0) const;
+ BOOL SetPartWidth(int iPart, int iWidth) const;
+
+ // Operations
+ CStatusBar(const CStatusBar&); // Disable copy construction
+ CStatusBar& operator = (const CStatusBar&); // Disable assignment operator
+
+ BOOL CreateParts(int iParts, const int iPaneWidths[]) const;
+ void SetSimple(BOOL fSimple = TRUE);
+ };
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ //////////////////////////////////////
+ // Definitions for the CStatusBar class
+ //
+ inline CStatusBar::CStatusBar()
+ {
+ }
+
+ inline BOOL CStatusBar::CreateParts(int iParts, const int iPaneWidths[]) const
+ // Sets the number of parts in a status window and the coordinate of the right edge of each part.
+ // If an element of iPaneWidths is -1, the right edge of the corresponding part extends
+ // to the border of the window
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(iParts <= 256);
+
+ return (BOOL)SendMessage(SB_SETPARTS, iParts, (LPARAM)iPaneWidths);
+ }
+
+ inline int CStatusBar::GetParts()
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(SB_GETPARTS, 0L, 0L);
+ }
+
+ inline HICON CStatusBar::GetPartIcon(int iPart)
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage(SB_GETICON, (WPARAM)iPart, 0L);
+ }
+
+ inline CRect CStatusBar::GetPartRect(int iPart)
+ {
+ assert(::IsWindow(m_hWnd));
+
+ CRect rc;
+ SendMessage(SB_GETRECT, (WPARAM)iPart, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline tString CStatusBar::GetPartText(int iPart) const
+ {
+ assert(::IsWindow(m_hWnd));
+ tString PaneText;
+
+ // Get size of Text array
+ int iChars = LOWORD (SendMessage(SB_GETTEXTLENGTH, iPart, 0L));
+
+ std::vector<TCHAR> Text( iChars +1, _T('\0') );
+ TCHAR* pTextArray = &Text[0];
+
+ SendMessage(SB_GETTEXT, iPart, (LPARAM)pTextArray);
+ PaneText = pTextArray;
+ return PaneText;
+ }
+
+ inline BOOL CStatusBar::IsSimple()
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(SB_ISSIMPLE, 0L, 0L);
+ }
+
+ inline void CStatusBar::PreCreate(CREATESTRUCT &cs)
+ {
+ cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | CCS_BOTTOM | SBARS_SIZEGRIP;
+ }
+
+ inline void CStatusBar::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = STATUSCLASSNAME;
+ }
+
+ inline BOOL CStatusBar::SetPartText(int iPart, LPCTSTR szText, UINT Style) const
+ // Available Styles: Combinations of ...
+ //0 The text is drawn with a border to appear lower than the plane of the window.
+ //SBT_NOBORDERS The text is drawn without borders.
+ //SBT_OWNERDRAW The text is drawn by the parent window.
+ //SBT_POPOUT The text is drawn with a border to appear higher than the plane of the window.
+ //SBT_RTLREADING The text will be displayed in the opposite direction to the text in the parent window.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ BOOL bResult = FALSE;
+ if (SendMessage(SB_GETPARTS, 0L, 0L) >= iPart)
+ bResult = (BOOL)SendMessage(SB_SETTEXT, iPart | Style, (LPARAM)szText);
+
+ return bResult;
+ }
+
+ inline BOOL CStatusBar::SetPartIcon(int iPart, HICON hIcon)
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(SB_SETICON, (WPARAM)iPart, (LPARAM) hIcon);
+ }
+
+ inline BOOL CStatusBar::SetPartWidth(int iPart, int iWidth) const
+ {
+ // This changes the width of an existing pane, or creates a new pane
+ // with the specified width.
+ // A width of -1 for the last part sets the width to the border of the window.
+
+ assert(::IsWindow(m_hWnd));
+ assert(iPart >= 0 && iPart <= 255);
+
+ // Fill the PartWidths vector with the current width of the statusbar parts
+ int PartsCount = (int)SendMessage(SB_GETPARTS, 0L, 0L);
+ std::vector<int> PartWidths(PartsCount, 0);
+ int* pPartWidthArray = &PartWidths[0];
+ SendMessage(SB_GETPARTS, PartsCount, (LPARAM)pPartWidthArray);
+
+ // Fill the NewPartWidths vector with the new width of the statusbar parts
+ int NewPartsCount = MAX(iPart+1, PartsCount);
+ std::vector<int> NewPartWidths(NewPartsCount, 0);;
+ NewPartWidths = PartWidths;
+ int* pNewPartWidthArray = &NewPartWidths[0];
+
+ if (0 == iPart)
+ pNewPartWidthArray[iPart] = iWidth;
+ else
+ {
+ if (iWidth >= 0)
+ pNewPartWidthArray[iPart] = pNewPartWidthArray[iPart -1] + iWidth;
+ else
+ pNewPartWidthArray[iPart] = -1;
+ }
+
+ // Set the statusbar parts with our new parts count and part widths
+ BOOL bResult = (BOOL)SendMessage(SB_SETPARTS, NewPartsCount, (LPARAM)pNewPartWidthArray);
+
+ return bResult;
+ }
+
+ inline void CStatusBar::SetSimple(BOOL fSimple /* = TRUE*/)
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(SB_SIMPLE, (WPARAM)fSimple, 0L);
+ }
+
+} // namespace Win32xx
+
+#endif // #ifndef _WIN32XX_STATUSBAR_H_
diff --git a/mmc_updater/depends/win32cpp/stdcontrols.h b/mmc_updater/depends/win32cpp/stdcontrols.h
new file mode 100644
index 00000000..b362f07b
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/stdcontrols.h
@@ -0,0 +1,1000 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// stdcontrols.h
+// Declaration of the CButton, CEdit, CListBox and CStatic classes
+
+// The Button, Edit, ListBox and Static controls are often referred to
+// as "standard controls". These set of older controls were originally
+// developed for Win16 operating systems (Windows 3.1 and 3.11). They use an
+// older form of notification, and send their notifications via a WM_COMMAND
+// message. Newer controls send their notifications via a WM_NOTIFY message.
+
+
+#ifndef _WIN32XX_STDCONTROLS_H_
+#define _WIN32XX_STDCONTROLS_H_
+
+#include "wincore.h"
+
+
+namespace Win32xx
+{
+ class CButton : public CWnd
+ {
+ public:
+ CButton() {}
+ virtual ~CButton() {}
+
+ // Attributes
+ HBITMAP GetBitmap() const;
+ UINT GetButtonStyle() const;
+ int GetCheck() const;
+ HCURSOR GetCursor() const;
+ HICON GetIcon() const;
+ UINT GetState() const;
+ HBITMAP SetBitmap(HBITMAP hBitmap) const;
+ void SetButtonStyle(DWORD dwStyle, BOOL bRedraw) const;
+ void SetCheck(int nCheckState) const;
+ HCURSOR SetCursor(HCURSOR hCursor) const;
+ HICON SetIcon(HICON hIcon) const;
+ void SetState(BOOL bHighlight) const;
+
+ protected:
+ // Overridables
+ virtual void PreCreate(CREATESTRUCT& cs);
+ };
+
+ class CEdit : public CWnd
+ {
+ public:
+ // Construction
+ CEdit() {}
+ virtual ~CEdit() {}
+
+ // Attributes
+ BOOL CanUndo() const;
+ int CharFromPos(CPoint pt) const;
+ int GetFirstVisibleLine() const;
+ HLOCAL GetHandle() const;
+ UINT GetLimitText() const;
+ int GetLine(int nIndex, LPTSTR lpszBuffer) const;
+ int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const;
+ int GetLineCount() const;
+ DWORD GetMargins() const;
+ BOOL GetModify() const;
+ TCHAR GetPasswordChar() const;
+ void GetRect(LPRECT lpRect) const;
+ void GetSel(int& nStartChar, int& nEndChar) const;
+ DWORD GetSel() const;
+ CPoint PosFromChar(UINT nChar) const;
+ void SetHandle(HLOCAL hBuffer) const;
+ void SetLimitText(UINT nMax) const;
+ void SetMargins(UINT nLeft, UINT nRight) const;
+ void SetModify(BOOL bModified = TRUE) const;
+
+ // Operations
+ void EmptyUndoBuffer() const;
+ BOOL FmtLines(BOOL bAddEOL) const;
+ void LimitText(int nChars = 0) const;
+ int LineFromChar(int nIndex = -1) const;
+ int LineIndex(int nLine = -1) const;
+ int LineLength(int nLine = -1) const;
+ void LineScroll(int nLines, int nChars = 0) const;
+ void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo) const;
+ void SetPasswordChar(TCHAR ch) const;
+ BOOL SetReadOnly(BOOL bReadOnly = TRUE) const;
+ void SetRect(LPCRECT lpRect) const;
+ void SetRectNP(LPCRECT lpRect) const;
+ void SetSel(DWORD dwSelection, BOOL bNoScroll) const;
+ void SetSel(int nStartChar, int nEndChar, BOOL bNoScroll) const;
+ BOOL SetTabStops(int nTabStops, LPINT rgTabStops) const;
+ BOOL SetTabStops() const;
+ BOOL SetTabStops(const int& cxEachStop) const;
+
+ //Clipboard Operations
+ void Clear() const;
+ void Copy() const;
+ void Cut() const;
+ void Paste() const;
+ void Undo() const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ };
+
+ class CListBox : public CWnd
+ {
+ public:
+ CListBox() {}
+ virtual ~CListBox() {}
+
+ // General Operations
+ int GetCount() const;
+ int GetHorizontalExtent() const;
+ DWORD GetItemData(int nIndex) const;
+ void* GetItemDataPtr(int nIndex) const;
+ int GetItemHeight(int nIndex) const;
+ int GetItemRect(int nIndex, LPRECT lpRect) const;
+ LCID GetLocale() const;
+ int GetSel(int nIndex) const;
+ int GetText(int nIndex, LPTSTR lpszBuffer) const;
+ int GetTextLen(int nIndex) const;
+ int GetTopIndex() const;
+ UINT ItemFromPoint(CPoint pt, BOOL& bOutside ) const;
+ void SetColumnWidth(int cxWidth) const;
+ void SetHorizontalExtent(int cxExtent) const;
+ int SetItemData(int nIndex, DWORD dwItemData) const;
+ int SetItemDataPtr(int nIndex, void* pData) const;
+ int SetItemHeight(int nIndex, UINT cyItemHeight) const;
+ LCID SetLocale(LCID nNewLocale) const;
+ BOOL SetTabStops(int nTabStops, LPINT rgTabStops) const;
+ void SetTabStops() const;
+ BOOL SetTabStops(const int& cxEachStop) const;
+ int SetTopIndex(int nIndex) const;
+
+ // Single-Selection Operations
+ int GetCurSel() const;
+ int SetCurSel(int nSelect) const;
+
+ // Multiple-Selection Operations
+ int GetAnchorIndex() const;
+ int GetCaretIndex() const;
+ int GetSelCount() const;
+ int GetSelItems(int nMaxItems, LPINT rgIndex) const;
+ int SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) const;
+ void SetAnchorIndex(int nIndex) const;
+ int SetCaretIndex(int nIndex, BOOL bScroll) const;
+ int SetSel(int nIndex, BOOL bSelect) const;
+
+ // String Operations
+ int AddString(LPCTSTR lpszItem) const;
+ int DeleteString(UINT nIndex) const;
+ int Dir(UINT attr, LPCTSTR lpszWildCard) const;
+ int FindString(int nStartAfter, LPCTSTR lpszItem) const;
+ int FindStringExact(int nIndexStart, LPCTSTR lpszFind) const;
+ int InsertString(int nIndex, LPCTSTR lpszItem) const;
+ void ResetContent() const;
+ int SelectString(int nStartAfter, LPCTSTR lpszItem) const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ };
+
+ class CStatic : public CWnd
+ {
+ public:
+ CStatic() {}
+ virtual ~CStatic() {}
+
+ // Operations
+ HBITMAP GetBitmap() const;
+ HCURSOR GetCursor() const;
+ HENHMETAFILE GetEnhMetaFile() const;
+ HICON GetIcon() const;
+ HBITMAP SetBitmap(HBITMAP hBitmap) const;
+ HCURSOR SetCursor(HCURSOR hCursor) const;
+ HENHMETAFILE SetEnhMetaFile(HENHMETAFILE hMetaFile) const;
+ HICON SetIcon(HICON hIcon) const;
+
+ protected:
+ // Overridables
+ virtual void PreRegisterClass(WNDCLASS &wc);
+
+ };
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ ////////////////////////////////////////
+ // Definitions for the CButton class
+ //
+ inline HBITMAP CButton::GetBitmap() const
+ // returns the handle to the bitmap associated with the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HBITMAP)SendMessage(BM_GETIMAGE, IMAGE_BITMAP, 0);
+ }
+
+ inline UINT CButton::GetButtonStyle() const
+ // returns the style of the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)GetWindowLongPtr(GWL_STYLE) & 0xff;
+ }
+
+ inline int CButton::GetCheck() const
+ // returns the check state of the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(BM_GETCHECK, 0, 0);
+ }
+
+ inline HCURSOR CButton::GetCursor() const
+ // returns the handle to the cursor associated withe the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HCURSOR)::SendMessage(m_hWnd, BM_GETIMAGE, IMAGE_CURSOR, 0L);
+ }
+
+ inline HICON CButton::GetIcon() const
+ // returns the handle to the icon associated withe the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage(BM_GETIMAGE, IMAGE_ICON, 0);
+ }
+
+ inline UINT CButton::GetState() const
+ // returns the state of the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)SendMessage(BM_GETSTATE, 0, 0);
+ }
+
+ inline HBITMAP CButton::SetBitmap(HBITMAP hBitmap) const
+ // sets the bitmap associated with the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HBITMAP)SendMessage(BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
+ }
+
+ inline void CButton::SetButtonStyle(DWORD dwStyle, BOOL bRedraw) const
+ // sets the button style
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(BM_SETSTYLE, dwStyle, bRedraw);
+ }
+
+ inline void CButton::SetCheck(int nCheckState) const
+ // sets the button check state
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(BM_SETCHECK, nCheckState, 0);
+ }
+
+ inline HCURSOR CButton::SetCursor(HCURSOR hCursor) const
+ // sets the cursor associated with the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HCURSOR)SendMessage(STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor);
+ }
+
+ inline HICON CButton::SetIcon(HICON hIcon) const
+ // sets the icon associated with the button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage( BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ }
+
+ inline void CButton::SetState(BOOL bHighlight) const
+ // sets the button state
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(BM_SETSTATE, bHighlight, 0);
+ }
+
+ inline void CButton::PreCreate(CREATESTRUCT& cs)
+ {
+ cs.lpszClass = _T("Button");
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CEdit class
+ //
+ inline BOOL CEdit::CanUndo() const
+ // Returns TRUE if the edit control operation can be undone.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(EM_CANUNDO, 0, 0);
+ }
+
+ inline int CEdit::CharFromPos(CPoint pt) const
+ // Returns the character index and line index of the character nearest the specified point.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y));
+ }
+
+ inline int CEdit::GetFirstVisibleLine() const
+ // Returns the zero-based index of the first visible character in a single-line edit control
+ // or the zero-based index of the uppermost visible line in a multiline edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(EM_GETFIRSTVISIBLELINE, 0, 0);
+ }
+
+ inline HLOCAL CEdit::GetHandle() const
+ // Returns a handle identifying the buffer containing the multiline edit control's text.
+ // It is not processed by single-line edit controls.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
+ }
+
+ inline UINT CEdit::GetLimitText() const
+ // Returns the current text limit, in characters.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)SendMessage(EM_GETLIMITTEXT, 0, 0);
+ }
+
+ inline int CEdit::GetLine(int nIndex, LPTSTR lpszBuffer) const
+ // Copies characters to a buffer and returns the number of characters copied.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);
+ }
+
+ inline int CEdit::GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const
+ // Copies characters to a buffer and returns the number of characters copied.
+ {
+ assert(::IsWindow(m_hWnd));
+ *(LPWORD)lpszBuffer = (WORD)nMaxLength;
+ return (int)SendMessage(EM_GETLINE, nIndex, (LPARAM)lpszBuffer);
+ }
+
+ inline int CEdit::GetLineCount() const
+ // Returns the number of lines in the edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(EM_GETLINECOUNT, 0, 0);
+ }
+
+ inline DWORD CEdit::GetMargins() const
+ // Returns the widths of the left and right margins.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(EM_GETMARGINS, 0, 0);
+ }
+
+ inline BOOL CEdit::GetModify() const
+ // Returns a flag indicating whether the content of an edit control has been modified.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(EM_GETMODIFY, 0, 0);
+ }
+
+ inline TCHAR CEdit::GetPasswordChar() const
+ // Returns the character that edit controls use in conjunction with the ES_PASSWORD style.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (TCHAR)SendMessage(EM_GETPASSWORDCHAR, 0, 0);
+ }
+
+ inline void CEdit::GetRect(LPRECT lpRect) const
+ // Returns the coordinates of the formatting rectangle in an edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_GETRECT, 0, (LPARAM)lpRect);
+ }
+
+ inline void CEdit::GetSel(int& nStartChar, int& nEndChar) const
+ // Returns the starting and ending character positions of the current selection in the edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_GETSEL, (WPARAM)&nStartChar,(LPARAM)&nEndChar);
+ }
+
+ inline DWORD CEdit::GetSel() const
+ // Returns the starting and ending character positions of the current selection in the edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(EM_GETSEL, 0, 0);
+ }
+
+ inline CPoint CEdit::PosFromChar(UINT nChar) const
+ // Returns the client coordinates of the specified character.
+ {
+ assert(::IsWindow(m_hWnd));
+ return CPoint( (DWORD)SendMessage(EM_POSFROMCHAR, nChar, 0));
+ }
+
+ inline void CEdit::SetHandle(HLOCAL hBuffer) const
+ // Sets a handle to the memory used as a text buffer, empties the undo buffer,
+ // resets the scroll positions to zero, and redraws the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETHANDLE, (WPARAM)hBuffer, 0);
+ }
+
+ inline void CEdit::SetLimitText(UINT nMax) const
+ // Sets the maximum number of characters the user may enter in the edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETLIMITTEXT, (WPARAM)nMax, 0);
+ }
+
+ inline void CEdit::SetMargins(UINT nLeft, UINT nRight) const
+ // Sets the widths of the left and right margins, and redraws the edit control to reflect the new margins.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(nLeft, nRight));
+ }
+
+ inline void CEdit::SetModify(BOOL bModified) const
+ // Sets or clears the modification flag to indicate whether the edit control has been modified.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETMODIFY, bModified, 0);
+ }
+
+ inline void CEdit::EmptyUndoBuffer() const
+ // Empties the undo buffer and sets the undo flag retrieved by the EM_CANUNDO message to FALSE.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_EMPTYUNDOBUFFER, 0, 0);
+ }
+
+ inline BOOL CEdit::FmtLines(BOOL bAddEOL) const
+ // Adds or removes soft line-break characters (two carriage returns and a line feed) to the ends of wrapped lines
+ // in a multiline edit control. It is not processed by single-line edit controls.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(EM_FMTLINES, bAddEOL, 0);
+ }
+
+ inline void CEdit::LimitText(int nChars) const
+ // Sets the text limit of an edit control. The text limit is the maximum amount of text, in TCHARs,
+ // that the user can type into the edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_LIMITTEXT, nChars, 0);
+ }
+
+ inline int CEdit::LineFromChar(int nIndex) const
+ // Returns the zero-based number of the line in a multiline edit control that contains a specified character index.
+ // This message is the reverse of the EM_LINEINDEX message.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(EM_LINEFROMCHAR, (WPARAM)nIndex, 0);
+ }
+
+ inline int CEdit::LineIndex(int nLine) const
+ // Returns the character of a line in a multiline edit control.
+ // This message is the reverse of the EM_LINEFROMCHAR message
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(EM_LINEINDEX, (WPARAM)nLine, 0);
+ }
+
+ inline int CEdit::LineLength(int nLine) const
+ // Returns the length, in characters, of a single-line edit control. In a multiline edit control,
+ // returns the length, in characters, of a specified line.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(EM_LINELENGTH, (WPARAM)nLine, 0);
+ }
+
+ inline void CEdit::LineScroll(int nLines, int nChars) const
+ // Scrolls the text vertically in a single-line edit control or horizontally in a multiline edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_LINESCROLL, (WPARAM)nChars, (LPARAM)nLines);
+ }
+
+ inline void CEdit::ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo) const
+ // Replaces the current selection with the text in an application-supplied buffer, sends the parent window
+ // EN_UPDATE and EN_CHANGE messages, and updates the undo buffer.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText);
+ }
+
+ inline void CEdit::SetPasswordChar(TCHAR ch) const
+ // Defines the character that edit controls use in conjunction with the ES_PASSWORD style.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETPASSWORDCHAR, ch, 0);
+ }
+
+ inline BOOL CEdit::SetReadOnly(BOOL bReadOnly) const
+ // Sets or removes the read-only style (ES_READONLY) in an edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(EM_SETREADONLY, bReadOnly, 0);
+ }
+
+ inline void CEdit::SetRect(LPCRECT lpRect) const
+ // Sets the formatting rectangle for the multiline edit control and redraws the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETRECT, 0, (LPARAM)lpRect);
+ }
+
+ inline void CEdit::SetRectNP(LPCRECT lpRect) const
+ // Sets the formatting rectangle for the multiline edit control but does not redraw the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETRECTNP, 0, (LPARAM)lpRect);
+ }
+
+ inline void CEdit::SetSel(DWORD dwSelection, BOOL bNoScroll) const
+ // Selects a range of characters in the edit control by setting the starting and ending positions to be selected.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_SETSEL, LOWORD(dwSelection), HIWORD(dwSelection));
+ if (!bNoScroll)
+ SendMessage(EM_SCROLLCARET, 0, 0);
+ }
+
+ inline void CEdit::SetSel(int nStartChar, int nEndChar, BOOL bNoScroll) const
+ // Selects a range of characters in the edit control by setting the starting and ending positions to be selected.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(m_hWnd, EM_SETSEL, nStartChar, nEndChar);
+ if (!bNoScroll)
+ SendMessage(EM_SCROLLCARET, 0, 0);
+ }
+
+ inline BOOL CEdit::SetTabStops(int nTabStops, LPINT rgTabStops) const
+ // Sets tab-stop positions in the multiline edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops);
+ }
+
+ inline BOOL CEdit::SetTabStops() const
+ // Sets tab-stop positions in the multiline edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage( EM_SETTABSTOPS, 0, 0);
+ }
+
+ inline BOOL CEdit::SetTabStops(const int& cxEachStop) const
+ // Sets tab-stop positions in the multiline edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop);
+ }
+
+ inline void CEdit::Clear() const
+ // Clears the current selection, if any, in an edit control.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(WM_CLEAR, 0, 0);
+ }
+
+ inline void CEdit::Copy() const
+ // Copies text to the clipboard unless the style is ES_PASSWORD, in which case the message returns zero.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(WM_COPY, 0, 0);
+ }
+
+ inline void CEdit::Cut() const
+ // Cuts the selection to the clipboard, or deletes the character to the left of the cursor if there is no selection.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(WM_CUT, 0, 0);
+ }
+
+ inline void CEdit::Paste() const
+ // Pastes text from the clipboard into the edit control window at the caret position.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(WM_PASTE, 0, 0);
+ }
+
+ inline void CEdit::Undo() const
+ // Removes any text that was just inserted or inserts any deleted characters and sets the selection to the inserted text.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(EM_UNDO, 0, 0);
+ }
+
+ inline void CEdit::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = _T("Edit");
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CListbox class
+ //
+ inline int CListBox::GetCount() const
+ // Returns the number of items in the list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETCOUNT, 0, 0);
+ }
+
+ inline int CListBox::GetHorizontalExtent() const
+ // Returns the scrollable width, in pixels, of a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETHORIZONTALEXTENT, 0, 0);
+ }
+
+ inline DWORD CListBox::GetItemData(int nIndex) const
+ // Returns the value associated with the specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(LB_GETITEMDATA, nIndex, 0);
+ }
+
+ inline void* CListBox::GetItemDataPtr(int nIndex) const
+ // Returns the value associated with the specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (LPVOID)SendMessage(LB_GETITEMDATA, nIndex, 0);
+ }
+
+ inline int CListBox::GetItemHeight(int nIndex) const
+ // Returns the height, in pixels, of an item in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETITEMHEIGHT, nIndex, 0L);
+ }
+
+ inline int CListBox::GetItemRect(int nIndex, LPRECT lpRect) const
+ // Retrieves the client coordinates of the specified list box item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETITEMRECT, nIndex, (LPARAM)lpRect);
+ }
+
+ inline LCID CListBox::GetLocale() const
+ // Retrieves the locale of the list box. The high-order word contains the country/region code
+ // and the low-order word contains the language identifier.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (LCID)::SendMessage(m_hWnd, LB_GETLOCALE, 0, 0);
+ }
+
+ inline int CListBox::GetSel(int nIndex) const
+ // Returns the selection state of a list box item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETSEL, nIndex, 0);
+ }
+
+ inline int CListBox::GetText(int nIndex, LPTSTR lpszBuffer) const
+ // Retrieves the string associated with a specified item and the length of the string.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)::SendMessage(m_hWnd, LB_GETTEXT, nIndex, (LPARAM)lpszBuffer);
+ }
+
+ inline int CListBox::GetTextLen(int nIndex) const
+ // Returns the length, in characters, of the string associated with a specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage( LB_GETTEXTLEN, nIndex, 0);
+ }
+
+ inline int CListBox::GetTopIndex() const
+ // Returns the index of the first visible item in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETTOPINDEX, 0, 0);
+ }
+
+ inline UINT CListBox::ItemFromPoint(CPoint pt, BOOL& bOutside) const
+ // Retrieves the zero-based index of the item nearest the specified point in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ DWORD dw = (DWORD)::SendMessage(m_hWnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y));
+ bOutside = !!HIWORD(dw);
+ return LOWORD(dw);
+ }
+
+ inline void CListBox::SetColumnWidth(int cxWidth) const
+ // Sets the width, in pixels, of all columns in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(LB_SETCOLUMNWIDTH, cxWidth, 0);
+ }
+
+ inline void CListBox::SetHorizontalExtent(int cxExtent) const
+ // Sets the scrollable width, in pixels, of a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(LB_SETHORIZONTALEXTENT, cxExtent, 0);
+ }
+
+ inline int CListBox::SetItemData(int nIndex, DWORD dwItemData) const
+ // Associates a value with a list box item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_SETITEMDATA, nIndex, (LPARAM)dwItemData);
+ }
+
+ inline int CListBox::SetItemDataPtr(int nIndex, void* pData) const
+ // Associates a value with a list box item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return SetItemData(nIndex, (DWORD)(DWORD_PTR)pData);
+ }
+
+ inline int CListBox::SetItemHeight(int nIndex, UINT cyItemHeight) const
+ // Sets the height, in pixels, of an item or items in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0));
+ }
+
+ inline LCID CListBox::SetLocale(LCID nNewLocale) const
+ // Sets the locale of a list box and returns the previous locale identifier.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (LCID)::SendMessage(m_hWnd, LB_SETLOCALE, (WPARAM)nNewLocale, 0);
+ }
+
+ inline BOOL CListBox::SetTabStops(int nTabStops, LPINT rgTabStops) const
+ // Sets the tab stops to those specified in a specified array.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(LB_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops);
+ }
+
+ inline void CListBox::SetTabStops() const
+ // Sets the tab stops to those specified in a specified array.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(LB_SETTABSTOPS, 0, 0);
+ }
+
+ inline BOOL CListBox::SetTabStops(const int& cxEachStop) const
+ // Sets the tab stops to those specified in a specified array.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(LB_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop);
+ }
+
+ inline int CListBox::SetTopIndex(int nIndex) const
+ // Scrolls the list box so the specified item is at the top of the visible range.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_SETTOPINDEX, nIndex, 0);
+ }
+
+ inline int CListBox::GetCurSel() const
+ // Returns the index of the currently selected item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETCURSEL, 0, 0);
+ }
+
+ inline int CListBox::SetCurSel(int nSelect) const
+ // Selects a specified list box item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_SETCURSEL, nSelect, 0);
+ }
+
+ inline int CListBox::GetAnchorIndex() const
+ // Returns the index of the item that the mouse last selected.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETANCHORINDEX, 0, 0);
+ }
+
+ inline int CListBox::GetCaretIndex() const
+ // Returns the index of the item that has the focus rectangle.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETCARETINDEX, 0, 0L);
+ }
+
+ inline int CListBox::GetSelCount() const
+ // Returns the number of selected items in a multiple-selection list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETSELCOUNT, 0, 0);
+ }
+
+ inline int CListBox::GetSelItems(int nMaxItems, LPINT rgIndex) const
+ // Creates an array of the indexes of all selected items in a multiple-selection list box
+ // and returns the total number of selected items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_GETSELITEMS, nMaxItems, (LPARAM)rgIndex);
+ }
+
+ inline int CListBox::SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) const
+ // Selects a specified range of items in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ if (bSelect)
+ return (int)SendMessage(LB_SELITEMRANGEEX, nFirstItem, nLastItem);
+ else
+ return (int)SendMessage(LB_SELITEMRANGEEX, nLastItem, nFirstItem);
+ }
+
+ inline void CListBox::SetAnchorIndex(int nIndex) const
+ // Sets the item that the mouse last selected to a specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(LB_SETANCHORINDEX, nIndex, 0);
+ }
+
+ inline int CListBox::SetCaretIndex(int nIndex, BOOL bScroll) const
+ // Sets the focus rectangle to a specified list box item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_SETCARETINDEX, nIndex, MAKELONG(bScroll, 0));
+ }
+
+ inline int CListBox::SetSel(int nIndex, BOOL bSelect) const
+ // Selects an item in a multiple-selection list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_SETSEL, bSelect, nIndex);
+ }
+
+ inline int CListBox::AddString(LPCTSTR lpszItem) const
+ // Adds a string to a list box and returns its index.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_ADDSTRING, 0, (LPARAM)lpszItem);
+ }
+
+ inline int CListBox::DeleteString(UINT nIndex) const
+ // Removes a string from a list box and returns the number of strings remaining in the list.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_DELETESTRING, nIndex, 0);
+ }
+
+ inline int CListBox::Dir(UINT attr, LPCTSTR lpszWildCard) const
+ // Adds a list of filenames to a list box and returns the index of the last filename added.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_DIR, attr, (LPARAM)lpszWildCard);
+ }
+
+ inline int CListBox::FindString(int nStartAfter, LPCTSTR lpszItem) const
+ // Returns the index of the first string in the list box that begins with a specified string.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_FINDSTRING, nStartAfter, (LPARAM)lpszItem);
+ }
+
+ inline int CListBox::FindStringExact(int nIndexStart, LPCTSTR lpszFind) const
+ // Returns the index of the string in the list box that is equal to a specified string.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind);
+ }
+
+ inline int CListBox::InsertString(int nIndex, LPCTSTR lpszItem) const
+ // Inserts a string at a specified index in a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(LB_INSERTSTRING, nIndex, (LPARAM)lpszItem);
+ }
+
+ inline void CListBox::ResetContent() const
+ // Removes all items from a list box.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(LB_RESETCONTENT, 0, 0);
+ }
+
+ inline int CListBox::SelectString(int nStartAfter, LPCTSTR lpszItem) const
+ // Selects the first string it finds that matches a specified prefix.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)::SendMessage(m_hWnd, LB_SELECTSTRING, nStartAfter, (LPARAM)lpszItem);
+ }
+
+ inline void CListBox::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = _T("ListBox");
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CStatic class
+ //
+ inline HBITMAP CStatic::GetBitmap() const
+ // Returns the handle to the bitmap for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HBITMAP)SendMessage(STM_GETIMAGE, IMAGE_BITMAP, 0);
+ }
+
+ inline HCURSOR CStatic::GetCursor() const
+ // Returns the handle to the icon for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HCURSOR)SendMessage(STM_GETIMAGE, IMAGE_CURSOR, 0);
+ }
+
+ inline HENHMETAFILE CStatic::GetEnhMetaFile() const
+ // Returns the handle to the enhanced metafile for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HENHMETAFILE)SendMessage(STM_GETIMAGE, IMAGE_ENHMETAFILE, 0);
+ }
+
+ inline HICON CStatic::GetIcon() const
+ // Returns the handle to the icon for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage(STM_GETIMAGE, IMAGE_ICON, 0);
+ }
+
+ inline HBITMAP CStatic::SetBitmap(HBITMAP hBitmap) const
+ // Sets the handle to the bitmap for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HBITMAP)SendMessage(STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
+ }
+
+ inline HCURSOR CStatic::SetCursor(HCURSOR hCursor) const
+ // Sets the handle to the cursor for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HCURSOR)SendMessage(STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor);
+ }
+
+ inline HENHMETAFILE CStatic::SetEnhMetaFile(HENHMETAFILE hMetaFile) const
+ // Sets the handle to the enhanced metafile for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HENHMETAFILE)SendMessage(STM_SETIMAGE, IMAGE_ENHMETAFILE, (LPARAM)hMetaFile);
+ }
+
+ inline HICON CStatic::SetIcon(HICON hIcon) const
+ // Sets the handle to the icon for the static control
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage(STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ }
+
+ inline void CStatic::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = _T("Static");
+ }
+
+}
+
+#endif // _WIN32XX_STDCONTROLS_H_
+
diff --git a/mmc_updater/depends/win32cpp/tab.h b/mmc_updater/depends/win32cpp/tab.h
new file mode 100644
index 00000000..15699144
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/tab.h
@@ -0,0 +1,1658 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////
+// tab.h
+// Declaration of the CTab and CMDITab classes
+
+#ifndef _WIN32XX_TAB_H_
+#define _WIN32XX_TAB_H_
+
+#include "wincore.h"
+#include "dialog.h"
+#include "gdi.h"
+#include "default_resource.h"
+
+namespace Win32xx
+{
+
+ struct TabPageInfo
+ {
+ TCHAR szTabText[MAX_MENU_STRING];
+ int iImage; // index of this tab's image
+ int idTab; // identifier for this tab (optional)
+ CWnd* pView; // pointer to the view window
+ };
+
+ class CTab : public CWnd
+ {
+ protected:
+ // Declaration of the CSelectDialog class, a nested class of CTab
+ // It creates the dialog to choose which tab to activate
+ class CSelectDialog : public CDialog
+ {
+ public:
+ CSelectDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent = NULL);
+ virtual ~CSelectDialog() {}
+ virtual void AddItem(LPCTSTR szString);
+ virtual BOOL IsTab() const { return FALSE; }
+
+ protected:
+ virtual BOOL OnInitDialog();
+ virtual void OnOK();
+ virtual void OnCancel() { EndDialog(-2); }
+
+ private:
+ CSelectDialog(const CSelectDialog&); // Disable copy construction
+ CSelectDialog& operator = (const CSelectDialog&); // Disable assignment operator
+
+ std::vector<tString> m_vItems;
+ int IDC_LIST;
+
+ };
+ public:
+ CTab();
+ virtual ~CTab();
+ virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText, HICON hIcon, UINT idTab);
+ virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText, int nID_Icon, UINT idTab = 0);
+ virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText);
+ virtual CRect GetCloseRect() const;
+ virtual CRect GetListRect() const;
+ virtual HMENU GetListMenu();
+ virtual BOOL GetTabsAtTop() const;
+ virtual int GetTabIndex(CWnd* pWnd) const;
+ virtual TabPageInfo GetTabPageInfo(UINT nTab) const;
+ virtual int GetTextHeight() const;
+ virtual void RecalcLayout();
+ virtual void RemoveTabPage(int nPage);
+ virtual void SelectPage(int nPage);
+ virtual void SetFixedWidth(BOOL bEnabled);
+ virtual void SetOwnerDraw(BOOL bEnabled);
+ virtual void SetShowButtons(BOOL bShow);
+ virtual void SetTabIcon(int i, HICON hIcon);
+ virtual void SetTabsAtTop(BOOL bTop);
+ virtual void SetTabText(UINT nTab, LPCTSTR szText);
+ virtual void SwapTabs(UINT nTab1, UINT nTab2);
+
+ // Attributes
+ std::vector <TabPageInfo>& GetAllTabs() const { return (std::vector <TabPageInfo>&) m_vTabPageInfo; }
+ HIMAGELIST GetImageList() const { return m_himlTab; }
+ BOOL GetShowButtons() const { return m_bShowButtons; }
+ int GetTabHeight() const { return m_nTabHeight; }
+ CWnd* GetActiveView() const { return m_pActiveView; }
+ void SetTabHeight(int nTabHeight) { m_nTabHeight = nTabHeight; NotifyChanged();}
+
+ // Wrappers for Win32 Macros
+ void AdjustRect(BOOL fLarger, RECT *prc) const;
+ int GetCurFocus() const;
+ int GetCurSel() const;
+ BOOL GetItem(int iItem, LPTCITEM pitem) const;
+ int GetItemCount() const;
+ int HitTest(TCHITTESTINFO& info) const;
+ void SetCurFocus(int iItem) const;
+ int SetCurSel(int iItem) const;
+ DWORD SetItemSize(int cx, int cy) const;
+ int SetMinTabWidth(int cx) const;
+ void SetPadding(int cx, int cy) const;
+
+ protected:
+ virtual void DrawCloseButton(CDC& DrawDC);
+ virtual void DrawListButton(CDC& DrawDC);
+ virtual void DrawTabs(CDC& dcMem);
+ virtual void DrawTabBorders(CDC& dcMem, CRect& rcTab);
+ virtual void OnCreate();
+ virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam);
+ virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam);
+ virtual void OnMouseLeave(WPARAM wParam, LPARAM lParam);
+ virtual void OnMouseMove(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNCHitTest(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam);
+ virtual void NotifyChanged();
+ virtual void Paint();
+ virtual void PreCreate(CREATESTRUCT& cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual void SetTabSize();
+ virtual void ShowListDialog();
+ virtual void ShowListMenu();
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CTab(const CTab&); // Disable copy construction
+ CTab& operator = (const CTab&); // Disable assignment operator
+
+ SIZE GetMaxTabSize() const;
+ void ShowActiveView(CWnd* pView);
+
+ std::vector<TabPageInfo> m_vTabPageInfo;
+ std::vector<WndPtr> m_vTabViews;
+ CFont m_Font;
+ HIMAGELIST m_himlTab;
+ HMENU m_hListMenu;
+ CWnd* m_pActiveView;
+ BOOL m_bShowButtons; // Show or hide the close and list button
+ BOOL m_IsTracking;
+ BOOL m_IsClosePressed;
+ BOOL m_IsListPressed;
+ BOOL m_IsListMenuActive;
+ int m_nTabHeight;
+ };
+
+ ////////////////////////////////////////
+ // Declaration of the CTabbedMDI class
+ class CTabbedMDI : public CWnd
+ {
+ public:
+ CTabbedMDI();
+ virtual ~CTabbedMDI();
+ virtual CWnd* AddMDIChild(CWnd* pView, LPCTSTR szTabText, int idMDIChild = 0);
+ virtual void CloseActiveMDI();
+ virtual void CloseAllMDIChildren();
+ virtual void CloseMDIChild(int nTab);
+ virtual CWnd* GetActiveMDIChild() const;
+ virtual int GetActiveMDITab() const;
+ virtual CWnd* GetMDIChild(int nTab) const;
+ virtual int GetMDIChildCount() const;
+ virtual int GetMDIChildID(int nTab) const;
+ virtual LPCTSTR GetMDIChildTitle(int nTab) const;
+ virtual HMENU GetListMenu() const { return GetTab().GetListMenu(); }
+ virtual CTab& GetTab() const {return (CTab&)m_Tab;}
+ virtual BOOL LoadRegistrySettings(tString tsRegistryKeyName);
+ virtual void RecalcLayout();
+ virtual BOOL SaveRegistrySettings(tString tsRegistryKeyName);
+ virtual void SetActiveMDIChild(CWnd* pWnd);
+ virtual void SetActiveMDITab(int nTab);
+
+ protected:
+ virtual HWND Create(CWnd* pParent);
+ virtual CWnd* NewMDIChildFromID(int idMDIChild);
+ virtual void OnCreate();
+ virtual void OnDestroy(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam);
+ virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CTabbedMDI(const CTabbedMDI&); // Disable copy construction
+ CTabbedMDI& operator = (const CTabbedMDI&); // Disable assignment operator
+
+ CTab m_Tab;
+ };
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ /////////////////////////////////////////////////////////////
+ // Definitions for the CSelectDialog class nested within CTab
+ //
+ inline CTab::CSelectDialog::CSelectDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent) :
+ CDialog(lpTemplate, pParent), IDC_LIST(121)
+ {
+ }
+
+ inline BOOL CTab::CSelectDialog::OnInitDialog()
+ {
+ for (UINT u = 0; u < m_vItems.size(); ++u)
+ {
+ SendDlgItemMessage(IDC_LIST, LB_ADDSTRING, 0, (LPARAM) m_vItems[u].c_str());
+ }
+
+ return true;
+ }
+
+ inline void CTab::CSelectDialog::AddItem(LPCTSTR szString)
+ {
+ m_vItems.push_back(szString);
+ }
+
+ inline void CTab::CSelectDialog::OnOK()
+ {
+ int iSelect = (int)SendDlgItemMessage(IDC_LIST, LB_GETCURSEL, 0, 0);
+ if (iSelect != LB_ERR)
+ EndDialog(iSelect);
+ else
+ EndDialog(-2);
+ }
+
+
+ //////////////////////////////////////////////////////////
+ // Definitions for the CTab class
+ //
+ inline CTab::CTab() : m_hListMenu(NULL), m_pActiveView(NULL), m_bShowButtons(FALSE), m_IsTracking(FALSE), m_IsClosePressed(FALSE),
+ m_IsListPressed(FALSE), m_IsListMenuActive(FALSE), m_nTabHeight(0)
+ {
+ // Create and assign the image list
+ m_himlTab = ImageList_Create(16, 16, ILC_MASK|ILC_COLOR32, 0, 0);
+
+ // Set the tab control's font
+ NONCLIENTMETRICS info = {0};
+ info.cbSize = GetSizeofNonClientMetrics();
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
+ m_Font.CreateFontIndirect(&info.lfStatusFont);
+ }
+
+ inline CTab::~CTab()
+ {
+ ImageList_Destroy(m_himlTab);
+
+ if (IsMenu(m_hListMenu)) ::DestroyMenu(m_hListMenu);
+ }
+
+ inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText, HICON hIcon, UINT idTab)
+ {
+ assert(pView.get());
+ assert(lstrlen(szTabText) < MAX_MENU_STRING);
+
+ m_vTabViews.push_back(pView);
+
+ TabPageInfo tpi = {0};
+ tpi.pView = pView.get();
+ tpi.idTab = idTab;
+ lstrcpyn(tpi.szTabText, szTabText, MAX_MENU_STRING);
+ if (hIcon)
+ tpi.iImage = ImageList_AddIcon(GetImageList(), hIcon);
+ else
+ tpi.iImage = -1;
+
+ int iNewPage = (int)m_vTabPageInfo.size();
+ m_vTabPageInfo.push_back(tpi);
+
+ if (m_hWnd)
+ {
+ TCITEM tie = {0};
+ tie.mask = TCIF_TEXT | TCIF_IMAGE;
+ tie.iImage = tpi.iImage;
+ tie.pszText = tpi.szTabText;
+ TabCtrl_InsertItem(m_hWnd, iNewPage, &tie);
+
+ SetTabSize();
+ SelectPage(iNewPage);
+ NotifyChanged();
+ }
+
+ return iNewPage;
+ }
+
+ inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText, int idIcon, UINT idTab /* = 0*/)
+ {
+ HICON hIcon = (HICON)LoadImage(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(idIcon), IMAGE_ICON, 0, 0, LR_SHARED);
+ return AddTabPage(pView, szTabText, hIcon, idTab);
+ }
+
+ inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText)
+ {
+ return AddTabPage(pView, szTabText, (HICON)0, 0);
+ }
+
+ inline void CTab::DrawCloseButton(CDC& DrawDC)
+ {
+ // The close button isn't displayed on Win95
+ if (GetWinVersion() == 1400) return;
+
+ if (!m_bShowButtons) return;
+ if (!GetActiveView()) return;
+ if (!(GetWindowLongPtr(GWL_STYLE) & TCS_FIXEDWIDTH)) return;
+ if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) return;
+
+ // Determine the close button's drawing position relative to the window
+ CRect rcClose = GetCloseRect();
+
+ CPoint pt = GetCursorPos();
+ ScreenToClient(pt);
+ UINT uState = rcClose.PtInRect(pt)? m_IsClosePressed? 2: 1: 0;
+
+ // Draw the outer highlight for the close button
+ if (!IsRectEmpty(&rcClose))
+ {
+ switch (uState)
+ {
+ case 0:
+ {
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220));
+
+ DrawDC.MoveTo(rcClose.left, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.bottom);
+ break;
+ }
+
+ case 1:
+ {
+ // Draw outline, white at top, black on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.MoveTo(rcClose.left, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.LineTo(rcClose.left, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.bottom);
+ }
+
+ break;
+ case 2:
+ {
+ // Draw outline, black on top, white on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.MoveTo(rcClose.left, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.bottom);
+ DrawDC.LineTo(rcClose.right, rcClose.top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.LineTo(rcClose.left, rcClose.top);
+ DrawDC.LineTo(rcClose.left, rcClose.bottom);
+ }
+ break;
+ }
+
+ // Manually draw close button
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(64, 64, 64));
+
+ DrawDC.MoveTo(rcClose.left + 3, rcClose.top +3);
+ DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.left + 4, rcClose.top +3);
+ DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -3);
+
+ DrawDC.MoveTo(rcClose.left + 3, rcClose.top +4);
+ DrawDC.LineTo(rcClose.right - 3, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.right -3, rcClose.top +3);
+ DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.right -3, rcClose.top +4);
+ DrawDC.LineTo(rcClose.left + 3, rcClose.bottom -2);
+
+ DrawDC.MoveTo(rcClose.right -4, rcClose.top +3);
+ DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -3);
+ }
+ }
+
+ inline void CTab::DrawListButton(CDC& DrawDC)
+ {
+ // The list button isn't displayed on Win95
+ if (GetWinVersion() == 1400) return;
+
+ if (!m_bShowButtons) return;
+ if (!GetActiveView()) return;
+ if (!(GetWindowLongPtr(GWL_STYLE) & TCS_FIXEDWIDTH)) return;
+ if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) return;
+
+ // Determine the list button's drawing position relative to the window
+ CRect rcList = GetListRect();
+
+ CPoint pt = GetCursorPos();
+ ScreenToClient(pt);
+ UINT uState = rcList.PtInRect(pt)? 1: 0;
+ if (m_IsListMenuActive) uState = 2;
+
+ // Draw the outer highlight for the list button
+ if (!IsRectEmpty(&rcList))
+ {
+ switch (uState)
+ {
+ case 0:
+ {
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220));
+
+ DrawDC.MoveTo(rcList.left, rcList.bottom);
+ DrawDC.LineTo(rcList.right, rcList.bottom);
+ DrawDC.LineTo(rcList.right, rcList.top);
+ DrawDC.LineTo(rcList.left, rcList.top);
+ DrawDC.LineTo(rcList.left, rcList.bottom);
+ break;
+ }
+
+ case 1:
+ {
+ // Draw outline, white at top, black on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.MoveTo(rcList.left, rcList.bottom);
+ DrawDC.LineTo(rcList.right, rcList.bottom);
+ DrawDC.LineTo(rcList.right, rcList.top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.LineTo(rcList.left, rcList.top);
+ DrawDC.LineTo(rcList.left, rcList.bottom);
+ }
+
+ break;
+ case 2:
+ {
+ // Draw outline, black on top, white on bottom
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
+ DrawDC.MoveTo(rcList.left, rcList.bottom);
+ DrawDC.LineTo(rcList.right, rcList.bottom);
+ DrawDC.LineTo(rcList.right, rcList.top);
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
+ DrawDC.LineTo(rcList.left, rcList.top);
+ DrawDC.LineTo(rcList.left, rcList.bottom);
+ }
+ break;
+ }
+
+ // Manually draw list button
+ DrawDC.CreatePen(PS_SOLID, 1, RGB(64, 64, 64));
+
+ int MaxLength = (int)(0.65 * rcList.Width());
+ int topGap = 1 + rcList.Height()/3;
+ for (int i = 0; i <= MaxLength/2; i++)
+ {
+ int Length = MaxLength - 2*i;
+ DrawDC.MoveTo(rcList.left +1 + (rcList.Width() - Length)/2, rcList.top +topGap +i);
+ DrawDC.LineTo(rcList.left +1 + (rcList.Width() - Length)/2 + Length, rcList.top +topGap +i);
+ }
+ }
+ }
+
+ inline void CTab::DrawTabs(CDC& dcMem)
+ {
+ // Draw the tab buttons:
+ for (int i = 0; i < TabCtrl_GetItemCount(m_hWnd); ++i)
+ {
+ CRect rcItem;
+ TabCtrl_GetItemRect(m_hWnd, i, &rcItem);
+ if (!rcItem.IsRectEmpty())
+ {
+ if (i == TabCtrl_GetCurSel(m_hWnd))
+ {
+ dcMem.CreateSolidBrush(RGB(248,248,248));
+ dcMem.SetBkColor(RGB(248,248,248));
+ }
+ else
+ {
+ dcMem.CreateSolidBrush(RGB(200,200,200));
+ dcMem.SetBkColor(RGB(200,200,200));
+ }
+
+ dcMem.CreatePen(PS_SOLID, 1, RGB(160, 160, 160));
+ dcMem.RoundRect(rcItem.left+1, rcItem.top, rcItem.right+2, rcItem.bottom, 6, 6);
+
+ if (rcItem.Width() >= 24)
+ {
+ TCHAR szText[30];
+ TCITEM tcItem = {0};
+ tcItem.mask = TCIF_TEXT | TCIF_IMAGE;
+ tcItem.cchTextMax = 30;
+ tcItem.pszText = szText;
+ TabCtrl_GetItem(m_hWnd, i, &tcItem);
+ int xImage;
+ int yImage;
+ int yOffset = 0;
+ if (ImageList_GetIconSize(m_himlTab, &xImage, &yImage))
+ yOffset = (rcItem.Height() - yImage)/2;
+
+ // Draw the icon
+ ImageList_Draw(m_himlTab, tcItem.iImage, dcMem, rcItem.left+5, rcItem.top+yOffset, ILD_NORMAL);
+
+ // Draw the text
+ dcMem.SelectObject(&m_Font);
+
+ // Calculate the size of the text
+ CRect rcText = rcItem;
+
+ int iImageSize = 20;
+ int iPadding = 4;
+ if (tcItem.iImage >= 0)
+ rcText.left += iImageSize;
+
+ rcText.left += iPadding;
+ dcMem.DrawText(szText, -1, rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
+ }
+ }
+ }
+ }
+
+ inline void CTab::DrawTabBorders(CDC& dcMem, CRect& rcTab)
+ {
+ BOOL IsBottomTab = (BOOL)GetWindowLongPtr(GWL_STYLE) & TCS_BOTTOM;
+
+ // Draw a lighter rectangle touching the tab buttons
+ CRect rcItem;
+ TabCtrl_GetItemRect(m_hWnd, 0, &rcItem);
+ int left = rcItem.left +1;
+ int right = rcTab.right;
+ int top = rcTab.bottom;
+ int bottom = top + 3;
+
+ if (!IsBottomTab)
+ {
+ bottom = MAX(rcTab.top, m_nTabHeight +4);
+ top = bottom -3;
+ }
+
+ dcMem.CreateSolidBrush(RGB(248,248,248));
+ dcMem.CreatePen(PS_SOLID, 1, RGB(248,248,248));
+ if (!rcItem.IsRectEmpty())
+ {
+ dcMem.Rectangle(left, top, right, bottom);
+
+ // Draw a darker line below the rectangle
+ dcMem.CreatePen(PS_SOLID, 1, RGB(160, 160, 160));
+ if (IsBottomTab)
+ {
+ dcMem.MoveTo(left-1, bottom);
+ dcMem.LineTo(right, bottom);
+ }
+ else
+ {
+ dcMem.MoveTo(left-1, top-1);
+ dcMem.LineTo(right, top-1);
+ }
+
+ // Draw a lighter line over the darker line for the selected tab
+ dcMem.CreatePen(PS_SOLID, 1, RGB(248,248,248));
+ TabCtrl_GetItemRect(m_hWnd, TabCtrl_GetCurSel(m_hWnd), &rcItem);
+ OffsetRect(&rcItem, 1, 1);
+
+ if (IsBottomTab)
+ {
+ dcMem.MoveTo(rcItem.left, bottom);
+ dcMem.LineTo(rcItem.right, bottom);
+ }
+ else
+ {
+ dcMem.MoveTo(rcItem.left, top-1);
+ dcMem.LineTo(rcItem.right, top-1);
+ }
+ }
+ }
+
+ inline CRect CTab::GetCloseRect() const
+ {
+ CRect rcClose;
+ if (GetShowButtons())
+ {
+ rcClose= GetClientRect();
+ int Gap = 2;
+ int cx = GetSystemMetrics(SM_CXSMICON) -1;
+ int cy = GetSystemMetrics(SM_CYSMICON) -1;
+ rcClose.right -= Gap;
+ rcClose.left = rcClose.right - cx;
+
+ if (GetTabsAtTop())
+ rcClose.top = Gap;
+ else
+ rcClose.top = MAX(Gap, rcClose.bottom - m_nTabHeight);
+
+ rcClose.bottom = rcClose.top + cy;
+ }
+ return rcClose;
+ }
+
+ inline HMENU CTab::GetListMenu()
+ {
+ if (IsMenu(m_hListMenu))
+ ::DestroyMenu(m_hListMenu);
+
+ m_hListMenu = CreatePopupMenu();
+
+ // Add the menu items
+ for(UINT u = 0; u < MIN(GetAllTabs().size(), 9); ++u)
+ {
+ TCHAR szMenuString[MAX_MENU_STRING+1];
+ TCHAR szTabText[MAX_MENU_STRING];
+ lstrcpyn(szTabText, GetAllTabs()[u].szTabText, MAX_MENU_STRING -4);
+ wsprintf(szMenuString, _T("&%d %s"), u+1, szTabText);
+ AppendMenu(m_hListMenu, MF_STRING, IDW_FIRSTCHILD +u, szMenuString);
+ }
+ if (GetAllTabs().size() >= 10)
+ AppendMenu(m_hListMenu, MF_STRING, IDW_FIRSTCHILD +9, _T("More Windows"));
+
+ // Add a checkmark to the menu
+ int iSelected = GetCurSel();
+ if (iSelected < 9)
+ CheckMenuItem(m_hListMenu, iSelected, MF_BYPOSITION|MF_CHECKED);
+
+ return m_hListMenu;
+ }
+
+ inline CRect CTab::GetListRect() const
+ {
+ CRect rcList;
+ if (GetShowButtons())
+ {
+ CRect rcClose = GetCloseRect();
+ rcList = rcClose;
+ rcList.OffsetRect( -(rcClose.Width() + 2), 0);
+ rcList.InflateRect(-1, 0);
+ }
+ return rcList;
+ }
+
+ inline SIZE CTab::GetMaxTabSize() const
+ {
+ CSize Size;
+
+ for (int i = 0; i < TabCtrl_GetItemCount(m_hWnd); i++)
+ {
+ CClientDC dcClient(this);
+ dcClient.SelectObject(&m_Font);
+ std::vector<TCHAR> vTitle(MAX_MENU_STRING, _T('\0'));
+ TCHAR* pszTitle = &vTitle.front();
+ TCITEM tcItem = {0};
+ tcItem.mask = TCIF_TEXT |TCIF_IMAGE;
+ tcItem.cchTextMax = MAX_MENU_STRING;
+ tcItem.pszText = pszTitle;
+ TabCtrl_GetItem(m_hWnd, i, &tcItem);
+ CSize TempSize = dcClient.GetTextExtentPoint32(pszTitle, lstrlen(pszTitle));
+
+ int iImageSize = 0;
+ int iPadding = 6;
+ if (tcItem.iImage >= 0)
+ iImageSize = 20;
+ TempSize.cx += iImageSize + iPadding;
+
+ if (TempSize.cx > Size.cx)
+ Size = TempSize;
+ }
+
+ return Size;
+ }
+
+ inline BOOL CTab::GetTabsAtTop() const
+ // Returns TRUE if the contol's tabs are placed at the top
+ {
+ DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
+ return (!(dwStyle & TCS_BOTTOM));
+ }
+
+ inline int CTab::GetTextHeight() const
+ {
+ CClientDC dcClient(this);
+ dcClient.SelectObject(&m_Font);
+ CSize szText = dcClient.GetTextExtentPoint32(_T("Text"), lstrlen(_T("Text")));
+ return szText.cy;
+ }
+
+ inline int CTab::GetTabIndex(CWnd* pWnd) const
+ {
+ assert(pWnd);
+
+ for (int i = 0; i < (int)m_vTabPageInfo.size(); ++i)
+ {
+ if (m_vTabPageInfo[i].pView == pWnd)
+ return i;
+ }
+
+ return -1;
+ }
+
+ inline TabPageInfo CTab::GetTabPageInfo(UINT nTab) const
+ {
+ assert (nTab < m_vTabPageInfo.size());
+
+ return m_vTabPageInfo[nTab];
+ }
+
+ inline void CTab::NotifyChanged()
+ {
+ NMHDR nmhdr = {0};
+ nmhdr.hwndFrom = m_hWnd;
+ nmhdr.code = UWM_TAB_CHANGED;
+ GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&nmhdr);
+ }
+
+ inline void CTab::OnCreate()
+ {
+ SetFont(&m_Font, TRUE);
+
+ // Assign ImageList unless we are owner drawn
+ if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED))
+ TabCtrl_SetImageList(m_hWnd, m_himlTab);
+
+ for (int i = 0; i < (int)m_vTabPageInfo.size(); ++i)
+ {
+ // Add tabs for each view.
+ TCITEM tie = {0};
+ tie.mask = TCIF_TEXT | TCIF_IMAGE;
+ tie.iImage = m_vTabPageInfo[i].iImage;
+ tie.pszText = m_vTabPageInfo[i].szTabText;
+ TabCtrl_InsertItem(m_hWnd, i, &tie);
+ }
+
+ int HeightGap = 5;
+ SetTabHeight(MAX(20, (GetTextHeight() + HeightGap)));
+ SelectPage(0);
+ }
+
+ inline void CTab::OnLButtonDown(WPARAM /*wParam*/, LPARAM lParam)
+ {
+ CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ if (GetCloseRect().PtInRect(pt))
+ {
+ m_IsClosePressed = TRUE;
+ SetCapture();
+ CClientDC dc(this);
+ DrawCloseButton(dc);
+ }
+ else
+ m_IsClosePressed = FALSE;
+
+ if (GetListRect().PtInRect(pt))
+ {
+ ShowListMenu();
+ }
+ }
+
+ inline void CTab::OnLButtonUp(WPARAM /*wParam*/, LPARAM lParam)
+ {
+ ReleaseCapture();
+ CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ if (m_IsClosePressed && GetCloseRect().PtInRect(pt))
+ {
+ RemoveTabPage(GetCurSel());
+ if (GetActiveView())
+ GetActiveView()->RedrawWindow();
+ }
+
+ m_IsClosePressed = FALSE;
+ }
+
+ inline void CTab::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/)
+ {
+ CClientDC dc(this);
+ DrawCloseButton(dc);
+ DrawListButton(dc);
+
+ m_IsTracking = FALSE;
+ }
+
+ inline void CTab::OnMouseMove(WPARAM /*wParam*/, LPARAM /*lParam*/)
+ {
+ if (!m_IsListMenuActive && m_IsListPressed)
+ {
+ m_IsListPressed = FALSE;
+ }
+
+ if (!m_IsTracking)
+ {
+ TRACKMOUSEEVENT TrackMouseEventStruct = {0};
+ TrackMouseEventStruct.cbSize = sizeof(TrackMouseEventStruct);
+ TrackMouseEventStruct.dwFlags = TME_LEAVE;
+ TrackMouseEventStruct.hwndTrack = m_hWnd;
+ _TrackMouseEvent(&TrackMouseEventStruct);
+ m_IsTracking = TRUE;
+ }
+
+ CClientDC dc(this);
+ DrawCloseButton(dc);
+ DrawListButton(dc);
+ }
+
+ inline LRESULT CTab::OnNCHitTest(WPARAM wParam, LPARAM lParam)
+ {
+ // Ensure we have an arrow cursor when the tab has no view window
+ if (0 == GetAllTabs().size())
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ // Cause WM_LBUTTONUP and WM_LBUTTONDOWN messages to be sent for buttons
+ CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ ScreenToClient(pt);
+ if (GetCloseRect().PtInRect(pt)) return HTCLIENT;
+ if (GetListRect().PtInRect(pt)) return HTCLIENT;
+
+ return CWnd::WndProcDefault(WM_NCHITTEST, wParam, lParam);
+ }
+
+ inline LRESULT CTab::OnNotifyReflect(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case TCN_SELCHANGE:
+ {
+ // Display the newly selected tab page
+ int nPage = GetCurSel();
+ ShowActiveView(m_vTabPageInfo[nPage].pView);
+ }
+ break;
+ }
+
+ return 0L;
+ }
+
+ inline void CTab::Paint()
+ {
+ // Microsoft's drawing for a tab control is rubbish, so we do our own.
+ // We use double buffering and regions to eliminate flicker
+
+ // Create the memory DC and bitmap
+ CClientDC dcView(this);
+ CMemDC dcMem(&dcView);
+ CRect rcClient = GetClientRect();
+ dcMem.CreateCompatibleBitmap(&dcView, rcClient.Width(), rcClient.Height());
+
+ if (0 == GetItemCount())
+ {
+ // No tabs, so simply display a grey background and exit
+ COLORREF rgbDialog = GetSysColor(COLOR_BTNFACE);
+ dcView.SolidFill(rgbDialog, rcClient);
+ return;
+ }
+
+ // Create a clipping region. Its the overall tab window's region,
+ // less the region belonging to the individual tab view's client area
+ CRgn rgnSrc1 = ::CreateRectRgn(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
+ CRect rcTab = GetClientRect();
+ TabCtrl_AdjustRect(m_hWnd, FALSE, &rcTab);
+ if (rcTab.Height() < 0)
+ rcTab.top = rcTab.bottom;
+ if (rcTab.Width() < 0)
+ rcTab.left = rcTab.right;
+
+ CRgn rgnSrc2 = ::CreateRectRgn(rcTab.left, rcTab.top, rcTab.right, rcTab.bottom);
+ CRgn rgnClip = ::CreateRectRgn(0, 0, 0, 0);
+ ::CombineRgn(rgnClip, rgnSrc1, rgnSrc2, RGN_DIFF);
+
+ // Use the region in the memory DC to paint the grey background
+ dcMem.SelectClipRgn(&rgnClip);
+ HWND hWndParent = ::GetParent(m_hWnd);
+ CDC dcParent = ::GetDC(hWndParent);
+ HBRUSH hBrush = (HBRUSH) SendMessage(hWndParent, WM_CTLCOLORDLG, (WPARAM)dcParent.GetHDC(), (LPARAM)hWndParent);
+ dcMem.SelectObject(FromHandle(hBrush));
+ dcMem.PaintRgn(&rgnClip);
+
+ // Draw the tab buttons on the memory DC:
+ DrawTabs(dcMem);
+
+ // Draw buttons and tab borders
+ DrawCloseButton(dcMem);
+ DrawListButton(dcMem);
+ DrawTabBorders(dcMem, rcTab);
+
+ // Now copy our from our memory DC to the window DC
+ dcView.SelectClipRgn(&rgnClip);
+ dcView.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY);
+ }
+
+ inline void CTab::PreCreate(CREATESTRUCT &cs)
+ {
+ // For Tabs on the bottom, add the TCS_BOTTOM style
+ cs.style = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
+ }
+
+ inline void CTab::PreRegisterClass(WNDCLASS &wc)
+ {
+ wc.lpszClassName = WC_TABCONTROL;
+ }
+
+ inline void CTab::RecalcLayout()
+ {
+ if (IsWindow())
+ {
+ if (GetActiveView())
+ {
+ // Set the tab sizes
+ SetTabSize();
+
+ // Position the View over the tab control's display area
+ CRect rc = GetClientRect();
+ TabCtrl_AdjustRect(m_hWnd, FALSE, &rc);
+ GetActiveView()->SetWindowPos(NULL, rc, SWP_SHOWWINDOW);
+ }
+ else
+ RedrawWindow();
+ }
+ }
+
+ inline void CTab::RemoveTabPage(int nPage)
+ {
+ if ((nPage < 0) || (nPage > (int)m_vTabPageInfo.size() -1))
+ return;
+
+ // Remove the tab
+ TabCtrl_DeleteItem(m_hWnd, nPage);
+
+ // Remove the TapPageInfo entry
+ std::vector<TabPageInfo>::iterator itTPI = m_vTabPageInfo.begin() + nPage;
+ CWnd* pView = (*itTPI).pView;
+ int iImage = (*itTPI).iImage;
+ if (iImage >= 0)
+ TabCtrl_RemoveImage(m_hWnd, iImage);
+
+ if (pView == m_pActiveView)
+ m_pActiveView = 0;
+
+ (*itTPI).pView->Destroy();
+ m_vTabPageInfo.erase(itTPI);
+
+ std::vector<WndPtr>::iterator itView;
+ for (itView = m_vTabViews.begin(); itView < m_vTabViews.end(); ++itView)
+ {
+ if ((*itView).get() == pView)
+ {
+ m_vTabViews.erase(itView);
+ break;
+ }
+ }
+
+ if (IsWindow())
+ {
+ if (m_vTabPageInfo.size() > 0)
+ {
+ SetTabSize();
+ SelectPage(0);
+ }
+ else
+ ShowActiveView(NULL);
+
+ NotifyChanged();
+ }
+ }
+
+ inline void CTab::SelectPage(int nPage)
+ {
+ if ((nPage >= 0) && (nPage < GetItemCount()))
+ {
+ if (nPage != GetCurSel())
+ SetCurSel(nPage);
+
+ ShowActiveView(m_vTabPageInfo[nPage].pView);
+ }
+ }
+
+ inline void CTab::SetFixedWidth(BOOL bEnabled)
+ {
+ DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
+ if (bEnabled)
+ SetWindowLongPtr(GWL_STYLE, dwStyle | TCS_FIXEDWIDTH);
+ else
+ SetWindowLongPtr(GWL_STYLE, dwStyle & ~TCS_FIXEDWIDTH);
+
+ RecalcLayout();
+ }
+
+ inline void CTab::SetOwnerDraw(BOOL bEnabled)
+ // Enable or disable owner draw
+ {
+ DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
+ if (bEnabled)
+ {
+ SetWindowLongPtr(GWL_STYLE, dwStyle | TCS_OWNERDRAWFIXED);
+ TabCtrl_SetImageList(m_hWnd, NULL);
+ }
+ else
+ {
+ SetWindowLongPtr(GWL_STYLE, dwStyle & ~TCS_OWNERDRAWFIXED);
+ TabCtrl_SetImageList(m_hWnd, m_himlTab);
+ }
+
+ RecalcLayout();
+ }
+
+ inline void CTab::SetShowButtons(BOOL bShow)
+ {
+ m_bShowButtons = bShow;
+ RecalcLayout();
+ }
+
+ inline void CTab::SetTabIcon(int i, HICON hIcon)
+ // Changes or sets the tab's icon
+ {
+ assert (GetItemCount() > i);
+ TCITEM tci = {0};
+ tci.mask = TCIF_IMAGE;
+ GetItem(i, &tci);
+ if (tci.iImage >= 0)
+ {
+ ImageList_ReplaceIcon(GetImageList(), i, hIcon);
+ }
+ else
+ {
+ int iImage = ImageList_AddIcon(GetImageList(), hIcon);
+ tci.iImage = iImage;
+ TabCtrl_SetItem(m_hWnd, i, &tci);
+ m_vTabPageInfo[i].iImage = iImage;
+ }
+ }
+
+ inline void CTab::SetTabsAtTop(BOOL bTop)
+ // Positions the tabs at the top or botttom of the control
+ {
+ DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
+
+ if (bTop)
+ dwStyle &= ~TCS_BOTTOM;
+ else
+ dwStyle |= TCS_BOTTOM;
+
+ SetWindowLongPtr(GWL_STYLE, dwStyle);
+ RecalcLayout();
+ }
+
+ inline void CTab::SetTabSize()
+ {
+ if (GetItemCount() > 0)
+ {
+ CRect rc = GetClientRect();
+ TabCtrl_AdjustRect(m_hWnd, FALSE, &rc);
+
+ int xGap = 2;
+ if (m_bShowButtons) xGap += GetCloseRect().Width() + GetListRect().Width() +2;
+
+ int nItemWidth = MIN( GetMaxTabSize().cx, (rc.Width() - xGap)/GetItemCount() );
+ nItemWidth = MAX(nItemWidth, 0);
+ SendMessage(TCM_SETITEMSIZE, 0L, MAKELPARAM(nItemWidth, m_nTabHeight));
+ NotifyChanged();
+ }
+ }
+
+ inline void CTab::SetTabText(UINT nTab, LPCTSTR szText)
+ {
+ // Allows the text to be changed on an existing tab
+ if (nTab < GetAllTabs().size())
+ {
+ TCITEM Item = {0};
+ std::vector<TCHAR> vTChar(MAX_MENU_STRING+1, _T('\0'));
+ TCHAR* pTChar = &vTChar.front();
+ lstrcpyn(pTChar, szText, MAX_MENU_STRING);
+ Item.mask = TCIF_TEXT;
+ Item.pszText = pTChar;
+
+ if (TabCtrl_SetItem(m_hWnd, nTab, &Item))
+ lstrcpyn(m_vTabPageInfo[nTab].szTabText, pTChar, MAX_MENU_STRING);
+ }
+ }
+
+ inline void CTab::ShowActiveView(CWnd* pView)
+ // Sets or changes the View window displayed within the tab page
+ {
+ // Hide the old view
+ if (GetActiveView() && (GetActiveView()->IsWindow()))
+ GetActiveView()->ShowWindow(SW_HIDE);
+
+ // Assign the view window
+ m_pActiveView = pView;
+
+ if (m_pActiveView && m_hWnd)
+ {
+ if (!m_pActiveView->IsWindow())
+ {
+ // The tab control is already created, so create the new view too
+ GetActiveView()->Create(this);
+ }
+
+ // Position the View over the tab control's display area
+ CRect rc = GetClientRect();
+ TabCtrl_AdjustRect(m_hWnd, FALSE, &rc);
+ GetActiveView()->SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW);
+ GetActiveView()->SetFocus();
+ }
+ }
+
+ inline void CTab::ShowListMenu()
+ // Displays the list of windows in a popup menu
+ {
+ if (!m_IsListPressed)
+ {
+ m_IsListPressed = TRUE;
+ HMENU hMenu = GetListMenu();
+
+ CPoint pt(GetListRect().left, GetListRect().top + GetTabHeight());
+ ClientToScreen(pt);
+
+ // Choosing the frame's hwnd for the menu's messages will automatically theme the popup menu
+ HWND MenuHwnd = GetAncestor()->GetHwnd();
+ int nPage = 0;
+ m_IsListMenuActive = TRUE;
+ nPage = TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, pt.x, pt.y, MenuHwnd, NULL) - IDW_FIRSTCHILD;
+ if ((nPage >= 0) && (nPage < 9)) SelectPage(nPage);
+ if (nPage == 9) ShowListDialog();
+ m_IsListMenuActive = FALSE;
+ }
+
+ CClientDC dc(this);
+ DrawListButton(dc);
+ }
+
+ inline void CTab::ShowListDialog()
+ {
+ // Definition of a dialog template which displays a List Box
+ unsigned char dlg_Template[] =
+ {
+ 0x01,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xc8,0x00,0xc8,0x90,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0xdc,0x00,0x8e,0x00,0x00,0x00,0x00,0x00,0x53,0x00,0x65,
+ 0x00,0x6c,0x00,0x65,0x00,0x63,0x00,0x74,0x00,0x20,0x00,0x57,0x00,0x69,0x00,0x6e,
+ 0x00,0x64,0x00,0x6f,0x00,0x77,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x01,0x4d,
+ 0x00,0x53,0x00,0x20,0x00,0x53,0x00,0x68,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x20,
+ 0x00,0x44,0x00,0x6c,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x01,0x00,0x01,0x50,0x40,0x00,0x7a,0x00,0x25,0x00,0x0f,0x00,0x01,
+ 0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x4f,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x50,0x7a,0x00,0x7a,0x00,0x25,
+ 0x00,0x0f,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x43,0x00,0x61,0x00,0x6e,
+ 0x00,0x63,0x00,0x65,0x00,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x02,0x00,0x01,0x01,0x21,0x50,0x06,0x00,0x06,0x00,0xcf,0x00,0x6d,0x00,0x79,
+ 0x00,0x00,0x00,0xff,0xff,0x83,0x00,0x00,0x00,0x00,0x00
+ };
+
+ // Display the modal dialog. The dialog is defined in the dialog template rather
+ // than in the resource script (rc) file.
+ CSelectDialog MyDialog((LPCDLGTEMPLATE) dlg_Template);
+ for(UINT u = 0; u < GetAllTabs().size(); ++u)
+ {
+ MyDialog.AddItem(GetAllTabs()[u].szTabText);
+ }
+
+ int iSelected = (int)MyDialog.DoModal();
+ if (iSelected >= 0) SelectPage(iSelected);
+ }
+
+ inline void CTab::SwapTabs(UINT nTab1, UINT nTab2)
+ {
+ if ((nTab1 < GetAllTabs().size()) && (nTab2 < GetAllTabs().size()) && (nTab1 != nTab2))
+ {
+ int nPage = GetCurSel();
+ TabPageInfo T1 = GetTabPageInfo(nTab1);
+ TabPageInfo T2 = GetTabPageInfo(nTab2);
+
+ TCITEM Item1 = {0};
+ Item1.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_RTLREADING | TCIF_STATE | TCIF_TEXT;
+ GetItem(nTab1, &Item1);
+ TCITEM Item2 = {0};
+ Item2.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_RTLREADING | TCIF_STATE | TCIF_TEXT;
+ GetItem(nTab2, &Item2);
+ TabCtrl_SetItem(m_hWnd, nTab1, &Item2);
+ TabCtrl_SetItem(m_hWnd, nTab2, &Item1);
+
+ m_vTabPageInfo[nTab1] = T2;
+ m_vTabPageInfo[nTab2] = T1;
+ SelectPage(nPage);
+ }
+ }
+
+ inline LRESULT CTab::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ if (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)
+ {
+ // Remove all pending paint requests
+ PAINTSTRUCT ps;
+ BeginPaint(ps);
+ EndPaint(ps);
+
+ // Now call our local Paint
+ Paint();
+ return 0;
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ if (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)
+ return 0;
+ break;
+ case WM_KILLFOCUS:
+ m_IsClosePressed = FALSE;
+ break;
+ case WM_LBUTTONDBLCLK:
+ case WM_LBUTTONDOWN:
+ OnLButtonDown(wParam, lParam);
+ break;
+ case WM_LBUTTONUP:
+ OnLButtonUp(wParam, lParam);
+ break;
+ case WM_MOUSEMOVE:
+ OnMouseMove(wParam, lParam);
+ break;
+ case WM_MOUSELEAVE:
+ OnMouseLeave(wParam, lParam);
+ break;
+ case WM_NCHITTEST:
+ return OnNCHitTest(wParam, lParam);
+
+ case WM_WINDOWPOSCHANGING:
+ // A little hack to reduce tab flicker
+ if (IsWindowVisible() && (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED))
+ {
+ LPWINDOWPOS pWinPos = (LPWINDOWPOS)lParam;
+ pWinPos->flags |= SWP_NOREDRAW;
+
+ Paint();
+ }
+
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ RecalcLayout();
+ break;
+ }
+
+ // pass unhandled messages on for default processing
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+ // Wrappers for Win32 Macros
+ inline void CTab::AdjustRect(BOOL fLarger, RECT *prc) const
+ {
+ assert(::IsWindow(m_hWnd));
+ TabCtrl_AdjustRect(m_hWnd, fLarger, prc);
+ }
+
+ inline int CTab::GetCurFocus() const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_GetCurFocus(m_hWnd);
+ }
+
+ inline int CTab::GetCurSel() const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_GetCurSel(m_hWnd);
+ }
+
+ inline BOOL CTab::GetItem(int iItem, LPTCITEM pitem) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_GetItem(m_hWnd, iItem, pitem);
+ }
+
+ inline int CTab::GetItemCount() const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_GetItemCount(m_hWnd);
+ }
+
+ inline int CTab::HitTest(TCHITTESTINFO& info) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_HitTest(m_hWnd, &info);
+ }
+
+ inline void CTab::SetCurFocus(int iItem) const
+ {
+ assert(::IsWindow(m_hWnd));
+ TabCtrl_SetCurFocus(m_hWnd, iItem);
+ }
+
+ inline int CTab::SetCurSel(int iItem) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_SetCurSel(m_hWnd, iItem);
+ }
+
+ inline DWORD CTab::SetItemSize(int cx, int cy) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_SetItemSize(m_hWnd, cx, cy);
+ }
+
+ inline int CTab::SetMinTabWidth(int cx) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return TabCtrl_SetMinTabWidth(m_hWnd, cx);
+ }
+
+ inline void CTab::SetPadding(int cx, int cy) const
+ {
+ assert(::IsWindow(m_hWnd));
+ TabCtrl_SetPadding(m_hWnd, cx, cy);
+ }
+
+ ////////////////////////////////////////
+ // Definitions for the CTabbedMDI class
+ inline CTabbedMDI::CTabbedMDI()
+ {
+ GetTab().SetShowButtons(TRUE);
+ }
+
+ inline CTabbedMDI::~CTabbedMDI()
+ {
+ }
+
+ inline CWnd* CTabbedMDI::AddMDIChild(CWnd* pView, LPCTSTR szTabText, int idMDIChild /*= 0*/)
+ {
+ assert(pView);
+ assert(lstrlen(szTabText) < MAX_MENU_STRING);
+
+ GetTab().AddTabPage(WndPtr(pView), szTabText, 0, idMDIChild);
+
+ // Fake a WM_MOUSEACTIVATE to propogate focus change to dockers
+ if (IsWindow())
+ GetParent()->SendMessage(WM_MOUSEACTIVATE, (WPARAM)GetAncestor(), MAKELPARAM(HTCLIENT,WM_LBUTTONDOWN));
+
+ return pView;
+ }
+
+ inline void CTabbedMDI::CloseActiveMDI()
+ {
+ int nTab = GetTab().GetCurSel();
+ if (nTab >= 0)
+ GetTab().RemoveTabPage(nTab);
+
+ RecalcLayout();
+ }
+
+ inline void CTabbedMDI::CloseAllMDIChildren()
+ {
+ while (GetMDIChildCount() > 0)
+ {
+ GetTab().RemoveTabPage(0);
+ }
+ }
+
+ inline void CTabbedMDI::CloseMDIChild(int nTab)
+ {
+ GetTab().RemoveTabPage(nTab);
+
+ if (GetActiveMDIChild())
+ GetActiveMDIChild()->RedrawWindow();
+ }
+
+ inline HWND CTabbedMDI::Create(CWnd* pParent /* = NULL*/)
+ {
+ CLIENTCREATESTRUCT clientcreate ;
+ clientcreate.hWindowMenu = m_hWnd;
+ clientcreate.idFirstChild = IDW_FIRSTCHILD ;
+ DWORD dwStyle = WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES;
+
+ // Create the MDICLIENT view window
+ if (!CreateEx(0, _T("MDICLIENT"), _T(""),
+ dwStyle, 0, 0, 0, 0, pParent, NULL, (PSTR) &clientcreate))
+ throw CWinException(_T("CMDIClient::Create ... CreateEx failed"));
+
+ return m_hWnd;
+ }
+
+ inline CWnd* CTabbedMDI::GetActiveMDIChild() const
+ {
+ CWnd* pView = NULL;
+ int nTab = GetTab().GetCurSel();
+ if (nTab >= 0)
+ {
+ TabPageInfo tbi = GetTab().GetTabPageInfo(nTab);
+ pView = tbi.pView;
+ }
+
+ return pView;
+ }
+
+ inline int CTabbedMDI::GetActiveMDITab() const
+ {
+ return GetTab().GetCurSel();
+ }
+
+ inline CWnd* CTabbedMDI::GetMDIChild(int nTab) const
+ {
+ assert(nTab >= 0);
+ assert(nTab < GetMDIChildCount());
+ return GetTab().GetTabPageInfo(nTab).pView;
+ }
+
+ inline int CTabbedMDI::GetMDIChildCount() const
+ {
+ return (int) GetTab().GetAllTabs().size();
+ }
+
+ inline int CTabbedMDI::GetMDIChildID(int nTab) const
+ {
+ assert(nTab >= 0);
+ assert(nTab < GetMDIChildCount());
+ return GetTab().GetTabPageInfo(nTab).idTab;
+ }
+
+ inline LPCTSTR CTabbedMDI::GetMDIChildTitle(int nTab) const
+ {
+ assert(nTab >= 0);
+ assert(nTab < GetMDIChildCount());
+ return GetTab().GetTabPageInfo(nTab).szTabText;
+ }
+
+ inline BOOL CTabbedMDI::LoadRegistrySettings(tString tsRegistryKeyName)
+ {
+ BOOL bResult = FALSE;
+
+ if (0 != tsRegistryKeyName.size())
+ {
+ tString tsKey = _T("Software\\") + tsRegistryKeyName + _T("\\MDI Children");
+ HKEY hKey = 0;
+ RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey);
+ if (hKey)
+ {
+ DWORD dwType = REG_BINARY;
+ DWORD BufferSize = sizeof(TabPageInfo);
+ TabPageInfo tbi = {0};
+ int i = 0;
+ TCHAR szNumber[16];
+ tString tsSubKey = _T("MDI Child ");
+ tsSubKey += _itot(i, szNumber, 10);
+
+ // Fill the DockList vector from the registry
+ while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&tbi, &BufferSize))
+ {
+ CWnd* pWnd = NewMDIChildFromID(tbi.idTab);
+ if (pWnd)
+ {
+ AddMDIChild(pWnd, tbi.szTabText, tbi.idTab);
+ i++;
+ tsSubKey = _T("MDI Child ");
+ tsSubKey += _itot(i, szNumber, 10);
+ bResult = TRUE;
+ }
+ else
+ {
+ TRACE(_T("Failed to get TabbedMDI info from registry"));
+ bResult = FALSE;
+ break;
+ }
+ }
+
+ // Load Active MDI Tab from the registry
+ tsSubKey = _T("Active MDI Tab");
+ int nTab;
+ dwType = REG_DWORD;
+ BufferSize = sizeof(int);
+ if(ERROR_SUCCESS == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&nTab, &BufferSize))
+ SetActiveMDITab(nTab);
+ else
+ SetActiveMDITab(0);
+
+ RegCloseKey(hKey);
+ }
+ }
+
+ if (!bResult)
+ CloseAllMDIChildren();
+
+ return bResult;
+ }
+
+ inline CWnd* CTabbedMDI::NewMDIChildFromID(int /*idMDIChild*/)
+ {
+ // Override this function to create new MDI children from IDs as shown below
+ CWnd* pView = NULL;
+ /* switch(idTab)
+ {
+ case ID_SIMPLE:
+ pView = new CViewSimple;
+ break;
+ case ID_RECT:
+ pView = new CViewRect;
+ break;
+ default:
+ TRACE(_T("Unknown MDI child ID\n"));
+ break;
+ } */
+
+ return pView;
+ }
+
+ inline void CTabbedMDI::OnCreate()
+ {
+ GetTab().Create(this);
+ GetTab().SetFixedWidth(TRUE);
+ GetTab().SetOwnerDraw(TRUE);
+ }
+
+ inline void CTabbedMDI::OnDestroy(WPARAM /*wParam*/, LPARAM /*lParam*/ )
+ {
+ CloseAllMDIChildren();
+ }
+
+ inline LRESULT CTabbedMDI::OnNotify(WPARAM /*wParam*/, LPARAM lParam)
+ {
+ LPNMHDR pnmhdr = (LPNMHDR)lParam;
+ if (pnmhdr->code == UWM_TAB_CHANGED)
+ RecalcLayout();
+
+ return 0L;
+ }
+
+ inline void CTabbedMDI::OnWindowPosChanged(WPARAM /*wParam*/, LPARAM /*lParam*/)
+ {
+ RecalcLayout();
+ }
+
+ inline void CTabbedMDI::RecalcLayout()
+ {
+ if (GetTab().IsWindow())
+ {
+ if (GetTab().GetItemCount() >0)
+ {
+ CRect rcClient = GetClientRect();
+ GetTab().SetWindowPos(NULL, rcClient, SWP_SHOWWINDOW);
+ GetTab().UpdateWindow();
+ }
+ else
+ {
+ CRect rcClient = GetClientRect();
+ GetTab().SetWindowPos(NULL, rcClient, SWP_HIDEWINDOW);
+ Invalidate();
+ }
+ }
+ }
+
+ inline BOOL CTabbedMDI::SaveRegistrySettings(tString tsRegistryKeyName)
+ {
+ if (0 != tsRegistryKeyName.size())
+ {
+ tString tsKeyName = _T("Software\\") + tsRegistryKeyName;
+ HKEY hKey = NULL;
+ HKEY hKeyMDIChild = NULL;
+
+ try
+ {
+ if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL))
+ throw (CWinException(_T("RegCreateKeyEx Failed")));
+
+ RegDeleteKey(hKey, _T("MDI Children"));
+ if (ERROR_SUCCESS != RegCreateKeyEx(hKey, _T("MDI Children"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyMDIChild, NULL))
+ throw (CWinException(_T("RegCreateKeyEx Failed")));
+
+ for (int i = 0; i < GetMDIChildCount(); ++i)
+ {
+ TCHAR szNumber[16];
+ tString tsSubKey = _T("MDI Child ");
+ tsSubKey += _itot(i, szNumber, 10);
+ TabPageInfo pdi = GetTab().GetTabPageInfo(i);
+ if (ERROR_SUCCESS != RegSetValueEx(hKeyMDIChild, tsSubKey.c_str(), 0, REG_BINARY, (LPBYTE)&pdi, sizeof(TabPageInfo)))
+ throw (CWinException(_T("RegSetValueEx Failed")));
+ }
+
+ // Add Active Tab to the registry
+ tString tsSubKey = _T("Active MDI Tab");
+ int nTab = GetActiveMDITab();
+ if(ERROR_SUCCESS != RegSetValueEx(hKeyMDIChild, tsSubKey.c_str(), 0, REG_DWORD, (LPBYTE)&nTab, sizeof(int)))
+ throw (CWinException(_T("RegSetValueEx failed")));
+
+ RegCloseKey(hKeyMDIChild);
+ RegCloseKey(hKey);
+ }
+ catch (const CWinException& e)
+ {
+ // Roll back the registry changes by deleting the subkeys
+ if (hKey)
+ {
+ if (hKeyMDIChild)
+ {
+ RegDeleteKey(hKeyMDIChild, _T("MDI Children"));
+ RegCloseKey(hKeyMDIChild);
+ }
+
+ RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str());
+ RegCloseKey(hKey);
+ }
+
+ e.what();
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ inline void CTabbedMDI::SetActiveMDIChild(CWnd* pWnd)
+ {
+ assert(pWnd);
+ int nPage = GetTab().GetTabIndex(pWnd);
+ if (nPage >= 0)
+ GetTab().SelectPage(nPage);
+ }
+
+ inline void CTabbedMDI::SetActiveMDITab(int iTab)
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(GetTab().IsWindow());
+ GetTab().SelectPage(iTab);
+ }
+
+ inline LRESULT CTabbedMDI::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ OnDestroy(wParam, lParam);
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ OnWindowPosChanged(wParam, lParam);
+ break;
+ }
+
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_TAB_H_
diff --git a/mmc_updater/depends/win32cpp/taskdialog.h b/mmc_updater/depends/win32cpp/taskdialog.h
new file mode 100644
index 00000000..2285d1e1
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/taskdialog.h
@@ -0,0 +1,811 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////
+// taskdialog.h
+// Declaration of the CTaskDialog class
+
+// A task dialog is a dialog box that can be used to display information
+// and receive simple input from the user. Like a message box, it is
+// formatted by the operating system according to parameters you set.
+// However, a task dialog has many more features than a message box.
+
+// NOTES:
+// Task Dialogs are only supported on Windows Vista and above.
+// Task Dialogs require XP themes enabled (use version 6 of Common Controls)
+// Task Dialogs are always modal.
+
+
+#ifndef _WIN32XX_TASKDIALOG_H_
+#define _WIN32XX_TASKDIALOG_H_
+
+#include "wincore.h"
+
+namespace Win32xx
+{
+
+ class CTaskDialog : public CWnd
+ {
+ public:
+ CTaskDialog();
+ virtual ~CTaskDialog() {}
+
+ void AddCommandControl(int nButtonID, LPCTSTR pszCaption);
+ void AddRadioButton(int nRadioButtonID, LPCTSTR pszCaption);
+ void AddRadioButtonGroup(int nIDRadioButtonsFirst, int nIDRadioButtonsLast);
+ void ClickButton(int nButtonID) const;
+ void ClickRadioButton(int nRadioButtonID) const;
+ LRESULT DoModal(CWnd* pParent = NULL);
+ void ElevateButton(int nButtonID, BOOL bElevated);
+ void EnableButton(int nButtonID, BOOL bEnabled);
+ void EnableRadioButton(int nButtonID, BOOL bEnabled);
+ TASKDIALOGCONFIG GetConfig() const;
+ TASKDIALOG_FLAGS GetOptions() const;
+ int GetSelectedButtonID() const;
+ int GetSelectedRadioButtonID() const;
+ BOOL GetVerificationCheckboxState() const;
+ static BOOL IsSupported();
+ void NavigateTo(CTaskDialog& TaskDialog) const;
+ void RemoveAllButtons();
+ void RemoveAllRadioButtons();
+ void Reset();
+ void SetCommonButtons(TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons);
+ void SetContent(LPCTSTR pszContent);
+ void SetDefaultButton(int nButtonID);
+ void SetDefaultRadioButton(int nRadioButtonID);
+ void SetDialogWidth(UINT nWidth = 0);
+ void SetExpansionArea(LPCTSTR pszExpandedInfo, LPCTSTR pszExpandedLabel = _T(""), LPCTSTR pszCollapsedLabel = _T(""));
+ void SetFooterIcon(HICON hFooterIcon);
+ void SetFooterIcon(LPCTSTR lpszFooterIcon);
+ void SetFooterText(LPCTSTR pszFooter);
+ void SetMainIcon(HICON hMainIcon);
+ void SetMainIcon(LPCTSTR lpszMainIcon);
+ void SetMainInstruction(LPCTSTR pszMainInstruction);
+ void SetOptions(TASKDIALOG_FLAGS dwFlags);
+ void SetProgressBarMarquee(BOOL bEnabled = TRUE, int nMarqueeSpeed = 0);
+ void SetProgressBarPosition(int nProgressPos);
+ void SetProgressBarRange(int nMinRange, int nMaxRange);
+ void SetProgressBarState(int nNewState = PBST_NORMAL);
+ void SetVerificationCheckbox(BOOL bChecked);
+ void SetVerificationCheckboxText(LPCTSTR pszVerificationText);
+ void SetWindowTitle(LPCTSTR pszWindowTitle);
+ static HRESULT CALLBACK StaticTaskDialogProc(HWND hWnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData);
+ void StoreText(std::vector<WCHAR>& vWChar, LPCTSTR pFromTChar);
+ void UpdateElementText(TASKDIALOG_ELEMENTS eElement, LPCTSTR pszNewText);
+
+
+ protected:
+ // Override these functions as required
+ virtual BOOL OnTDButtonClicked(int nButtonID);
+ virtual void OnTDConstructed();
+ virtual void OnTDCreated();
+ virtual void OnTDDestroyed();
+ virtual void OnTDExpandButtonClicked(BOOL bExpanded);
+ virtual void OnTDHelp();
+ virtual void OnTDHyperlinkClicked(LPCTSTR pszHref);
+ virtual void OnTDNavigatePage();
+ virtual BOOL OnTDRadioButtonClicked(int nRadioButtonID);
+ virtual BOOL OnTDTimer(DWORD dwTickCount);
+ virtual void OnTDVerificationCheckboxClicked(BOOL bChecked);
+ virtual LRESULT TaskDialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual LRESULT TaskDialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CTaskDialog(const CTaskDialog&); // Disable copy construction
+ CTaskDialog& operator = (const CTaskDialog&); // Disable assignment operator
+
+ std::vector<TASKDIALOG_BUTTON> m_vButtons;
+ std::vector<TASKDIALOG_BUTTON> m_vRadioButtons;
+
+ std::vector< std::vector<WCHAR> > m_vButtonsText; // A vector of WCHAR vectors
+ std::vector< std::vector<WCHAR> > m_vRadioButtonsText; // A vector of WCHAR vectors
+
+ std::vector<WCHAR> m_vWindowTitle;
+ std::vector<WCHAR> m_vMainInstruction;
+ std::vector<WCHAR> m_vContent;
+ std::vector<WCHAR> m_vVerificationText;
+ std::vector<WCHAR> m_vExpandedInformation;
+ std::vector<WCHAR> m_vExpandedControlText;
+ std::vector<WCHAR> m_vCollapsedControlText;
+ std::vector<WCHAR> m_vFooter;
+
+ TASKDIALOGCONFIG m_tc;
+ int m_SelectedButtonID;
+ int m_SelectedRadioButtonID;
+ BOOL m_VerificationCheckboxState;
+ };
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ inline CTaskDialog::CTaskDialog() : m_SelectedButtonID(0), m_SelectedRadioButtonID(0), m_VerificationCheckboxState(FALSE)
+ {
+ ZeroMemory(&m_tc, sizeof(m_tc));
+ m_tc.cbSize = sizeof(m_tc);
+ m_tc.pfCallback = CTaskDialog::StaticTaskDialogProc;
+ }
+
+ inline void CTaskDialog::AddCommandControl(int nButtonID, LPCTSTR pszCaption)
+ // Adds a command control or push button to the Task Dialog.
+ {
+ assert (m_hWnd == NULL);
+
+ std::vector<WCHAR> vButtonText;
+ StoreText(vButtonText, pszCaption);
+ m_vButtonsText.push_back(vButtonText); // m_vButtonsText is a vector of vector<WCHAR>'s
+
+ TASKDIALOG_BUTTON tdb;
+ tdb.nButtonID = nButtonID;
+ tdb.pszButtonText = &m_vButtonsText.back().front();
+
+ m_vButtons.push_back(tdb);
+ }
+
+ inline void CTaskDialog::AddRadioButton(int nRadioButtonID, LPCTSTR pszCaption)
+ // Adds a radio button to the Task Dialog.
+ {
+ assert (m_hWnd == NULL);
+
+ std::vector<WCHAR> vRadioButtonText;
+ StoreText(vRadioButtonText, pszCaption);
+ m_vRadioButtonsText.push_back(vRadioButtonText); // m_vRadioButtonsText is a vector of vector<WCHAR>'s
+
+ TASKDIALOG_BUTTON tdb;
+ tdb.nButtonID = nRadioButtonID;
+ tdb.pszButtonText = &m_vRadioButtonsText.back().front();
+
+ m_vRadioButtons.push_back(tdb);
+ }
+
+ inline void CTaskDialog::AddRadioButtonGroup(int nIDRadioButtonsFirst, int nIDRadioButtonsLast)
+ // Adds a range of radio buttons to the Task Dialog.
+ // Assumes the resource ID of the button and it's string match
+ {
+ assert (m_hWnd == NULL);
+ assert(nIDRadioButtonsFirst > 0);
+ assert(nIDRadioButtonsLast > nIDRadioButtonsFirst);
+
+ TASKDIALOG_BUTTON tdb;
+ for (int nID = nIDRadioButtonsFirst; nID <= nIDRadioButtonsLast; ++nID)
+ {
+ tdb.nButtonID = nID;
+ tdb.pszButtonText = MAKEINTRESOURCEW(nID);
+ m_vRadioButtons.push_back(tdb);
+ }
+ }
+
+ inline void CTaskDialog::ClickButton(int nButtonID) const
+ // Simulates the action of a button click in the Task Dialog.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_CLICK_BUTTON, (WPARAM)nButtonID, 0);
+ }
+
+ inline void CTaskDialog::ClickRadioButton(int nRadioButtonID) const
+ // Simulates the action of a radio button click in the TaskDialog.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_CLICK_RADIO_BUTTON, (WPARAM)nRadioButtonID, 0);
+ }
+
+ inline LRESULT CTaskDialog::DoModal(CWnd* pParent /* = NULL */)
+ // Creates and displays the Task Dialog.
+ {
+ assert (m_hWnd == NULL);
+
+ m_tc.cbSize = sizeof(m_tc);
+ m_tc.pButtons = m_vButtons.empty()? NULL : &m_vButtons.front();
+ m_tc.cButtons = m_vButtons.size();
+ m_tc.pRadioButtons = m_vRadioButtons.empty()? NULL : &m_vRadioButtons.front();
+ m_tc.cRadioButtons = m_vRadioButtons.size();
+ m_tc.hwndParent = pParent? pParent->GetHwnd() : NULL;
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+
+ // Store the CWnd pointer in thread local storage
+ pTLSData->pCWnd = this;
+
+ // Declare a pointer to the TaskDialogIndirect function
+ HMODULE hComCtl = ::LoadLibrary(_T("COMCTL32.DLL"));
+ assert(hComCtl);
+ typedef HRESULT WINAPI TASKDIALOGINDIRECT(const TASKDIALOGCONFIG*, int*, int*, BOOL*);
+ TASKDIALOGINDIRECT* pTaskDialogIndirect = (TASKDIALOGINDIRECT*)::GetProcAddress(hComCtl, "TaskDialogIndirect");
+
+ // Call TaskDialogIndirect through our function pointer
+ LRESULT lr = (*pTaskDialogIndirect)(&m_tc, &m_SelectedButtonID, &m_SelectedRadioButtonID, &m_VerificationCheckboxState);
+
+ FreeLibrary(hComCtl);
+ return lr;
+ }
+
+ inline void CTaskDialog::ElevateButton(int nButtonID, BOOL bElevated)
+ // Adds a shield icon to indicate that the button's action requires elevated privilages.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, (WPARAM)nButtonID, (LPARAM)bElevated);
+ }
+
+ inline void CTaskDialog::EnableButton(int nButtonID, BOOL bEnabled)
+ // Enables or disables a push button in the TaskDialog.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_ENABLE_BUTTON, (WPARAM)nButtonID, (LPARAM)bEnabled);
+ }
+ inline void CTaskDialog::EnableRadioButton(int nRadioButtonID, BOOL bEnabled)
+ // Enables or disables a radio button in the TaskDialog.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_ENABLE_RADIO_BUTTON, (WPARAM)nRadioButtonID, (LPARAM)bEnabled);
+ }
+
+ inline TASKDIALOGCONFIG CTaskDialog::GetConfig() const
+ // Returns the TASKDIALOGCONFIG structure for the Task Dialog.
+ {
+ return m_tc;
+ }
+
+ inline TASKDIALOG_FLAGS CTaskDialog::GetOptions() const
+ // Returns the Task Dialog's options. These are a combination of:
+ // TDF_ENABLE_HYPERLINKS
+ // TDF_USE_HICON_MAIN
+ // TDF_USE_HICON_FOOTER
+ // TDF_ALLOW_DIALOG_CANCELLATION
+ // TDF_USE_COMMAND_LINKS
+ // TDF_USE_COMMAND_LINKS_NO_ICON
+ // TDF_EXPAND_FOOTER_AREA
+ // TDF_EXPANDED_BY_DEFAULT
+ // TDF_VERIFICATION_FLAG_CHECKED
+ // TDF_SHOW_PROGRESS_BAR
+ // TDF_SHOW_MARQUEE_PROGRESS_BAR
+ // TDF_CALLBACK_TIMER
+ // TDF_POSITION_RELATIVE_TO_WINDOW
+ // TDF_RTL_LAYOUT
+ // TDF_NO_DEFAULT_RADIO_BUTTON
+ // TDF_CAN_BE_MINIMIZED
+ {
+ return m_tc.dwFlags;
+ }
+
+ inline int CTaskDialog::GetSelectedButtonID() const
+ // Returns the ID of the selected button.
+ {
+ assert (m_hWnd == NULL);
+ return m_SelectedButtonID;
+ }
+
+ inline int CTaskDialog::GetSelectedRadioButtonID() const
+ // Returns the ID of the selected radio button.
+ {
+ assert (m_hWnd == NULL);
+ return m_SelectedRadioButtonID;
+ }
+
+ inline BOOL CTaskDialog::GetVerificationCheckboxState() const
+ // Returns the state of the verification check box.
+ {
+ assert (m_hWnd == NULL);
+ return m_VerificationCheckboxState;
+ }
+
+ inline BOOL CTaskDialog::IsSupported()
+ // Returns true if TaskDialogs are supported on this system.
+ {
+ HMODULE hModule = ::LoadLibrary(_T("COMCTL32.DLL"));
+ assert(hModule);
+
+ BOOL bResult = (BOOL)::GetProcAddress(hModule, "TaskDialogIndirect");
+
+ ::FreeLibrary(hModule);
+ return bResult;
+ }
+
+ inline void CTaskDialog::NavigateTo(CTaskDialog& TaskDialog) const
+ // Replaces the information displayed by the task dialog.
+ {
+ assert(m_hWnd);
+ TASKDIALOGCONFIG tc = TaskDialog.GetConfig();
+ SendMessage(TDM_NAVIGATE_PAGE, 0, (LPARAM)&tc);
+ }
+
+ inline BOOL CTaskDialog::OnTDButtonClicked(int nButtonID)
+ // Called when the user selects a button or command link.
+ {
+ UNREFERENCED_PARAMETER(nButtonID);
+
+ // return TRUE to prevent the task dialog from closing
+ return FALSE;
+ }
+
+ inline void CTaskDialog::OnTDConstructed()
+ // Called when the task dialog is constructed, before it is displayed.
+ {}
+
+ inline void CTaskDialog::OnTDCreated()
+ // Called when the task dialog is displayed.
+ {}
+
+ inline void CTaskDialog::OnTDDestroyed()
+ // Called when the task dialog is destroyed.
+ {
+ }
+
+ inline void CTaskDialog::OnTDExpandButtonClicked(BOOL bExpanded)
+ // Called when the expand button is clicked.
+ {
+ UNREFERENCED_PARAMETER(bExpanded);
+ }
+
+ inline void CTaskDialog::OnTDHelp()
+ // Called when the user presses F1 on the keyboard.
+ {}
+
+ inline void CTaskDialog::OnTDHyperlinkClicked(LPCTSTR pszHref)
+ // Called when the user clicks on a hyperlink.
+ {
+ UNREFERENCED_PARAMETER(pszHref);
+ }
+
+ inline void CTaskDialog::OnTDNavigatePage()
+ // Called when a navigation has occurred.
+ {}
+
+ inline BOOL CTaskDialog::OnTDRadioButtonClicked(int nRadioButtonID)
+ // Called when the user selects a radio button.
+ {
+ UNREFERENCED_PARAMETER(nRadioButtonID);
+ return TRUE;
+ }
+
+ inline BOOL CTaskDialog::OnTDTimer(DWORD dwTickCount)
+ // Called every 200 milliseconds (aproximately) when the TDF_CALLBACK_TIMER flag is set.
+ {
+ UNREFERENCED_PARAMETER(dwTickCount);
+
+ // return TRUE to reset the tick count
+ return FALSE;
+ }
+
+ inline void CTaskDialog::OnTDVerificationCheckboxClicked(BOOL bChecked)
+ // Called when the user clicks the Task Dialog verification check box.
+ {
+ UNREFERENCED_PARAMETER(bChecked);
+ }
+
+ inline void CTaskDialog::RemoveAllButtons()
+ // Removes all push buttons from the task dialog.
+ {
+ assert (m_hWnd == NULL);
+ m_vButtons.clear();
+ m_vButtonsText.clear();
+ }
+
+ inline void CTaskDialog::RemoveAllRadioButtons()
+ // Removes all radio buttons from the task dialog.
+ {
+ assert (m_hWnd == NULL);
+ m_vRadioButtons.clear();
+ m_vRadioButtonsText.clear();
+ }
+
+ inline void CTaskDialog::Reset()
+ // Returns the dialog to its default state.
+ {
+ assert (m_hWnd == NULL);
+
+ RemoveAllButtons();
+ RemoveAllRadioButtons();
+ ZeroMemory(&m_tc, sizeof(m_tc));
+ m_tc.cbSize = sizeof(m_tc);
+ m_tc.pfCallback = CTaskDialog::StaticTaskDialogProc;
+
+ m_SelectedButtonID = 0;
+ m_SelectedRadioButtonID = 0;
+ m_VerificationCheckboxState = FALSE;
+
+ m_vWindowTitle.clear();
+ m_vMainInstruction.clear();
+ m_vContent.clear();
+ m_vVerificationText.clear();
+ m_vExpandedInformation.clear();
+ m_vExpandedControlText.clear();
+ m_vCollapsedControlText.clear();
+ m_vFooter.clear();
+ }
+
+ inline void CTaskDialog::SetCommonButtons(TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons)
+ // The dwCommonButtons parameter can be a combination of:
+ // TDCBF_OK_BUTTON OK button
+ // TDCBF_YES_BUTTON Yes button
+ // TDCBF_NO_BUTTON No button
+ // TDCBF_CANCEL_BUTTON Cancel button
+ // TDCBF_RETRY_BUTTON Retry button
+ // TDCBF_CLOSE_BUTTON Close button
+ {
+ assert (m_hWnd == NULL);
+ m_tc.dwCommonButtons = dwCommonButtons;
+ }
+
+ inline void CTaskDialog::SetContent(LPCTSTR pszContent)
+ // Sets the task dialog's primary content.
+ {
+ StoreText(m_vContent, pszContent);
+ m_tc.pszContent = &m_vContent.front();
+
+ if (IsWindow())
+ SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_CONTENT, (LPARAM)(LPCWSTR)T2W(pszContent));
+ }
+
+ inline void CTaskDialog::SetDefaultButton(int nButtonID)
+ // Sets the task dialog's default button.
+ // Can be either a button ID or one of the common buttons
+ {
+ assert (m_hWnd == NULL);
+ m_tc.nDefaultButton = nButtonID;
+ }
+
+ inline void CTaskDialog::SetDefaultRadioButton(int nRadioButtonID)
+ // Sets the default radio button.
+ {
+ assert (m_hWnd == NULL);
+ m_tc.nDefaultRadioButton = nRadioButtonID;
+ }
+
+ inline void CTaskDialog::SetDialogWidth(UINT nWidth /*= 0*/)
+ // The width of the task dialog's client area. If 0, the
+ // task dialog manager will calculate the ideal width.
+ {
+ assert (m_hWnd == NULL);
+ m_tc.cxWidth = nWidth;
+ }
+
+ inline void CTaskDialog::SetExpansionArea(LPCTSTR pszExpandedInfo, LPCTSTR pszExpandedLabel /* = _T("")*/, LPCTSTR pszCollapsedLabel /* = _T("")*/)
+ // Sets the text in the expandable area of the Task Dialog.
+ {
+ StoreText(m_vExpandedInformation, pszExpandedInfo);
+ m_tc.pszExpandedInformation = &m_vExpandedInformation.front();
+
+ StoreText(m_vExpandedControlText, pszExpandedLabel);
+ m_tc.pszExpandedControlText = &m_vExpandedControlText.front();
+
+ StoreText(m_vCollapsedControlText, pszCollapsedLabel);
+ m_tc.pszCollapsedControlText = &m_vCollapsedControlText.front();
+
+ if (IsWindow())
+ SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_EXPANDED_INFORMATION, (LPARAM)(LPCWSTR)T2W(pszExpandedInfo));
+ }
+
+ inline void CTaskDialog::SetFooterIcon(HICON hFooterIcon)
+ // Sets the icon that will be displayed in the Task Dialog's footer.
+ {
+ m_tc.hFooterIcon = hFooterIcon;
+
+ if (IsWindow())
+ SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_FOOTER, (LPARAM)hFooterIcon);
+ }
+
+ inline void CTaskDialog::SetFooterIcon(LPCTSTR lpszFooterIcon)
+ // Sets the icon that will be displayed in the Task Dialog's footer.
+ // Possible icons:
+ // TD_ERROR_ICON A stop-sign icon appears in the task dialog.
+ // TD_WARNING_ICON An exclamation-point icon appears in the task dialog.
+ // TD_INFORMATION_ICON An icon consisting of a lowercase letter i in a circle appears in the task dialog.
+ // TD_SHIELD_ICON A shield icon appears in the task dialog.
+ // or a value passed via MAKEINTRESOURCE
+ {
+ m_tc.pszFooterIcon = (LPCWSTR)lpszFooterIcon;
+
+ if (IsWindow())
+ SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_FOOTER, (LPARAM)lpszFooterIcon);
+ }
+
+ inline void CTaskDialog::SetFooterText(LPCTSTR pszFooter)
+ // Sets the text that will be displayed in the Task Dialog's footer.
+ {
+ StoreText(m_vFooter, pszFooter);
+ m_tc.pszFooter = &m_vFooter.front();
+
+ if (IsWindow())
+ SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_FOOTER, (LPARAM)(LPCWSTR)T2W(pszFooter));
+ }
+
+ inline void CTaskDialog::SetMainIcon(HICON hMainIcon)
+ // Sets Task Dialog's main icon.
+ {
+ m_tc.hMainIcon = hMainIcon;
+
+ if (IsWindow())
+ SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_MAIN, (LPARAM)hMainIcon);
+ }
+
+ inline void CTaskDialog::SetMainIcon(LPCTSTR lpszMainIcon)
+ // Sets Task Dialog's main icon.
+ // Possible icons:
+ // TD_ERROR_ICON A stop-sign icon appears in the task dialog.
+ // TD_WARNING_ICON An exclamation-point icon appears in the task dialog.
+ // TD_INFORMATION_ICON An icon consisting of a lowercase letter i in a circle appears in the task dialog.
+ // TD_SHIELD_ICON A shield icon appears in the task dialog.
+ // or a value passed via MAKEINTRESOURCE
+ //
+ // Note: Some values of main icon will also generate a MessageBeep when the TaskDialog is created.
+ {
+ m_tc.pszMainIcon = (LPCWSTR)lpszMainIcon;
+
+ if (IsWindow())
+ SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_MAIN, (LPARAM)lpszMainIcon);
+ }
+
+ inline void CTaskDialog::SetMainInstruction(LPCTSTR pszMainInstruction)
+ // Sets the Task Dialog's main instruction text.
+ {
+ StoreText(m_vMainInstruction, pszMainInstruction);
+ m_tc.pszMainInstruction = &m_vMainInstruction.front();
+
+ if (IsWindow())
+ SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_FOOTER, (LPARAM)(LPCWSTR)T2W(pszMainInstruction));
+ }
+
+ inline void CTaskDialog::SetOptions(TASKDIALOG_FLAGS dwFlags)
+ // Sets the Task Dialog's options. These are a combination of:
+ // TDF_ENABLE_HYPERLINKS
+ // TDF_USE_HICON_MAIN
+ // TDF_USE_HICON_FOOTER
+ // TDF_ALLOW_DIALOG_CANCELLATION
+ // TDF_USE_COMMAND_LINKS
+ // TDF_USE_COMMAND_LINKS_NO_ICON
+ // TDF_EXPAND_FOOTER_AREA
+ // TDF_EXPANDED_BY_DEFAULT
+ // TDF_VERIFICATION_FLAG_CHECKED
+ // TDF_SHOW_PROGRESS_BAR
+ // TDF_SHOW_MARQUEE_PROGRESS_BAR
+ // TDF_CALLBACK_TIMER
+ // TDF_POSITION_RELATIVE_TO_WINDOW
+ // TDF_RTL_LAYOUT
+ // TDF_NO_DEFAULT_RADIO_BUTTON
+ // TDF_CAN_BE_MINIMIZED
+ {
+ assert (m_hWnd == NULL);
+ m_tc.dwFlags = dwFlags;
+ }
+
+ inline void CTaskDialog::SetProgressBarMarquee(BOOL bEnabled /* = TRUE*/, int nMarqueeSpeed /* = 0*/)
+ // Starts and stops the marquee display of the progress bar, and sets the speed of the marquee.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_SET_PROGRESS_BAR_MARQUEE, (WPARAM)bEnabled, (LPARAM)nMarqueeSpeed);
+ }
+
+ inline void CTaskDialog::SetProgressBarPosition(int nProgressPos)
+ // Sets the current position for a progress bar.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_SET_PROGRESS_BAR_POS, (WPARAM)nProgressPos, 0);
+ }
+
+ inline void CTaskDialog::SetProgressBarRange(int nMinRange, int nMaxRange)
+ // Sets the minimum and maximum values for the hosted progress bar.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(nMinRange, nMaxRange));
+ }
+
+ inline void CTaskDialog::SetProgressBarState(int nNewState /* = PBST_NORMAL*/)
+ // Sets the current state of the progress bar. Possible states are:
+ // PBST_NORMAL
+ // PBST_PAUSE
+ // PBST_ERROR
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_SET_PROGRESS_BAR_STATE, (WPARAM)nNewState, 0);
+ }
+
+ inline void CTaskDialog::SetVerificationCheckbox(BOOL bChecked)
+ // Simulates a click on the verification checkbox of the Task Dialog, if it exists.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_CLICK_VERIFICATION, (WPARAM)bChecked, (LPARAM)bChecked);
+ }
+
+ inline void CTaskDialog::SetVerificationCheckboxText(LPCTSTR pszVerificationText)
+ // Sets the text for the verification check box.
+ {
+ assert (m_hWnd == NULL);
+ StoreText(m_vVerificationText, pszVerificationText);
+ m_tc.pszVerificationText = &m_vVerificationText.front();
+ }
+
+ inline void CTaskDialog::SetWindowTitle(LPCTSTR pszWindowTitle)
+ // Sets the Task Dialog's window title.
+ {
+ assert (m_hWnd == NULL);
+ StoreText(m_vWindowTitle, pszWindowTitle);
+ m_tc.pszWindowTitle = &m_vWindowTitle.front();
+ }
+
+ inline HRESULT CALLBACK CTaskDialog::StaticTaskDialogProc(HWND hWnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData)
+ // TaskDialogs direct their messages here.
+ {
+ UNREFERENCED_PARAMETER(dwRefData);
+
+ assert( GetApp() );
+
+ try
+ {
+ CTaskDialog* t = (CTaskDialog*)GetApp()->GetCWndFromMap(hWnd);
+ if (0 == t)
+ {
+ // The CTaskDialog pointer wasn't found in the map, so add it now
+
+ // Retrieve the pointer to the TLS Data
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ if (NULL == pTLSData)
+ throw CWinException(_T("Unable to get TLS"));
+
+ // Retrieve pointer to CTaskDialog object from Thread Local Storage TLS
+ t = (CTaskDialog*)(pTLSData->pCWnd);
+ if (NULL == t)
+ throw CWinException(_T("Failed to route message"));
+
+ pTLSData->pCWnd = NULL;
+
+ // Store the CTaskDialog pointer in the HWND map
+ t->m_hWnd = hWnd;
+ t->AddToMap();
+ }
+
+ return t->TaskDialogProc(uNotification, wParam, lParam);
+ }
+
+ catch (const CWinException &e)
+ {
+ // Most CWinExceptions will end up here unless caught earlier.
+ e.what();
+ }
+
+ return 0L;
+
+ } // LRESULT CALLBACK StaticTaskDialogProc(...)
+
+ inline void CTaskDialog::StoreText(std::vector<WCHAR>& vWChar, LPCTSTR pFromTChar)
+ {
+ // Stores a TChar string in a WCHAR vector
+
+ std::vector<TCHAR> vTChar;
+
+ if (IS_INTRESOURCE(pFromTChar)) // support MAKEINTRESOURCE
+ {
+ tString ts = LoadString((UINT)pFromTChar);
+ int len = pFromTChar? ts.length() + 1 : 1;
+ vTChar.assign(len, _T('\0'));
+ vWChar.assign(len, _T('\0'));
+ if (pFromTChar)
+ lstrcpy( &vTChar.front(), ts.c_str());
+
+ }
+ else
+ {
+ int len = lstrlen(pFromTChar) +1;
+ vTChar.assign(len, _T('\0'));
+ vWChar.assign(len, _T('\0'));
+ lstrcpy( &vTChar.front(), pFromTChar);
+ }
+
+ lstrcpyW(&vWChar.front(), T2W(&vTChar.front()) );
+ }
+
+ inline LRESULT CTaskDialog::TaskDialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // Handles the Task Dialog's notificaions.
+ {
+ switch(uMsg)
+ {
+ case TDN_BUTTON_CLICKED:
+ return OnTDButtonClicked((int)wParam);
+
+ case TDN_CREATED:
+ OnTDCreated();
+ break;
+ case TDN_DESTROYED:
+ Cleanup(); // Prepare this CWnd to be reused.
+ OnTDDestroyed();
+ break;
+ case TDN_DIALOG_CONSTRUCTED:
+ OnTDConstructed();
+ break;
+ case TDN_EXPANDO_BUTTON_CLICKED:
+ OnTDExpandButtonClicked((BOOL)wParam);
+ break;
+ case TDN_HELP:
+ OnTDHelp();
+ break;
+ case TDN_HYPERLINK_CLICKED:
+ OnTDHyperlinkClicked(W2T((LPCWSTR)lParam));
+ break;
+ case TDN_NAVIGATED:
+ OnTDNavigatePage();
+ break;
+ case TDN_RADIO_BUTTON_CLICKED:
+ OnTDRadioButtonClicked((int)wParam);
+ break;
+ case TDN_TIMER:
+ return OnTDTimer((DWORD)wParam);
+
+ case TDN_VERIFICATION_CLICKED:
+ OnTDVerificationCheckboxClicked((BOOL)wParam);
+ break;
+ }
+
+ return S_OK;
+ }
+
+ inline LRESULT CTaskDialog::TaskDialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Override this function in your class derrived from CDialog if you wish to handle messages
+ // A typical function might look like this:
+
+ // switch (uMsg)
+ // {
+ // case MESSAGE1: // Some Windows API message
+ // OnMessage1(); // A user defined function
+ // break; // Also do default processing
+ // case MESSAGE2:
+ // OnMessage2();
+ // return x; // Don't do default processing, but instead return
+ // // a value recommended by the Windows API documentation
+ // }
+
+ // Always pass unhandled messages on to TaskDialogProcDefault
+ return TaskDialogProcDefault(uMsg, wParam, lParam);
+ }
+
+ inline void CTaskDialog::UpdateElementText(TASKDIALOG_ELEMENTS eElement, LPCTSTR pszNewText)
+ // Updates a text element on the Task Dialog.
+ {
+ assert(m_hWnd);
+ SendMessage(TDM_UPDATE_ELEMENT_TEXT, (WPARAM)eElement, (LPARAM)(LPCWSTR)T2W(pszNewText));
+ }
+
+}
+
+
+
+#endif // _WIN32XX_TASKDIALOG_H_ \ No newline at end of file
diff --git a/mmc_updater/depends/win32cpp/thread.h b/mmc_updater/depends/win32cpp/thread.h
new file mode 100644
index 00000000..5564d888
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/thread.h
@@ -0,0 +1,241 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+// The CThread class simplifies the use of threads with Win32++.
+// To use threads in your Win32++ application, inherit a class from
+// CThread, and override InitInstance. When your class is instanciated,
+// a new thread is started, and the InitInstance function is called to
+// run in the new thread.
+
+// If your thread is used to run one or more windows, InitInstance should
+// return TRUE, causing the MessageLoop function to be called. If your
+// thread doesn't require a MessageLoop, it should return FALSE. Threads
+// which don't run a message loop as sometimes referred to as "worker" threads.
+
+// Note: It is your job to end the thread before CThread ends!
+// To end a thread with a message loop, use PostQuitMessage on the thread.
+// To end a thread without a message loop, set an event, and end the thread
+// when the event is received.
+
+// Hint: It is never a good idea to use things like TerminateThread or ExitThread to
+// end your thread. These represent poor programming techniques, and are likely
+// to leak memory and resources.
+
+// More Hints for thread programming:
+// 1) Avoid using SendMessage between threads, as this will cause one thread to wait for
+// the other to respond. Use PostMessage between threads to avoid this problem.
+// 2) Access to variables and resources shared between threads need to be made thread safe.
+// Having one thread modify a resouce or variable while another thread is accessing it is
+// a recipe for disaster.
+// 3) Thread Local Storage (TLS) can be used to replace global variables to make them thread
+// safe. With TLS, each thread gets its own copy of the variable.
+// 4) Critical Sections can be used to make shared resources thread safe.
+// 5) Window messages (including user defined messages) can be posted between GUI threads to
+// communicate information between them.
+// 6) Events (created by CreateEvent) can be used to comunicate information between threads
+// (both GUI and worker threads).
+// 7) Avoid using sleep to synchronise threads. Generally speaking, the various wait
+// functions (e.g. WaitForSingleObject) will be better for this.
+
+// About Threads:
+// Each program that executes has a "process" allocated to it. A process has one or more
+// threads. Threads run independantly of each other. It is the job of the operating system
+// to manage the running of the threads, and do the task switching between threads as required.
+// Systems with multiple CPUs will be able to run as many threads simultaneously as there are
+// CPUs.
+
+// Threads behave like a program within a program. When the main thread starts, the application
+// runs the WinMain function and ends when WinMain ends. When another thread starts, it too
+// will run the function provided to it, and end when that function ends.
+
+
+#ifndef _WIN32XX_WINTHREAD_H_
+#define _WIN32XX_WINTHREAD_H_
+
+
+#include <process.h>
+
+
+namespace Win32xx
+{
+
+ //////////////////////////////////////
+ // Declaration of the CThread class
+ //
+ class CThread
+ {
+ public:
+ CThread();
+ CThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag);
+ virtual ~CThread();
+
+ // Overridables
+ virtual BOOL InitInstance();
+ virtual int MessageLoop();
+
+ // Operations
+ HANDLE GetThread() const;
+ int GetThreadID() const;
+ int GetThreadPriority() const;
+ DWORD ResumeThread() const;
+ BOOL SetThreadPriority(int nPriority) const;
+ DWORD SuspendThread() const;
+
+ private:
+ CThread(const CThread&); // Disable copy construction
+ CThread& operator = (const CThread&); // Disable assignment operator
+ void CreateThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag);
+ static UINT WINAPI StaticThreadCallback(LPVOID pCThread);
+
+ HANDLE m_hThread; // Handle of this thread
+ UINT m_nThreadID; // ID of this thread
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+
+ ///////////////////////////////////////
+ // Definitions for the CThread class
+ //
+ inline CThread::CThread() : m_hThread(0), m_nThreadID(0)
+ {
+ CreateThread(0, 0, CREATE_SUSPENDED);
+ }
+
+ inline CThread::CThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag)
+ : m_hThread(0), m_nThreadID(0)
+
+ {
+ // Valid argument values:
+ // pSecurityAttributes Either a pointer to SECURITY_ATTRIBUTES or 0
+ // stack_size Either the stack size or 0
+ // initflag Either CREATE_SUSPENDED or 0
+
+ CreateThread(pSecurityAttributes, stack_size, initflag);
+ }
+
+ inline CThread::~CThread()
+ {
+ // A thread's state is set to signalled when the thread terminates.
+ // If your thread is still running at this point, you have a bug.
+ if (0 != WaitForSingleObject(m_hThread, 0))
+ TRACE(_T("*** Error *** Ending CThread before ending its thread\n"));
+
+ // Close the thread's handle
+ ::CloseHandle(m_hThread);
+ }
+
+ inline void CThread::CreateThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag)
+ {
+ // NOTE: By default, the thread is created in the default state.
+ m_hThread = (HANDLE)_beginthreadex(pSecurityAttributes, stack_size, CThread::StaticThreadCallback, (LPVOID) this, initflag, &m_nThreadID);
+
+ if (0 == m_hThread)
+ throw CWinException(_T("Failed to create thread"));
+ }
+
+ inline HANDLE CThread::GetThread() const
+ {
+ assert(m_hThread);
+ return m_hThread;
+ }
+
+ inline int CThread::GetThreadID() const
+ {
+ assert(m_hThread);
+ return m_nThreadID;
+ }
+
+ inline int CThread::GetThreadPriority() const
+ {
+ assert(m_hThread);
+ return ::GetThreadPriority(m_hThread);
+ }
+
+ inline BOOL CThread::InitInstance()
+ {
+ // Override this function to perform tasks when the thread starts.
+
+ // return TRUE to run a message loop, otherwise return FALSE.
+ // A thread with a window must run a message loop.
+ return FALSE;
+ }
+
+ inline int CThread::MessageLoop()
+ {
+ // Override this function if your thread needs a different message loop
+ return GetApp()->MessageLoop();
+ }
+
+ inline DWORD CThread::ResumeThread() const
+ {
+ assert(m_hThread);
+ return ::ResumeThread(m_hThread);
+ }
+
+ inline DWORD CThread::SuspendThread() const
+ {
+ assert(m_hThread);
+ return ::SuspendThread(m_hThread);
+ }
+
+ inline BOOL CThread::SetThreadPriority(int nPriority) const
+ {
+ assert(m_hThread);
+ return ::SetThreadPriority(m_hThread, nPriority);
+ }
+
+ inline UINT WINAPI CThread::StaticThreadCallback(LPVOID pCThread)
+ // When the thread starts, it runs this function.
+ {
+ // Get the pointer for this CMyThread object
+ CThread* pThread = (CThread*)pCThread;
+
+ if (pThread->InitInstance())
+ return pThread->MessageLoop();
+
+ return 0;
+ }
+
+}
+
+#endif // #define _WIN32XX_WINTHREAD_H_
+
diff --git a/mmc_updater/depends/win32cpp/toolbar.h b/mmc_updater/depends/win32cpp/toolbar.h
new file mode 100644
index 00000000..1ed005a0
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/toolbar.h
@@ -0,0 +1,1361 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+#ifndef _WIN32XX_TOOLBAR_H_
+#define _WIN32XX_TOOLBAR_H_
+
+#include "wincore.h"
+#include "gdi.h"
+#include "rebar.h"
+
+
+namespace Win32xx
+{
+
+ struct ToolBarTheme
+ {
+ BOOL UseThemes; // TRUE if themes are used
+ COLORREF clrHot1; // Colour 1 for hot button
+ COLORREF clrHot2; // Colour 2 for hot button
+ COLORREF clrPressed1; // Colour 1 for pressed button
+ COLORREF clrPressed2; // Colour 2 for pressed button
+ COLORREF clrOutline; // Colour for border outline
+ };
+
+
+ ////////////////////////////////////
+ // Declaration of the CToolBar class
+ //
+ class CToolBar : public CWnd
+ {
+ public:
+ CToolBar();
+ virtual ~CToolBar();
+
+ // Operations
+ virtual int AddBitmap(UINT ToolBarID);
+ virtual BOOL AddButton(UINT nID, BOOL bEnabled = TRUE);
+ virtual void Destroy();
+ virtual BOOL ReplaceBitmap(UINT NewToolBarID);
+ virtual BOOL SetBitmap(UINT nID);
+ virtual int SetButtons(const std::vector<UINT>& vToolBarData) const;
+ virtual BOOL SetButtonText(int idButton, LPCTSTR szText);
+ virtual BOOL SetImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID);
+
+ // Wrappers for Win32 API functions
+ BOOL AddButtons(UINT uNumButtons, LPTBBUTTON lpButtons) const;
+ int AddString(UINT nStringID) const;
+ int AddStrings(LPCTSTR lpszStrings) const;
+ void Autosize() const;
+ void CheckButton(int idButton, BOOL fCheck) const;
+ int CommandToIndex(int idButton) const;
+ BOOL DeleteButton(int iButton) const;
+ BOOL DisableButton(int idButton) const;
+ BOOL EnableButton(int idButton) const;
+ BOOL GetButton(int iButton, LPTBBUTTON lpButton) const;
+ int GetButtonCount() const;
+ DWORD GetButtonSize() const;
+ UINT GetButtonState(int idButton) const;
+ BYTE GetButtonStyle(int idButton) const;
+ CString GetButtonText(int idButton) const;
+ int GetCommandID(int iIndex) const;
+ HIMAGELIST GetDisabledImageList() const;
+ int GetHotItem() const;
+ HIMAGELIST GetHotImageList() const;
+ HIMAGELIST GetImageList() const;
+ CRect GetItemRect(int iIndex) const;
+ CSize GetMaxSize() const;
+ DWORD GetPadding() const;
+ CRect GetRect(int idButton) const;
+ int GetRows() const;
+ int GetTextRows() const;
+ HWND GetToolTips() const;
+ BOOL HasText() const;
+ BOOL HideButton(int idButton, BOOL fShow) const;
+ int HitTest() const;
+ BOOL Indeterminate(int idButton, BOOL fIndeterminate) const;
+ BOOL InsertButton(int iButton, LPTBBUTTON lpButton) const;
+ BOOL IsButtonHidden(int idButton) const;
+ BOOL IsButtonHighlighted(int idButton) const;
+ BOOL IsButtonIndeterminate(int idButton) const;
+ BOOL IsButtonPressed(int idButton) const;
+ int MapAccelerator(TCHAR chAccel) const;
+ BOOL MarkButton(int idButton) const;
+ BOOL MoveButton(UINT uOldPos, UINT uNewPos) const;
+ BOOL PressButton(int idButton, BOOL fPress) const;
+ void SaveRestore(BOOL fSave, TBSAVEPARAMS* ptbsp) const;
+ BOOL SetBitmapSize(int cx, int cy) const;
+ BOOL SetButtonSize(int cx, int cy) const;
+ BOOL SetButtonState(int idButton, UINT State) const;
+ BOOL SetButtonStyle(int idButton, BYTE Style) const;
+ BOOL SetButtonWidth(int idButton, int nWidth) const;
+ BOOL SetCommandID(int iIndex, int idButton) const;
+ HIMAGELIST SetDisableImageList(HIMAGELIST himlNewDisabled) const;
+ DWORD SetDrawTextFlags(DWORD dwMask, DWORD dwDTFlags) const;
+ DWORD SetExtendedStyle(DWORD dwExStyle) const;
+ HIMAGELIST SetHotImageList(HIMAGELIST himlNewHot) const;
+ int SetHotItem(int iHot) const;
+ HIMAGELIST SetImageList(HIMAGELIST himlNew) const;
+ BOOL SetIndent(int iIndent) const;
+ BOOL SetMaxTextRows(int iMaxRows) const;
+ BOOL SetPadding(int cx, int cy) const;
+ void SetToolTips(HWND hwndToolTip) const;
+
+ // Attributes
+ std::vector<UINT>& GetToolBarData() const {return (std::vector <UINT> &)m_vToolBarData;}
+ ToolBarTheme& GetToolBarTheme() {return m_Theme;}
+ void SetToolBarTheme(ToolBarTheme& Theme);
+
+ protected:
+ // Overridables
+ virtual void OnCreate();
+ virtual void OnDestroy();
+ virtual void OnWindowPosChanging(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnCustomDraw(NMHDR* pNMHDR);
+ virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam);
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void PreRegisterClass(WNDCLASS &wc);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CToolBar(const CToolBar&); // Disable copy construction
+ CToolBar& operator = (const CToolBar&); // Disable assignment operator
+
+ std::vector<UINT> m_vToolBarData; // vector of resource IDs for toolbar buttons
+ std::map<tString, int> m_StringMap; // a map of strings used in SetButtonText
+ UINT m_OldToolBarID; // Bitmap Resource ID, used in AddBitmap/ReplaceBitmap
+ ToolBarTheme m_Theme; // The theme structure
+ BOOL m_bDrawArrowBkgrnd; // True if a seperate arrow background is to be drawn
+
+ }; // class CToolBar
+
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+namespace Win32xx
+{
+
+ ////////////////////////////////////
+ // Definitions for the CToolBar class
+ //
+ inline CToolBar::CToolBar() : m_OldToolBarID(0), m_bDrawArrowBkgrnd(FALSE)
+ {
+ ZeroMemory(&m_Theme, sizeof(ToolBarTheme));
+ }
+
+ inline CToolBar::~CToolBar()
+ {
+ }
+
+ inline int CToolBar::AddBitmap(UINT ToolBarID)
+ // Adds one or more images to the list of button images available for a toolbar.
+
+ // Note: AddBitmap supports a maximum colour depth of 8 bits (256 colours)
+ // For more colours, use an ImageList instead
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int iNumButtons = 0;
+ std::vector<UINT>::iterator iter;
+ for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter)
+ if ((*iter) != 0) ++iNumButtons;
+
+ TBADDBITMAP tbab = {0};
+ tbab.hInst = GetApp()->GetResourceHandle();
+ tbab.nID = ToolBarID;
+ int iResult = (int)SendMessage(TB_ADDBITMAP, iNumButtons, (LPARAM)&tbab);
+
+ if (-1 != iResult)
+ m_OldToolBarID = ToolBarID;
+
+ return iResult;
+ }
+
+ inline BOOL CToolBar::AddButton(UINT nID, BOOL bEnabled /* = TRUE */)
+ // Adds Resource IDs to toolbar buttons.
+ // A resource ID of 0 is a separator
+ {
+ assert(::IsWindow(m_hWnd));
+
+ m_vToolBarData.push_back(nID);
+
+ // TBBUTTON structure for each button in the toolbar
+ TBBUTTON tbb = {0};
+
+ std::vector<UINT>::iterator iter;
+ int iImages = 0;
+ for(iter = m_vToolBarData.begin(); iter < m_vToolBarData.end(); ++iter)
+ if (0 != *iter) iImages++;
+
+ ZeroMemory(&tbb, sizeof(TBBUTTON));
+
+ if (0 == nID)
+ {
+ tbb.fsStyle = TBSTYLE_SEP;
+ }
+ else
+ {
+ tbb.dwData = iImages -1;
+ tbb.iBitmap = iImages -1;
+ tbb.idCommand = nID;
+ tbb.fsState = bEnabled? TBSTATE_ENABLED : 0;
+ tbb.fsStyle = TBSTYLE_BUTTON;
+ }
+
+ // Add the button to the toolbar
+ return (BOOL)SendMessage(TB_ADDBUTTONS, 1L, (LPARAM)&tbb);
+ }
+
+ inline BOOL CToolBar::AddButtons(UINT uNumButtons, LPTBBUTTON lpButtons) const
+ // Adds one or more buttons to a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ADDBUTTONS, (LPARAM)uNumButtons, (WPARAM)lpButtons);
+ }
+
+ inline int CToolBar::AddString(UINT nStringID) const
+ // Adds a new string, passed as a resource ID, to the toolbar's internal list of strings.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_ADDSTRING, (LPARAM)GetApp()->GetResourceHandle(), (WPARAM)nStringID);
+ }
+
+ inline int CToolBar::AddStrings(LPCTSTR lpszStrings) const
+ // Adds a new string or strings to the list of strings available for a toolbar control.
+ // Strings in the buffer must be separated by a null character. You must ensure that the last string has two null terminators.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_ADDSTRING, 0L, (WPARAM)lpszStrings);
+ }
+
+ inline void CToolBar::Autosize() const
+ // Causes a toolbar to be resized.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(TB_AUTOSIZE, 0L, 0L);
+ }
+
+ inline void CToolBar::CheckButton(int idButton, BOOL fCheck) const
+ // Checks or unchecks a given button in a toolbar.
+ // When a button is checked, it is displayed in the pressed state.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(TB_CHECKBUTTON, (WPARAM)idButton, (LPARAM)MAKELONG(fCheck, 0));
+ }
+
+ inline int CToolBar::CommandToIndex(int idButton) const
+ // Retrieves the zero-based index for the button associated with the specified command identifier
+ {
+ assert(::IsWindow(m_hWnd));
+
+ // returns -1 on fail
+ return (int)SendMessage(TB_COMMANDTOINDEX, (WPARAM)idButton, 0L);
+ }
+
+ inline BOOL CToolBar::DeleteButton(int iButton) const
+ // Deletes a button from the toolbar.
+ // iButton is the Zero-based index of the button to delete.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_DELETEBUTTON, (WPARAM)iButton, 0L);
+ }
+
+ inline void CToolBar::Destroy()
+ // Allows CToolBar to be reused after the window is destroyed
+ {
+ CWnd::Destroy();
+ m_StringMap.clear();
+ }
+
+ inline BOOL CToolBar::DisableButton(int idButton) const
+ // Disables the specified button in a toolbar
+ // An example of idButton would be IDM_FILE_OPEN
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ENABLEBUTTON, (WPARAM)idButton, (LPARAM) MAKELONG(FALSE, 0));
+ }
+
+ inline BOOL CToolBar::EnableButton(int idButton) const
+ // Enables the specified button in a toolbar
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ENABLEBUTTON, (WPARAM)idButton, (LPARAM) MAKELONG(TRUE,0 ));
+ }
+
+ inline BOOL CToolBar::GetButton(int iButton, LPTBBUTTON lpButton) const
+ // Recieves the TBBUTTON structure information from the specified button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_GETBUTTON, (LPARAM)iButton, (WPARAM)lpButton);
+ }
+
+ inline int CToolBar::GetButtonCount() const
+ // Retrieves a count of the buttons currently in the toolbar
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_BUTTONCOUNT, 0L, 0L);
+ }
+
+ inline DWORD CToolBar::GetButtonSize() const
+ // Retrieves the current width and height of toolbar buttons, in pixels.
+ // Returns a DWORD value that contains the width and height values in the low word and high word, respectively.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(TB_GETBUTTONSIZE, 0L, 0L);
+ }
+
+ inline UINT CToolBar::GetButtonState(int idButton) const
+ // Get the state of an individual button
+ // TBSTATE_CHECKED The button has the TBSTYLE_CHECK style and is being clicked.
+ // TBSTATE_ELLIPSES The button's text is cut off and an ellipsis is displayed.
+ // TBSTATE_ENABLED The button accepts user input. A button that doesn't have this state is grayed.
+ // TBSTATE_HIDDEN The button is not visible and cannot receive user input.
+ // TBSTATE_INDETERMINATE The button is grayed.
+ // TBSTATE_MARKED The button is marked. The interpretation of a marked item is dependent upon the application.
+ // TBSTATE_PRESSED The button is being clicked.
+ // TBSTATE_WRAP The button is followed by a line break.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (UINT)SendMessage(TB_GETSTATE, (WPARAM) idButton, 0L);
+ }
+
+ inline BYTE CToolBar::GetButtonStyle(int idButton) const
+ // Get the the style of the toolbar control. The following button styles are supported:
+ // TBSTYLE_BUTTON Standard pushbutton (default)
+ // TBSTYLE_SEP Separator
+ // TBSTYLE_CHECK Auto check-box button
+ // TBSTYLE_GROUP Marks the start of a group of buttons
+ // TBSTYLE_CHECKGROUP Marks the start of a group of check-box buttons
+ // TBSTYLE_DROPDOWN Creates a drop-down list button
+ // TBSTYLE_AUTOSIZE The button's width will be calculated based on the text of the button, not on the size of the image
+ // TBSTYLE_NOPREFIX The button text will not have an accelerator prefix associated with it
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int iIndex = CommandToIndex(idButton);
+ TBBUTTON tbb = {0};
+ SendMessage(TB_GETBUTTON, iIndex, (LPARAM) &tbb);
+
+ return tbb.fsStyle;
+ }
+
+ inline CString CToolBar::GetButtonText(int idButton) const
+ // Retrieves the display text of a button on a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int Length = (int)SendMessage(TB_GETBUTTONTEXT, idButton, 0);
+ CString str;
+ LPTSTR szStr = str.GetBuffer(Length +1);
+ SendMessage(TB_GETBUTTONTEXT, (LPARAM)idButton, (WPARAM)szStr);
+ str.ReleaseBuffer();
+ return str;
+ }
+
+ inline int CToolBar::GetCommandID(int iIndex) const
+ // Retrieves information about the specified button in a toolbar
+ {
+ assert(::IsWindow(m_hWnd));
+ TBBUTTON tbb = {0};
+ SendMessage(TB_GETBUTTON, iIndex, (WPARAM) &tbb);
+
+ // returns zero if failed
+ return tbb.idCommand;
+ }
+
+ inline HIMAGELIST CToolBar::GetDisabledImageList() const
+ // Retrieves the image list that a toolbar control uses to display inactive buttons.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L);
+ }
+
+ inline HIMAGELIST CToolBar::GetHotImageList() const
+ // Retrieves the image list that a toolbar control uses to display hot buttons.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L);
+ }
+
+ inline int CToolBar::GetHotItem() const
+ // Retrieves the index of the hot item in a toolbar, or -1 if no hot item is set.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_GETHOTITEM, 0L, 0L);
+ }
+
+ inline HIMAGELIST CToolBar::GetImageList() const
+ // Retrieves the image list that a toolbar control uses to display buttons in their default state.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L);
+ }
+
+ inline CRect CToolBar::GetItemRect(int iIndex) const
+ // Retrieves the bounding rectangle of a button in a toolbar
+ {
+ assert(::IsWindow(m_hWnd));
+ CRect rc;
+ int iCount = (int)SendMessage(TB_BUTTONCOUNT, 0L, 0L);
+
+ if (iCount >= iIndex)
+ SendMessage(TB_GETITEMRECT, (WPARAM)iIndex, (LPARAM)&rc);
+
+ return rc;
+ }
+
+ inline CSize CToolBar::GetMaxSize() const
+ // Retrieves the total size of all of the visible buttons and separators in the toolbar
+ {
+ assert(::IsWindow(m_hWnd));
+ CSize sz;
+ SendMessage(TB_GETMAXSIZE, 0L, (LPARAM)&sz);
+
+ // This fixes a Windows bug calculating the size when TBSTYLE_DROPDOWN is used.
+ int xMaxSize = 0;
+ for (int i= 0 ; i < GetButtonCount(); ++i)
+ {
+ xMaxSize += GetItemRect(i).Width();
+ }
+
+ sz.cx = xMaxSize;
+ return sz;
+ }
+
+ inline DWORD CToolBar::GetPadding() const
+ // Returns a DWORD value that contains the horizontal padding in the low word and the vertical padding in the high word, in pixels.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(TB_GETPADDING, 0L, 0L);
+ }
+
+ inline CRect CToolBar::GetRect(int idButton) const
+ // Retrieves the bounding rectangle for a specified toolbar button.
+ {
+ assert(::IsWindow(m_hWnd));
+ CRect rc;
+ SendMessage(TB_GETRECT, (WPARAM)idButton, (LPARAM)&rc);
+ return rc;
+ }
+
+ inline int CToolBar::GetRows() const
+ // Retrieves the number of rows of buttons in a toolbar with the TBSTYLE_WRAPABLE style.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_GETROWS, 0L, 0L);
+ }
+
+ inline int CToolBar::GetTextRows() const
+ // Retrieves the maximum number of text rows that can be displayed on a toolbar button.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_GETTEXTROWS, 0L, 0L);
+ }
+
+ inline HWND CToolBar::GetToolTips() const
+ // Retrieves the handle to the ToolTip control, if any, associated with the toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HWND)SendMessage(TB_GETTOOLTIPS, 0L, 0L);
+ }
+
+ inline BOOL CToolBar::HasText() const
+ {
+ assert(::IsWindow(m_hWnd));
+ BOOL bReturn = FALSE;
+
+ for (int i = 0 ; i < GetButtonCount(); ++i)
+ {
+ if (SendMessage(TB_GETBUTTONTEXT, GetCommandID(i), 0L) != -1)
+ bReturn = TRUE;
+ }
+
+ // return TRUE if any button has text
+ return bReturn;
+ }
+
+ inline BOOL CToolBar::HideButton(int idButton, BOOL fShow) const
+ //Hides or shows the specified button in a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_HIDEBUTTON, (WPARAM)idButton, (LPARAM)MAKELONG (fShow, 0));
+ }
+
+ inline int CToolBar::HitTest() const
+ // Determines where a point lies in a toolbar control.
+
+ // We do our own hit test since TB_HITTEST is a bit buggy,
+ // and also doesn't work at all on earliest versions of Win95
+ {
+ assert(::IsWindow(m_hWnd));
+ CPoint pt = GetCursorPos();
+ ScreenToClient(pt);
+
+ int nButtons = (int)SendMessage(TB_BUTTONCOUNT, 0L, 0L);
+ int iButton = -1;
+
+ for (int i = 0 ; i < nButtons; ++i)
+ {
+ CRect rc = GetItemRect(i);
+ if (rc.PtInRect(pt))
+ iButton = i;
+ }
+
+ return iButton;
+ }
+
+ inline BOOL CToolBar::Indeterminate(int idButton, BOOL fIndeterminate) const
+ //Hides or shows the specified button in a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_INDETERMINATE, (WPARAM)idButton, (LPARAM)MAKELONG (fIndeterminate, 0));
+ }
+
+ inline BOOL CToolBar::InsertButton(int iButton, LPTBBUTTON lpButton) const
+ // Inserts a button in a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_INSERTBUTTON, (WPARAM)iButton, (LPARAM)lpButton);
+ }
+
+ inline BOOL CToolBar::IsButtonHidden(int idButton) const
+ // Determines whether the specified button in a toolbar is hidden.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ISBUTTONHIDDEN, (WPARAM)idButton, 0L);
+ }
+
+ inline BOOL CToolBar::IsButtonHighlighted(int idButton) const
+ // Checks the highlight state of a toolbar button.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ISBUTTONHIGHLIGHTED, (WPARAM)idButton, 0L);
+ }
+
+ inline BOOL CToolBar::IsButtonIndeterminate(int idButton) const
+ // Determines whether the specified button in a toolbar is indeterminate.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ISBUTTONINDETERMINATE, (WPARAM)idButton, 0L);
+ }
+
+ inline BOOL CToolBar::IsButtonPressed(int idButton) const
+ // Determines whether the specified button in a toolbar is pressed.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_ISBUTTONPRESSED, (WPARAM)idButton, 0L);
+ }
+
+ inline int CToolBar::MapAccelerator(TCHAR chAccel) const
+ // Determines whether the specified button in a toolbar is pressed.
+ {
+ assert(::IsWindow(m_hWnd));
+ int uButtonID;
+ int idButton;
+ if (SendMessage(TB_MAPACCELERATOR, (WPARAM)chAccel, (LPARAM)&uButtonID))
+ idButton = uButtonID;
+ else
+ idButton = -1;
+
+ return idButton;
+ }
+
+ inline BOOL CToolBar::MarkButton(int idButton) const
+ // Sets the highlight state of a given button in a toolbar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_MARKBUTTON, (WPARAM)idButton, 0L);
+ }
+
+ inline BOOL CToolBar::MoveButton(UINT uOldPos, UINT uNewPos) const
+ // Moves a button from one index to another.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_MOVEBUTTON, (WPARAM)uOldPos, (LPARAM)uNewPos);
+ }
+
+
+ inline void CToolBar::OnCreate()
+ {
+ // We must send this message before sending the TB_ADDBITMAP or TB_ADDBUTTONS message
+ SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0L);
+
+ // allows buttons to have a separate dropdown arrow
+ // Note: TBN_DROPDOWN notification is sent by a toolbar control when the user clicks a dropdown button
+ SendMessage(TB_SETEXTENDEDSTYLE, 0L, TBSTYLE_EX_DRAWDDARROWS);
+
+ // Turn of Double click processing (i.e. treat a double click as two single clicks)
+ DWORD dwStyle = (DWORD)GetClassLongPtr(GCL_STYLE);
+ dwStyle &= ~CS_DBLCLKS;
+ SetClassLongPtr(GCL_STYLE, dwStyle);
+
+ // Add extra styles for toolbars inside a rebar
+ if (lstrcmp(GetParent()->GetClassName(), _T("ReBarWindow32")) == 0)
+ {
+ DWORD style = (DWORD)GetWindowLongPtr(GWL_STYLE);
+ style |= CCS_NODIVIDER | CCS_NORESIZE;
+ SetWindowLongPtr(GWL_STYLE, style);
+ }
+
+ SetButtons(m_vToolBarData);
+
+ // Set rows of text to zero
+ SendMessage(TB_SETMAXTEXTROWS, 0L, 0L);
+ }
+
+ inline LRESULT CToolBar::OnCustomDraw(NMHDR* pNMHDR)
+ // With CustomDraw we manually control the drawing of each toolbar button
+ {
+ LPNMTBCUSTOMDRAW lpNMCustomDraw = (LPNMTBCUSTOMDRAW)pNMHDR;
+
+ switch (lpNMCustomDraw->nmcd.dwDrawStage)
+ {
+ // Begin paint cycle
+ case CDDS_PREPAINT:
+ // Send NM_CUSTOMDRAW item draw, and post-paint notification messages.
+ return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT ;
+
+ // An item is about to be drawn
+ case CDDS_ITEMPREPAINT:
+ {
+ CDC* pDrawDC = FromHandle(lpNMCustomDraw->nmcd.hdc);
+ CRect rcRect = lpNMCustomDraw->nmcd.rc;
+ int nState = lpNMCustomDraw->nmcd.uItemState;
+ DWORD dwItem = (DWORD)lpNMCustomDraw->nmcd.dwItemSpec;
+ DWORD dwTBStyle = (DWORD)SendMessage(TB_GETSTYLE, 0L, 0L);
+ int nStyle = GetButtonStyle(dwItem);
+
+ int nButton = (int)SendMessage(TB_COMMANDTOINDEX, (WPARAM) dwItem, 0L);
+ TBBUTTON tbb = {0};
+ SendMessage(TB_GETBUTTON, nButton, (LPARAM)&tbb);
+ int iImage = (int)tbb.dwData;
+
+ // Calculate text size
+ std::vector<TCHAR> vText(MAX_MENU_STRING, _T('\0'));
+ TCHAR* pszText = &vText[0];
+ CSize TextSize;
+ if (HasText()) // Does any button have text?
+ {
+ pDrawDC->SelectObject(GetFont());
+ if (SendMessage(TB_GETBUTTONTEXT, dwItem, (LPARAM)pszText)> 0)
+ {
+ TextSize = pDrawDC->GetTextExtentPoint32(pszText, lstrlen(pszText));
+ }
+ }
+
+ // Draw outline rectangle
+ if (nState & (CDIS_HOT | CDIS_SELECTED | CDIS_CHECKED))
+ {
+ pDrawDC->CreatePen(PS_SOLID, 1, m_Theme.clrOutline);
+ pDrawDC->MoveTo(rcRect.left, rcRect.top);
+ pDrawDC->LineTo(rcRect.left, rcRect.bottom-1);
+ pDrawDC->LineTo(rcRect.right-1, rcRect.bottom-1);
+ pDrawDC->LineTo(rcRect.right-1, rcRect.top);
+ pDrawDC->LineTo(rcRect.left, rcRect.top);
+ }
+
+ // Draw filled gradient background
+ rcRect.InflateRect(-1, -1);
+ if ((nState & (CDIS_SELECTED|CDIS_CHECKED)) || (GetButtonState(dwItem) & TBSTATE_PRESSED))
+ {
+ pDrawDC->GradientFill(m_Theme.clrPressed1, m_Theme.clrPressed2, rcRect, FALSE);
+ }
+ else if (nState & CDIS_HOT)
+ {
+ pDrawDC->GradientFill(m_Theme.clrHot1, m_Theme.clrHot2, rcRect, FALSE);
+ }
+
+ // Get the appropriate image list depending on the button state
+ HIMAGELIST himlToolBar;
+ if (nState & CDIS_DISABLED)
+ {
+ himlToolBar = (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L);
+ }
+ else if (nState & (CDIS_HOT | CDIS_SELECTED | CDIS_CHECKED))
+ {
+ himlToolBar = (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L);
+ if (0 == himlToolBar)
+ himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L);
+ }
+ else
+ {
+ himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L);
+ }
+
+ BOOL IsWin95 = (1400 == (GetWinVersion()) || (2400 == GetWinVersion()));
+
+ // Calculate image position
+ int cxImage = 0;
+ int cyImage = 0;
+ ImageList_GetIconSize(himlToolBar, &cxImage, &cyImage);
+
+ int yImage = (rcRect.bottom - rcRect.top - cyImage - TextSize.cy +2)/2;
+ int xImage = (rcRect.right + rcRect.left - cxImage)/2 + ((nState & (CDIS_SELECTED|CDIS_CHECKED))? 1:0);
+ if (dwTBStyle & TBSTYLE_LIST)
+ {
+ xImage = rcRect.left + (IsXPThemed()?2:4) + ((nState & CDIS_SELECTED)? 1:0);
+ yImage = (rcRect.bottom -rcRect.top - cyImage +2)/2 + ((nState & (CDIS_SELECTED|CDIS_CHECKED))? 1:0);
+ }
+
+ // Handle the TBSTYLE_DROPDOWN and BTNS_WHOLEDROPDOWN styles
+ if ((nStyle & TBSTYLE_DROPDOWN) || ((nStyle & 0x0080) && (!IsWin95)))
+ {
+ // Calculate the dropdown arrow position
+ int xAPos = (nStyle & TBSTYLE_DROPDOWN)? rcRect.right -6 : (rcRect.right + rcRect.left + cxImage + 4)/2;
+ int yAPos = (nStyle & TBSTYLE_DROPDOWN)? (rcRect.bottom - rcRect.top +1)/2 : (cyImage)/2;
+ if (dwTBStyle & TBSTYLE_LIST)
+ {
+ xAPos = (nStyle & TBSTYLE_DROPDOWN)?rcRect.right -6:rcRect.right -5;
+ yAPos = (rcRect.bottom - rcRect.top +1)/2 + ((nStyle & TBSTYLE_DROPDOWN)?0:1);
+ }
+
+ xImage -= (nStyle & TBSTYLE_DROPDOWN)?((dwTBStyle & TBSTYLE_LIST)? (IsXPThemed()?-4:0):6):((dwTBStyle & TBSTYLE_LIST)? 0:4);
+
+ // Draw separate background for dropdown arrow
+ if ((m_bDrawArrowBkgrnd) && (nState & CDIS_HOT))
+ {
+ CRect rcArrowBkgnd = rcRect;
+ rcArrowBkgnd.left = rcArrowBkgnd.right - 13;
+ pDrawDC->GradientFill(m_Theme.clrPressed1, m_Theme.clrPressed2, rcArrowBkgnd, FALSE);
+ }
+
+ m_bDrawArrowBkgrnd = FALSE;
+
+ // Manually draw the dropdown arrow
+ pDrawDC->CreatePen(PS_SOLID, 1, RGB(0,0,0));
+ for (int i = 2; i >= 0; --i)
+ {
+ pDrawDC->MoveTo(xAPos -i-1, yAPos - i+1);
+ pDrawDC->LineTo(xAPos +i, yAPos - i+1);
+ }
+
+ // Draw line between icon and dropdown arrow
+ if ((nStyle & TBSTYLE_DROPDOWN) && ((nState & CDIS_SELECTED) || nState & CDIS_HOT))
+ {
+ pDrawDC->CreatePen(PS_SOLID, 1, m_Theme.clrOutline);
+ pDrawDC->MoveTo(rcRect.right - 13, rcRect.top);
+ pDrawDC->LineTo(rcRect.right - 13, rcRect.bottom);
+ }
+ }
+
+ // Draw the button image
+ if (xImage > 0)
+ {
+ ImageList_Draw(himlToolBar, iImage, *pDrawDC, xImage, yImage, ILD_TRANSPARENT);
+ }
+
+ //Draw Text
+ if (lstrlen(pszText) > 0)
+ {
+ int iWidth = rcRect.right - rcRect.left - ((nStyle & TBSTYLE_DROPDOWN)?13:0);
+ CRect rcText(0, 0, MIN(TextSize.cx, iWidth), TextSize.cy);
+
+ int xOffset = (rcRect.right + rcRect.left - rcText.right + rcText.left - ((nStyle & TBSTYLE_DROPDOWN)? 11 : 1))/2;
+ int yOffset = yImage + cyImage +1;
+
+ if (dwTBStyle & TBSTYLE_LIST)
+ {
+ xOffset = rcRect.left + cxImage + ((nStyle & TBSTYLE_DROPDOWN)?(IsXPThemed()?10:6): 6) + ((nState & CDIS_SELECTED)? 1:0);
+ yOffset = (2+rcRect.bottom - rcRect.top - rcText.bottom + rcText.top)/2 + ((nState & CDIS_SELECTED)? 1:0);
+ rcText.right = MIN(rcText.right, rcRect.right - xOffset);
+ }
+
+ OffsetRect(&rcText, xOffset, yOffset);
+
+ int iMode = pDrawDC->SetBkMode(TRANSPARENT);
+ pDrawDC->SelectObject(GetFont());
+
+ if (nState & (CDIS_DISABLED))
+ {
+ // Draw text twice for embossed look
+ rcText.OffsetRect(1, 1);
+ pDrawDC->SetTextColor(RGB(255,255,255));
+ pDrawDC->DrawText(pszText, lstrlen(pszText), rcText, DT_LEFT);
+ rcText.OffsetRect(-1, -1);
+ pDrawDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
+ pDrawDC->DrawText(pszText, lstrlen(pszText), rcText, DT_LEFT);
+ }
+ else
+ {
+ pDrawDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
+ pDrawDC->DrawText(pszText, lstrlen(pszText), rcText, DT_LEFT | DT_END_ELLIPSIS);
+ }
+ pDrawDC->SetBkMode(iMode);
+ }
+ }
+ return CDRF_SKIPDEFAULT; // No further drawing
+ }
+ return 0L;
+ }
+
+ inline void CToolBar::OnDestroy()
+ {
+ HIMAGELIST himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L);
+ HIMAGELIST himlToolBarHot = (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L);
+ HIMAGELIST himlToolBarDis = (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L);
+ ImageList_Destroy(himlToolBar);
+ ImageList_Destroy(himlToolBarHot);
+ ImageList_Destroy(himlToolBarDis);
+ }
+
+ inline LRESULT CToolBar::OnNotifyReflect(WPARAM wParam, LPARAM lParam)
+ // Notifications sent to the parent window are reflected back here
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case NM_CUSTOMDRAW:
+ {
+ if (m_Theme.UseThemes)
+ return OnCustomDraw((LPNMHDR) lParam);
+ }
+ break;
+
+ case TBN_DROPDOWN:
+ {
+ int iItem = ((LPNMTOOLBAR) lParam)->iItem;
+
+ // a boolean expression
+ m_bDrawArrowBkgrnd = (GetButtonStyle(iItem) & TBSTYLE_DROPDOWN);
+ }
+ break;
+ }
+ return 0L;
+ }
+
+ inline void CToolBar::OnWindowPosChanging(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+
+ // Adjust size for toolbars inside a rebar
+ CWnd* pParent = GetParent();
+ if (lstrcmp(pParent->GetClassName(), _T("ReBarWindow32")) == 0)
+ {
+ ReBarTheme* pTheme = (ReBarTheme*)pParent->SendMessage(UWM_GETREBARTHEME, 0, 0);
+
+ if (pTheme && pTheme->UseThemes && pTheme->ShortBands)
+ {
+ LPWINDOWPOS pWinPos = (LPWINDOWPOS)lParam;
+ pWinPos->cx = GetMaxSize().cx+2;
+ }
+ }
+ }
+
+ inline void CToolBar::PreCreate(CREATESTRUCT &cs)
+ {
+ // Sets the CREATESTRUCT parameters prior to window creation
+ cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT;
+ }
+
+ inline void CToolBar::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = TOOLBARCLASSNAME;
+ }
+
+ inline BOOL CToolBar::PressButton(int idButton, BOOL fPress) const
+ // Presses or releases the specified button in a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_PRESSBUTTON, (WPARAM)idButton, (LPARAM)MAKELONG(fPress, 0));
+ }
+
+ inline BOOL CToolBar::ReplaceBitmap(UINT NewToolBarID)
+ // Replaces an existing bitmap with a new bitmap.
+
+ // Note: ReplaceBitmap supports a maximum colour depth of 8 bits (256 colours)
+ // For more colours, use an ImageList instead
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int iNumButtons = 0;
+ std::vector<UINT>::iterator iter;
+ for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter)
+ if ((*iter) != 0) ++iNumButtons;
+
+ TBREPLACEBITMAP tbrb = {0};
+ tbrb.hInstNew = GetApp()->GetResourceHandle();
+ tbrb.hInstOld = GetApp()->GetResourceHandle();
+ tbrb.nIDNew = NewToolBarID;
+ tbrb.nIDOld = m_OldToolBarID;
+ tbrb.nButtons = iNumButtons;
+
+ BOOL bResult = (BOOL)SendMessage(TB_REPLACEBITMAP, iNumButtons, (LPARAM)&tbrb);
+ if (bResult)
+ m_OldToolBarID = NewToolBarID;
+
+ return bResult;
+ }
+
+ inline void CToolBar::SaveRestore(BOOL fSave, TBSAVEPARAMS* ptbsp) const
+ // Presses or releases the specified button in a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(TB_PRESSBUTTON, (WPARAM)fSave, (LPARAM)ptbsp);
+ }
+
+ inline BOOL CToolBar::SetBitmap(UINT nID)
+ // Set the button images
+ {
+ assert(::IsWindow(m_hWnd));
+
+ CBitmap Bitmap(nID);
+ assert (Bitmap.GetHandle());
+ BITMAP bm = Bitmap.GetBitmapData();
+
+ int iNumButtons = 0;
+ std::vector<UINT>::iterator iter;
+ for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter)
+ if ((*iter) != 0) ++iNumButtons;
+
+ int iImageWidth = bm.bmWidth / iNumButtons;
+ int iImageHeight = bm.bmHeight;
+
+ // Set the bitmap size first
+ SetBitmapSize(iImageWidth, iImageHeight);
+
+ BOOL bResult = FALSE;
+ if (m_OldToolBarID)
+ bResult = ReplaceBitmap(nID);
+ else
+ bResult = (BOOL)AddBitmap(nID);
+
+ return bResult;
+ }
+
+ inline BOOL CToolBar::SetBitmapSize(int cx, int cy) const
+ // Sets the size of the bitmapped images to be added to a toolbar.
+
+ // Needs to be used when the image size is not the default 16 x 15
+ // Call this function before using AddBitmap or ReplaceBitmap
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETBITMAPSIZE, 0L, MAKELONG(cx, cy));
+ }
+
+ inline int CToolBar::SetButtons(const std::vector<UINT>& vToolBarData) const
+ // Assigns a resource ID to each toolbar button
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int iImages = 0;
+ UINT iNumButtons = (UINT)vToolBarData.size();
+
+ // Remove any existing buttons
+ while (SendMessage(TB_BUTTONCOUNT, 0L, 0L) > 0)
+ {
+ if(!SendMessage(TB_DELETEBUTTON, 0L, 0L))
+ break;
+ }
+
+ if (iNumButtons > 0)
+ {
+ // TBBUTTON structure for each button in the toolbar
+ TBBUTTON tbb = {0};
+
+ for (UINT j = 0 ; j < iNumButtons; ++j)
+ {
+ ZeroMemory(&tbb, sizeof(TBBUTTON));
+
+ if (0 == vToolBarData[j])
+ {
+ tbb.fsStyle = TBSTYLE_SEP;
+ }
+ else
+ {
+ tbb.dwData = iImages;
+ tbb.iBitmap = iImages;
+ tbb.idCommand = vToolBarData[j];
+ tbb.fsState = TBSTATE_ENABLED;
+ tbb.fsStyle = TBSTYLE_BUTTON;
+ }
+
+ // Add the button to the toolbar
+ if (SendMessage(TB_ADDBUTTONS, 1L, (LPARAM)&tbb))
+ iImages++;
+ else
+ break;
+ }
+ }
+
+ return iImages;
+ }
+
+ inline BOOL CToolBar::SetButtonSize(int cx, int cy) const
+ // Sets the size of the buttons to be added to a toolbar
+ // The size can be set only before adding any buttons to the toolbar
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETBUTTONSIZE, 0L, MAKELONG(cx, cy));
+ }
+
+ inline BOOL CToolBar::SetButtonState(int idButton, UINT State) const
+ {
+ // Set the state of an individual button
+ // TBSTATE_CHECKED The button has the TBSTYLE_CHECK style and is being clicked.
+ // TBSTATE_ELLIPSES The button's text is cut off and an ellipsis is displayed.
+ // TBSTATE_ENABLED The button accepts user input. A button that doesn't have this state is grayed.
+ // TBSTATE_HIDDEN The button is not visible and cannot receive user input.
+ // TBSTATE_INDETERMINATE The button is grayed.
+ // TBSTATE_MARKED The button is marked. The interpretation of a marked item is dependent upon the application.
+ // TBSTATE_PRESSED The button is being clicked.
+ // TBSTATE_WRAP The button is followed by a line break.
+
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETSTATE, (WPARAM) idButton, (LPARAM)MAKELONG (State, 0));
+ }
+
+ inline BOOL CToolBar::SetButtonStyle(int idButton, BYTE Style) const
+ // The the style of the toolbar control. The following button styles are supported:
+ // TBSTYLE_BUTTON Standard pushbutton (default)
+ // TBSTYLE_SEP Separator
+ // TBSTYLE_CHECK Auto check-box button
+ // TBSTYLE_GROUP Marks the start of a group of buttons
+ // TBSTYLE_CHECKGROUP Marks the start of a group of check-box buttons
+ // TBSTYLE_DROPDOWN Creates a drop-down list button
+ // TBSTYLE_AUTOSIZE The button's width will be calculated based on the text of the button, not on the size of the image
+ // TBSTYLE_NOPREFIX The button text will not have an accelerator prefix associated with it
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TBBUTTONINFO tbbi = {0};
+ tbbi.cbSize = sizeof(TBBUTTONINFO);
+ tbbi.dwMask = TBIF_STYLE;
+ tbbi.fsStyle = Style;
+
+ // Note: TB_SETBUTTONINFO requires comctl32.dll version 4.71 or later
+ // i.e. Win95 with IE4 / NT with IE4 or later
+ return (BOOL)SendMessage(TB_SETBUTTONINFO, idButton, (LPARAM) &tbbi);
+ }
+
+ inline BOOL CToolBar::SetButtonText(int idButton, LPCTSTR szText)
+ // This rather convoluted approach to setting toolbar button text supports
+ // all versions of Windows, including Win95 with COMCTL32.DLL version 4.0
+ {
+ assert(::IsWindow(m_hWnd));
+ int iIndex = CommandToIndex(idButton);
+ assert(-1 != iIndex);
+
+ BOOL Succeeded = TRUE;
+ tString sString = szText;
+ std::map<tString, int>::iterator m;
+ int iString;
+
+ // Check to see if the string is already added
+ m = m_StringMap.find(sString);
+ if (m_StringMap.end() == m)
+ {
+ if (0 == m_StringMap.size())
+ {
+ // Place a blank string first in the string table, in case some
+ // buttons don't have text
+ TCHAR szString[3] = _T(" ");
+ szString[2] = _T('\0'); // Double-null terminate
+ SendMessage(TB_ADDSTRING, 0L, (LPARAM)szString);
+ }
+
+ // No index for this string exists, so create it now
+ TCHAR szBuf[80] = _T("");
+ lstrcpyn(szBuf, szText, 79);
+ szBuf[lstrlen(szBuf)+1] = _T('\0'); // Double-null terminate
+
+ iString = (int)SendMessage(TB_ADDSTRING, 0L, (LPARAM)szBuf);
+ if (-1 == iString )
+ Succeeded = FALSE;
+
+ // Save the string its index in our map
+ m_StringMap.insert(std::make_pair(sString, iString));
+ }
+ else
+ {
+ // String found, use the index from our map
+ iString = m->second;
+ }
+
+ if (Succeeded)
+ {
+ TBBUTTON tbb = {0};
+ Succeeded = (BOOL)SendMessage(TB_GETBUTTON, iIndex, (LPARAM)&tbb);
+
+ tbb.iString = iString;
+
+ // Turn off ToolBar drawing
+ SendMessage(WM_SETREDRAW, FALSE, 0L);
+
+ if (Succeeded)
+ Succeeded = (BOOL)SendMessage(TB_DELETEBUTTON, iIndex, 0L);
+
+ if (Succeeded)
+ Succeeded = (BOOL)SendMessage(TB_INSERTBUTTON, iIndex, (LPARAM)&tbb);
+
+ // Ensure the button now includes some text rows
+ if (0 == SendMessage(TB_GETTEXTROWS, 0L, 0L))
+ SendMessage(TB_SETMAXTEXTROWS, 1L, 0L);
+
+ // Turn on ToolBar drawing
+ SendMessage(WM_SETREDRAW, TRUE, 0L);
+ }
+ // Redraw button
+ CRect r = GetItemRect(iIndex);
+ InvalidateRect(&r, TRUE);
+
+ return Succeeded;
+ }
+
+ inline BOOL CToolBar::SetButtonWidth(int idButton, int nWidth) const
+ // The set button width can adjust the width of the button after it is created.
+ // This is useful when replacing a button with a ComboBox or other control.
+ // Note: TB_SETBUTTONINFO requires comctl32.dll version 4.71 or later
+ // i.e. Win95 with IE4 / NT with IE4 or later
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TBBUTTONINFO tbbi = {0};
+ tbbi.cbSize = sizeof(TBBUTTONINFO);
+ tbbi.dwMask = TBIF_SIZE;
+ tbbi.cx = (WORD)nWidth;
+ BOOL bResult = (BOOL)SendMessage(TB_SETBUTTONINFO, (WPARAM)idButton, (LPARAM)&tbbi);
+
+ // Send a changed message to the parent (used by the rebar)
+ SIZE MaxSize = GetMaxSize();
+ GetParent()->SendMessage(UWM_TOOLBAR_RESIZE, (WPARAM)m_hWnd, (LPARAM)&MaxSize);
+
+ return bResult;
+ }
+
+ inline BOOL CToolBar::SetCommandID(int iIndex, int idButton) const
+ // Sets the command identifier of a toolbar button
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETCMDID, iIndex, idButton);
+ }
+
+ inline HIMAGELIST CToolBar::SetDisableImageList(HIMAGELIST himlNewDisabled) const
+ // Sets the image list that the toolbar control will use to display disabled buttons.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HIMAGELIST)SendMessage(TB_SETDISABLEDIMAGELIST, 0L, (LPARAM)himlNewDisabled);
+ }
+
+ inline DWORD CToolBar::SetDrawTextFlags(DWORD dwMask, DWORD dwDTFlags) const
+ // Sets the text drawing flags for the toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(TB_SETDRAWTEXTFLAGS, (WPARAM)dwMask, (LPARAM)dwDTFlags);
+ }
+
+ inline DWORD CToolBar::SetExtendedStyle(DWORD dwExStyle) const
+ // Sets the text drawing flags for the toolbar.
+ // Extended styles include: TBSTYLE_EX_DRAWDDARROWS, TBSTYLE_EX_HIDECLIPPEDBUTTONS, TBSTYLE_EX_DOUBLEBUFFER and TBSTYLE_EX_MIXEDBUTTONS
+ {
+ assert(::IsWindow(m_hWnd));
+ return (DWORD)SendMessage(TB_SETEXTENDEDSTYLE, 0L, (LPARAM)dwExStyle);
+ }
+
+ inline HIMAGELIST CToolBar::SetHotImageList(HIMAGELIST himlNewHot) const
+ // Sets the image list that the toolbar control will use to display hot buttons.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HIMAGELIST)SendMessage(TB_SETHOTIMAGELIST, 0L, (LPARAM)himlNewHot);
+ }
+
+ inline int CToolBar::SetHotItem(int iHot) const
+ // Sets the hot item in a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (int)SendMessage(TB_SETHOTITEM, (WPARAM)iHot, 0L);
+ }
+
+ inline HIMAGELIST CToolBar::SetImageList(HIMAGELIST himlNew) const
+ // Sets the image list that the toolbar will use to display buttons that are in their default state.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HIMAGELIST)SendMessage(TB_SETIMAGELIST, 0L, (LPARAM)himlNew);
+ }
+
+ inline BOOL CToolBar::SetImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID)
+ // Either sets the imagelist or adds/replaces bitmap depending on ComCtl32.dll version
+ // Assumes the width of the button image = bitmap_size / buttons
+ // Assumes buttons have been already been added via AdddToolBarButton
+ // The colour mask is often grey RGB(192,192,192) or magenta (255,0,255);
+ // The color mask is ignored for 32bit bitmap resources
+ // The Hot and disiabled bitmap resources can be 0
+ {
+ assert(::IsWindow(m_hWnd));
+
+ // ToolBar ImageLists require Comctl32.dll version 4.7 or later
+ if (400 == GetComCtlVersion())
+ {
+ // We are using COMCTL32.DLL version 4.0, so we can't use an imagelist.
+ // Instead we simply set the bitmap.
+ return SetBitmap(ToolBarID);
+ }
+
+ int iNumButtons = 0;
+ std::vector<UINT>::iterator iter;
+ for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter)
+ if ((*iter) != 0) ++iNumButtons;
+
+ if (iNumButtons > 0)
+ {
+ // Set the button images
+ CBitmap Bitmap(ToolBarID);
+ assert(Bitmap.GetHandle());
+
+ BITMAP bm = Bitmap.GetBitmapData();
+ int iImageWidth = bm.bmWidth / iNumButtons;
+ int iImageHeight = bm.bmHeight;
+
+ HIMAGELIST himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L);
+ HIMAGELIST himlToolBarHot = (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L);
+ HIMAGELIST himlToolBarDis = (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L);
+ ImageList_Destroy(himlToolBar);
+ ImageList_Destroy(himlToolBarHot);
+ ImageList_Destroy(himlToolBarDis);
+
+ himlToolBar = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iNumButtons, 0);
+ assert(himlToolBar);
+
+ ImageList_AddMasked(himlToolBar, Bitmap, crMask);
+ SendMessage(TB_SETIMAGELIST, 0L, (LPARAM)himlToolBar);
+
+ if (ToolBarHotID)
+ {
+ CBitmap BitmapHot(ToolBarHotID);
+ assert(BitmapHot);
+
+ himlToolBarHot = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iNumButtons, 0);
+ assert(himlToolBarHot);
+
+ ImageList_AddMasked(himlToolBarHot, BitmapHot, crMask);
+ SendMessage(TB_SETHOTIMAGELIST, 0L, (LPARAM)himlToolBarHot);
+ }
+
+ if (ToolBarDisabledID)
+ {
+ CBitmap BitmapDisabled(ToolBarDisabledID);
+ assert(BitmapDisabled);
+
+ himlToolBarDis = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iNumButtons, 0);
+ assert(himlToolBarDis);
+
+ ImageList_AddMasked(himlToolBarDis, BitmapDisabled, crMask);
+ SendMessage(TB_SETDISABLEDIMAGELIST, 0L, (LPARAM)himlToolBarDis);
+ }
+ else
+ {
+ himlToolBarDis = CreateDisabledImageList(himlToolBar);
+ SendMessage(TB_SETDISABLEDIMAGELIST, 0L, (LPARAM)himlToolBarDis);
+ }
+
+ // Inform the parent of the change (rebar needs this)
+ SIZE MaxSize = GetMaxSize();
+ GetParent()->SendMessage(UWM_TOOLBAR_RESIZE, (WPARAM)m_hWnd, (LPARAM)&MaxSize);
+ }
+
+ return TRUE;
+ }
+
+ inline BOOL CToolBar::SetIndent(int iIndent) const
+ // Sets the indentation for the first button in a toolbar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETINDENT, (WPARAM)iIndent, 0L);
+ }
+
+ inline BOOL CToolBar::SetMaxTextRows(int iMaxRows) const
+ // Sets the maximum number of text rows displayed on a toolbar button.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETMAXTEXTROWS, (WPARAM)iMaxRows, 0L);
+ }
+
+ inline BOOL CToolBar::SetPadding(int cx, int cy) const
+ // Sets the padding for a toolbar control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)SendMessage(TB_SETPADDING, 0L, (WPARAM)MAKELONG(cx, cy));
+ }
+
+ inline void CToolBar::SetToolBarTheme(ToolBarTheme& Theme)
+ {
+ m_Theme.UseThemes = Theme.UseThemes;
+ m_Theme.clrHot1 = Theme.clrHot1;
+ m_Theme.clrHot2 = Theme.clrHot2;
+ m_Theme.clrPressed1 = Theme.clrPressed1;
+ m_Theme.clrPressed2 = Theme.clrPressed2;
+ m_Theme.clrOutline = Theme.clrOutline;
+
+ if (IsWindow())
+ Invalidate();
+ }
+
+ inline void CToolBar::SetToolTips(HWND hwndToolTip) const
+ // Associates a ToolTip control with a toolbar.
+ {
+ assert(::IsWindow(m_hWnd));
+ SendMessage(TB_SETTOOLTIPS, (WPARAM)hwndToolTip, 0L);
+ }
+
+ inline LRESULT CToolBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_DESTROY:
+ OnDestroy();
+ break;
+ case UWM_GETTOOLBARTHEME:
+ {
+ ToolBarTheme& tt = GetToolBarTheme();
+ return (LRESULT)&tt;
+ }
+ case WM_WINDOWPOSCHANGING:
+ OnWindowPosChanging(wParam, lParam);
+ break;
+ }
+
+ // pass unhandled messages on for default processing
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+} // namespace Win32xx
+
+#endif // #ifndef _WIN32XX_TOOLBAR_H_
diff --git a/mmc_updater/depends/win32cpp/treeview.h b/mmc_updater/depends/win32cpp/treeview.h
new file mode 100644
index 00000000..4186e9ce
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/treeview.h
@@ -0,0 +1,624 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+
+
+#ifndef _WIN32XX_TREEVIEW_H_
+#define _WIN32XX_TREEVIEW_H_
+
+#include "wincore.h"
+#include "commctrl.h"
+
+// Disable macros from Windowsx.h
+#undef GetNextSibling
+#undef GetPrevSibling
+
+namespace Win32xx
+{
+
+ class CTreeView : public CWnd
+ {
+ public:
+ CTreeView() {}
+ virtual ~CTreeView() {}
+ virtual void PreRegisterClass(WNDCLASS &wc);
+
+// Attributes
+ COLORREF GetBkColor() const;
+ HTREEITEM GetChild(HTREEITEM hItem) const;
+ UINT GetCount() const;
+ HTREEITEM GetDropHiLightItem() const;
+ HWND GetEditControl() const;
+ HTREEITEM GetFirstVisible() const;
+ HIMAGELIST GetImageList(int iImageType) const;
+ UINT GetIndent() const;
+ COLORREF GetInsertMarkColor() const;
+ BOOL GetItem(TVITEM& Item) const;
+ DWORD_PTR GetItemData(HTREEITEM hItem) const;
+ int GetItemHeight() const;
+ BOOL GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage ) const;
+ BOOL GetItemRect(HTREEITEM hItem, CRect& rc, BOOL bTextOnly) const;
+ tString GetItemText(HTREEITEM hItem, UINT nTextMax /* = 260 */) const;
+ HTREEITEM GetLastVisible() const;
+ HTREEITEM GetNextItem(HTREEITEM hItem, UINT nCode) const;
+ HTREEITEM GetNextSibling(HTREEITEM hItem) const;
+ HTREEITEM GetNextVisible(HTREEITEM hItem) const;
+ HTREEITEM GetParentItem(HTREEITEM hItem) const;
+ HTREEITEM GetPrevSibling(HTREEITEM hItem) const;
+ HTREEITEM GetPrevVisible(HTREEITEM hItem) const;
+ HTREEITEM GetRootItem() const;
+ int GetScrollTime() const;
+ HTREEITEM GetSelection() const;
+ COLORREF GetTextColor() const;
+ HWND GetToolTips() const;
+ UINT GetVisibleCount() const;
+ BOOL ItemHasChildren(HTREEITEM hItem) const;
+ COLORREF SetBkColor(COLORREF clrBk) const;
+ HIMAGELIST SetImageList(HIMAGELIST himl, int nType) const;
+ void SetIndent(int indent) const;
+ BOOL SetInsertMark(HTREEITEM hItem, BOOL fAfter = TRUE) const;
+ COLORREF SetInsertMarkColor(COLORREF clrInsertMark) const;
+ BOOL SetItem(TVITEM& Item) const;
+ BOOL SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR szText, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) const;
+ BOOL SetItemData(HTREEITEM hItem, DWORD_PTR dwData) const;
+ int SetItemHeight(SHORT cyItem) const;
+ BOOL SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage) const;
+ BOOL SetItemText(HTREEITEM hItem, LPCTSTR szText) const;
+ UINT SetScrollTime(UINT uScrollTime) const;
+ COLORREF SetTextColor(COLORREF clrText) const;
+ HWND SetToolTips(HWND hwndTooltip) const;
+
+// Operations
+ HIMAGELIST CreateDragImage(HTREEITEM hItem) const;
+ BOOL DeleteAllItems() const;
+ BOOL DeleteItem(HTREEITEM hItem) const;
+ HWND EditLabel(HTREEITEM hItem) const;
+ BOOL EndEditLabelNow(BOOL fCancel) const;
+ BOOL EnsureVisible(HTREEITEM hItem) const;
+ BOOL Expand(HTREEITEM hItem, UINT nCode) const;
+ HTREEITEM HitTest(TVHITTESTINFO& ht) const;
+ HTREEITEM InsertItem(TVINSERTSTRUCT& tvIS) const;
+ BOOL Select(HTREEITEM hitem, UINT flag) const;
+ BOOL SelectDropTarget(HTREEITEM hItem) const;
+ BOOL SelectItem(HTREEITEM hItem) const;
+ BOOL SelectSetFirstVisible(HTREEITEM hItem) const;
+ BOOL SortChildren(HTREEITEM hItem, BOOL fRecurse) const;
+ BOOL SortChildrenCB(TVSORTCB& sort, BOOL fRecurse) const;
+
+ private:
+ CTreeView(const CTreeView&); // Disable copy construction
+ CTreeView& operator = (const CTreeView&); // Disable assignment operator
+
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+
+ inline void CTreeView::PreRegisterClass(WNDCLASS &wc)
+ {
+ // Set the Window Class
+ wc.lpszClassName = WC_TREEVIEW;
+ }
+
+// Attributes
+ inline COLORREF CTreeView::GetBkColor() const
+ // Retrieves the current background color of the control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetBkColor( m_hWnd );
+ }
+
+ inline HTREEITEM CTreeView::GetChild(HTREEITEM hItem) const
+ // Retrieves the first child item of the specified tree-view item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetChild(m_hWnd, hItem);
+ }
+
+ inline UINT CTreeView::GetCount() const
+ // Retrieves a count of the items in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetCount( m_hWnd );
+ }
+
+ inline HTREEITEM CTreeView::GetDropHiLightItem() const
+ // Retrieves the tree-view item that is the target of a drag-and-drop operation.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetDropHilight(m_hWnd);
+ }
+
+ inline HWND CTreeView::GetEditControl() const
+ // Retrieves the handle to the edit control being used to edit a tree-view item's text.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetEditControl( m_hWnd );
+ }
+
+ inline HTREEITEM CTreeView::GetFirstVisible() const
+ // Retrieves the first visible item in a tree-view control window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetFirstVisible(m_hWnd);
+ }
+
+ inline HIMAGELIST CTreeView::GetImageList(int iImageType) const
+ // Retrieves the handle to the normal or state image list associated with a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetImageList( m_hWnd, iImageType );
+ }
+
+ inline UINT CTreeView::GetIndent() const
+ // Retrieves the amount, in pixels, that child items are indented relative to their parent items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetIndent( m_hWnd );
+ }
+
+ inline COLORREF CTreeView::GetInsertMarkColor() const
+ // Retrieves the color used to draw the insertion mark for the tree view.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetInsertMarkColor( m_hWnd );
+ }
+
+ inline BOOL CTreeView::GetItem(TVITEM& Item) const
+ // Retrieves some or all of a tree-view item's attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetItem( m_hWnd, &Item );
+ }
+
+ inline DWORD_PTR CTreeView::GetItemData(HTREEITEM hItem) const
+ // Retrieves a tree-view item's application data.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TVITEM tvi = {0};
+ tvi.mask = TVIF_PARAM;
+ tvi.hItem = hItem;
+ TreeView_GetItem( m_hWnd, &tvi );
+ return tvi.lParam;
+ }
+
+ inline int CTreeView::GetItemHeight() const
+ // Retrieves the current height of the tree-view item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetItemHeight( m_hWnd );
+ }
+
+ inline BOOL CTreeView::GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage ) const
+ // Retrieves the index of the tree-view item's image and selected image.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TVITEM tvi = {0};
+ tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = hItem;
+ BOOL bResult = TreeView_GetItem( m_hWnd, &tvi );
+ nImage = tvi.iImage;
+ nSelectedImage = tvi.iSelectedImage;
+ return bResult;
+ }
+
+ inline BOOL CTreeView::GetItemRect(HTREEITEM hItem, CRect& rc, BOOL bTextOnly) const
+ // Retrieves the bounding rectangle for a tree-view item and indicates whether the item is visible.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetItemRect( m_hWnd, hItem, &rc, bTextOnly );
+ }
+
+ inline tString CTreeView::GetItemText(HTREEITEM hItem, UINT nTextMax /* = 260 */) const
+ // Retrieves the text for a tree-view item.
+ // Note: Although the tree-view control allows any length string to be stored
+ // as item text, only the first 260 characters are displayed.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ tString t;
+ if (nTextMax > 0)
+ {
+ TVITEM tvi = {0};
+ tvi.hItem = hItem;
+ tvi.mask = TVIF_TEXT;
+ tvi.cchTextMax = nTextMax;
+ std::vector<TCHAR> vTChar(nTextMax +1, _T('\0'));
+ TCHAR* pTCharArray = &vTChar.front();
+ tvi.pszText = pTCharArray;
+ ::SendMessage(m_hWnd, TVM_GETITEM, 0L, (LPARAM)&tvi);
+ t = tvi.pszText;
+ }
+ return t;
+ }
+
+ inline HTREEITEM CTreeView::GetLastVisible() const
+ // Retrieves the last expanded item in a tree-view control.
+ // This does not retrieve the last item visible in the tree-view window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetLastVisible(m_hWnd);
+ }
+
+ inline HTREEITEM CTreeView::GetNextItem(HTREEITEM hItem, UINT nCode) const
+ // Retrieves the tree-view item that bears the specified relationship to a specified item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetNextItem( m_hWnd, hItem, nCode);
+ }
+
+ inline HTREEITEM CTreeView::GetNextSibling(HTREEITEM hItem) const
+ // Retrieves the next sibling item of a specified item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetNextSibling(m_hWnd, hItem);
+ }
+
+ inline HTREEITEM CTreeView::GetNextVisible(HTREEITEM hItem) const
+ // Retrieves the next visible item that follows a specified item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetNextVisible(m_hWnd, hItem);
+ }
+
+ inline HTREEITEM CTreeView::GetParentItem(HTREEITEM hItem) const
+ // Retrieves the parent item of the specified tree-view item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetParent(m_hWnd, hItem);
+ }
+
+ inline HTREEITEM CTreeView::GetPrevSibling(HTREEITEM hItem) const
+ // Retrieves the previous sibling item of a specified item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetPrevSibling(m_hWnd, hItem);
+ }
+
+ inline HTREEITEM CTreeView::GetPrevVisible(HTREEITEM hItem) const
+ // Retrieves the first visible item that precedes a specified item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetPrevSibling(m_hWnd, hItem);
+ }
+
+ inline HTREEITEM CTreeView::GetRootItem() const
+ // Retrieves the topmost or very first item of the tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetRoot(m_hWnd);
+ }
+
+ inline int CTreeView::GetScrollTime() const
+ // Retrieves the maximum scroll time for the tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetScrollTime( m_hWnd );
+ }
+
+ inline HTREEITEM CTreeView::GetSelection() const
+ // Retrieves the currently selected item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetSelection(m_hWnd);
+ }
+
+ inline COLORREF CTreeView::GetTextColor() const
+ // Retrieves the current text color of the control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetTextColor( m_hWnd );
+ }
+
+ inline HWND CTreeView::GetToolTips() const
+ // Retrieves the handle to the child ToolTip control used by a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetToolTips( m_hWnd );
+ }
+
+ inline UINT CTreeView::GetVisibleCount() const
+ // Obtains the number of items that can be fully visible in the client window of a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_GetVisibleCount( m_hWnd );
+ }
+
+ inline BOOL CTreeView::ItemHasChildren(HTREEITEM hItem) const
+ // Returns true of the tree-view item has one or more children
+ {
+ assert(::IsWindow(m_hWnd));
+
+ if (TreeView_GetChild( m_hWnd, hItem ))
+ return TRUE;
+
+ return FALSE;
+ }
+
+ inline COLORREF CTreeView::SetBkColor(COLORREF clrBk) const
+ // Sets the background color of the control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetBkColor( m_hWnd, clrBk );
+ }
+
+ inline HIMAGELIST CTreeView::SetImageList(HIMAGELIST himl, int nType) const
+ // Sets the normal or state image list for a tree-view control
+ // and redraws the control using the new images.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetImageList( m_hWnd, himl, nType );
+ }
+
+ inline void CTreeView::SetIndent(int indent) const
+ // Sets the width of indentation for a tree-view control
+ // and redraws the control to reflect the new width.
+ {
+ assert(::IsWindow(m_hWnd));
+ TreeView_SetIndent( m_hWnd, indent );
+ }
+
+ inline BOOL CTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter/* = TRUE*/) const
+ // Sets the insertion mark in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetInsertMark( m_hWnd, hItem, fAfter );
+ }
+
+ inline COLORREF CTreeView::SetInsertMarkColor(COLORREF clrInsertMark) const
+ // Sets the color used to draw the insertion mark for the tree view.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetInsertMarkColor( m_hWnd, clrInsertMark );
+ }
+
+ inline BOOL CTreeView::SetItem(TVITEM& Item) const
+ // Sets some or all of a tree-view item's attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetItem( m_hWnd, &Item );
+ }
+
+ inline BOOL CTreeView::SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR szText, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) const
+ // Sets some or all of a tree-view item's attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TVITEM tvi = {0};
+ tvi.hItem = hItem;
+ tvi.mask = nMask;
+ tvi.pszText = (LPTSTR)szText;
+ tvi.iImage = nImage;
+ tvi.iSelectedImage = nSelectedImage;
+ tvi.state = nState;
+ tvi.stateMask = nStateMask;
+ tvi.lParam = lParam;
+ return TreeView_SetItem( m_hWnd, &tvi );
+ }
+
+ inline BOOL CTreeView::SetItemData(HTREEITEM hItem, DWORD_PTR dwData) const
+ // Sets the tree-view item's application data.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TVITEM tvi = {0};
+ tvi.hItem = hItem;
+ tvi.mask = TVIF_PARAM;
+ tvi.lParam = dwData;
+ return TreeView_SetItem( m_hWnd, &tvi );
+ }
+
+ inline int CTreeView::SetItemHeight(SHORT cyItem) const
+ // Sets the height of the tree-view items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetItemHeight( m_hWnd, cyItem );
+ }
+
+ inline BOOL CTreeView::SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage) const
+ // Sets the tree-view item's application image.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TVITEM tvi = {0};
+ tvi.hItem = hItem;
+ tvi.iImage = nImage;
+ tvi.iSelectedImage = nSelectedImage;
+ tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ return TreeView_SetItem(m_hWnd, &tvi );
+ }
+
+ inline BOOL CTreeView::SetItemText(HTREEITEM hItem, LPCTSTR szText) const
+ // Sets the tree-view item's application text.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ TVITEM tvi = {0};
+ tvi.hItem = hItem;
+ tvi.pszText = (LPTSTR)szText;
+ tvi.mask = TVIF_TEXT;
+ return TreeView_SetItem(m_hWnd, &tvi );
+ }
+
+ inline UINT CTreeView::SetScrollTime(UINT uScrollTime) const
+ // Sets the maximum scroll time for the tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetScrollTime( m_hWnd, uScrollTime );
+ }
+
+ inline COLORREF CTreeView::SetTextColor(COLORREF clrText) const
+ // Sets the text color of the control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetTextColor( m_hWnd, clrText );
+ }
+
+ inline HWND CTreeView::SetToolTips(HWND hwndTooltip) const
+ // Sets a tree-view control's child ToolTip control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SetToolTips( m_hWnd, hwndTooltip );
+ }
+
+ // Operations
+
+ inline HIMAGELIST CTreeView::CreateDragImage(HTREEITEM hItem) const
+ // Creates a dragging bitmap for the specified item in a tree-view control.
+ // It also creates an image list for the bitmap and adds the bitmap to the image list.
+ // An application can display the image when dragging the item by using the image list functions.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_CreateDragImage( m_hWnd, hItem );
+ }
+
+ inline BOOL CTreeView::DeleteAllItems() const
+ // Deletes all items from a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_DeleteAllItems( m_hWnd );
+ }
+
+ inline BOOL CTreeView::DeleteItem(HTREEITEM hItem) const
+ // Removes an item and all its children from a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_DeleteItem( m_hWnd, hItem );
+ }
+
+ inline HWND CTreeView::EditLabel(HTREEITEM hItem) const
+ // Begins in-place editing of the specified item's text, replacing the text of the item
+ // with a single-line edit control containing the text.
+ // The specified item is implicitly selected and focused.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_EditLabel( m_hWnd, hItem );
+ }
+
+ inline BOOL CTreeView::EndEditLabelNow(BOOL fCancel) const
+ // Ends the editing of a tree-view item's label.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_EndEditLabelNow(m_hWnd, fCancel);
+ }
+
+ inline BOOL CTreeView::EnsureVisible(HTREEITEM hItem) const
+ // Ensures that a tree-view item is visible, expanding the parent item or
+ // scrolling the tree-view control, if necessary.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_EnsureVisible( m_hWnd, hItem );
+ }
+
+ inline BOOL CTreeView::Expand(HTREEITEM hItem, UINT nCode) const
+ // The TreeView_Expand macro expands or collapses the list of child items associated
+ // with the specified parent item, if any.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_Expand( m_hWnd, hItem, nCode );
+ }
+
+ inline HTREEITEM CTreeView::HitTest(TVHITTESTINFO& ht) const
+ // Determines the location of the specified point relative to the client area of a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_HitTest( m_hWnd, &ht );
+ }
+
+ inline HTREEITEM CTreeView::InsertItem(TVINSERTSTRUCT& tvIS) const
+ // Inserts a new item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_InsertItem( m_hWnd, &tvIS );
+ }
+
+ inline BOOL CTreeView::Select(HTREEITEM hitem, UINT flag) const
+ // Selects the specified tree-view item, scrolls the item into view, or redraws
+ // the item in the style used to indicate the target of a drag-and-drop operation.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_Select(m_hWnd, hitem, flag );
+ }
+
+ inline BOOL CTreeView::SelectDropTarget(HTREEITEM hItem) const
+ // Redraws a specified tree-view control item in the style used to indicate the
+ // target of a drag-and-drop operation.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SelectDropTarget(m_hWnd, hItem);
+ }
+
+ inline BOOL CTreeView::SelectItem(HTREEITEM hItem) const
+ // Selects the specified tree-view item.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SelectItem(m_hWnd, hItem);
+ }
+
+ inline BOOL CTreeView::SelectSetFirstVisible(HTREEITEM hItem) const
+ // Scrolls the tree-view control vertically to ensure that the specified item is visible.
+ // If possible, the specified item becomes the first visible item at the top of the control's window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SelectSetFirstVisible(m_hWnd, hItem);
+ }
+
+ inline BOOL CTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse) const
+ // Sorts the child items of the specified parent item in a tree-view control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SortChildren( m_hWnd, hItem, fRecurse );
+ }
+
+ inline BOOL CTreeView::SortChildrenCB(TVSORTCB& sort, BOOL fRecurse) const
+ // Sorts tree-view items using an application-defined callback function that compares the items.
+ {
+ assert(::IsWindow(m_hWnd));
+ return TreeView_SortChildrenCB( m_hWnd, &sort, fRecurse );
+ }
+
+
+} // namespace Win32xx
+
+#endif // #ifndef _WIN32XX_TREEVIEW_H_
+
diff --git a/mmc_updater/depends/win32cpp/wceframe.h b/mmc_updater/depends/win32cpp/wceframe.h
new file mode 100644
index 00000000..f3aa67ef
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/wceframe.h
@@ -0,0 +1,420 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////
+// WceFrame.h
+// Definitions for the CCmdBar and CWceFrame
+
+// These classes are provide a frame window for use on Window CE devices such
+// as Pocket PCs. The frame uses CommandBar (a control unique to the Windows CE
+// operating systems) to display the menu and toolbar.
+//
+// Use the PocketPCWceFrame generic application as the starting point for your own
+// frame based applications on the Pocket PC.
+//
+// Refer to the Scribble demo application for an example of how these classes
+// can be used.
+
+
+#ifndef _WIN32XX_WCEFRAME_H_
+#define _WIN32XX_WCEFRAME_H_
+
+
+#include "wincore.h"
+#include <commctrl.h>
+#include <vector>
+#include "default_resource.h"
+
+#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)
+ #define SHELL_AYGSHELL
+#endif
+
+#ifdef SHELL_AYGSHELL
+ #include <aygshell.h>
+ #pragma comment(lib, "aygshell.lib")
+#endif // SHELL_AYGSHELL
+
+#if (_WIN32_WCE < 0x500 && defined(SHELL_AYGSHELL)) || _WIN32_WCE == 420
+ #pragma comment(lib, "ccrtrtti.lib")
+#endif
+
+
+namespace Win32xx
+{
+
+ ////////////////////////////////////
+ // Declaration of the CCmdBar class
+ //
+ class CCmdBar : public CWnd
+ {
+ public:
+ CCmdBar();
+ virtual ~CCmdBar();
+ virtual BOOL AddAdornments(DWORD dwFlags);
+ virtual int AddBitmap(int idBitmap, int iNumImages, int iImageWidth, int iImageHeight);
+ virtual BOOL AddButtons(int nButtons, TBBUTTON* pTBButton);
+ virtual HWND Create(HWND hwndParent);
+ virtual int GetHeight() const;
+ virtual HWND InsertComboBox(int iWidth, UINT dwStyle, WORD idComboBox, WORD iButton);
+ virtual BOOL IsVisible();
+ virtual BOOL Show(BOOL fShow);
+
+ private:
+
+#ifdef SHELL_AYGSHELL
+ SHMENUBARINFO m_mbi;
+#endif
+
+ };
+
+
+ //////////////////////////////////////
+ // Declaration of the CWceFrame class
+ // A mini frame based on CCmdBar
+ class CWceFrame : public CWnd
+ {
+ public:
+ CWceFrame();
+ virtual ~CWceFrame();
+ virtual void AddToolBarButton(UINT nID);
+ CRect GetViewRect() const;
+ CCmdBar& GetMenuBar() const {return (CCmdBar&)m_MenuBar;}
+ virtual void OnActivate(WPARAM wParam, LPARAM lParam);
+ virtual void OnCreate();
+ virtual void PreCreate(CREATESTRUCT &cs);
+ virtual void RecalcLayout();
+ virtual void SetButtons(const std::vector<UINT> ToolBarData);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ protected:
+ std::vector<UINT> m_ToolBarData;
+
+ private:
+ CCmdBar m_MenuBar;
+ tString m_tsAppName;
+
+#ifdef SHELL_AYGSHELL
+ SHACTIVATEINFO m_sai;
+#endif
+
+ };
+
+ //////////////////////////////////////////
+ // Definitions for the CCmdBar class
+ // This class wraps CommandBar_Create which
+ // creates a CommandBar at the top of the window
+ inline CCmdBar::CCmdBar()
+ {
+ }
+
+ inline CCmdBar::~CCmdBar()
+ {
+ if (IsWindow())
+ ::CommandBar_Destroy(m_hWnd);
+ }
+
+
+ inline BOOL CCmdBar::AddAdornments(DWORD dwFlags)
+ {
+ BOOL bReturn = CommandBar_AddAdornments(m_hWnd, dwFlags, 0);
+
+ if (!bReturn)
+ throw CWinException(_T("AddAdornments failed"));
+
+ return bReturn;
+ }
+
+ inline int CCmdBar::AddBitmap(int idBitmap, int iNumImages, int iImageWidth, int iImageHeight)
+ {
+ HINSTANCE hInst = GetApp()->GetInstanceHandle();
+ return CommandBar_AddBitmap(m_hWnd, hInst, idBitmap, iNumImages, iImageWidth, iImageHeight);
+ }
+
+ inline BOOL CCmdBar::AddButtons(int nButtons, TBBUTTON* pTBButton)
+ {
+ BOOL bReturn = CommandBar_AddButtons(m_hWnd, nButtons, pTBButton);
+ if (!bReturn)
+ throw CWinException(_T("Failed to add buttons to commandbar"));
+
+ return bReturn;
+ }
+
+ inline HWND CCmdBar::Create(HWND hParent)
+ {
+#ifdef SHELL_AYGSHELL
+ SHMENUBARINFO mbi;
+
+ memset(&mbi, 0, sizeof(SHMENUBARINFO));
+ mbi.cbSize = sizeof(SHMENUBARINFO);
+ mbi.hwndParent = hParent;
+ mbi.nToolBarId = IDW_MAIN;
+ mbi.hInstRes = GetApp()->GetInstanceHandle();
+ mbi.nBmpId = 0;
+ mbi.cBmpImages = 0;
+
+ if (SHCreateMenuBar(&mbi))
+ {
+ m_hWnd = mbi.hwndMB;
+ }
+ else
+ throw CWinException(_T("Failed to create MenuBar"));
+
+#else
+ m_hWnd = CommandBar_Create(GetApp()->GetInstanceHandle(), hParent, IDW_MENUBAR);
+
+ if (m_hWnd == NULL)
+ throw CWinException(_T("Failed to create CommandBar"));
+
+ CommandBar_InsertMenubar(m_hWnd, GetApp()->GetInstanceHandle(), IDW_MAIN, 0);
+#endif
+ return m_hWnd;
+ }
+
+ inline int CCmdBar::GetHeight() const
+ {
+ return CommandBar_Height(m_hWnd);
+ }
+
+ inline HWND CCmdBar::InsertComboBox(int iWidth, UINT dwStyle, WORD idComboBox, WORD iButton)
+ {
+ HINSTANCE hInst = GetApp()->GetInstanceHandle();
+ HWND hWnd = CommandBar_InsertComboBox(m_hWnd, hInst, iWidth, dwStyle, idComboBox, iButton);
+
+ if (!hWnd)
+ throw CWinException(_T("InsertComboBox failed"));
+
+ return hWnd;
+ }
+
+ inline BOOL CCmdBar::IsVisible()
+ {
+ return ::CommandBar_IsVisible(m_hWnd);
+ }
+
+ inline BOOL CCmdBar::Show(BOOL fShow)
+ {
+ return ::CommandBar_Show(m_hWnd, fShow);
+ }
+
+
+ /////////////////////////////////////////
+ // Definitions for the CWceFrame class
+ // This class creates a simple frame using CCmdBar
+ inline CWceFrame::CWceFrame()
+ {
+#ifdef SHELL_AYGSHELL
+ // Initialize the shell activate info structure
+ memset (&m_sai, 0, sizeof (m_sai));
+ m_sai.cbSize = sizeof (m_sai);
+#endif
+ }
+
+ inline CWceFrame::~CWceFrame()
+ {
+ }
+
+ inline void CWceFrame::AddToolBarButton(UINT nID)
+ // Adds Resource IDs to toolbar buttons.
+ // A resource ID of 0 is a separator
+ {
+ m_ToolBarData.push_back(nID);
+ }
+
+ inline CRect CWceFrame::GetViewRect() const
+ {
+ CRect r;
+ ::GetClientRect(m_hWnd, &r);
+
+#ifndef SHELL_AYGSHELL
+ // Reduce the size of the client rectange, by the commandbar height
+ r.top += m_MenuBar.GetHeight();
+#endif
+
+ return r;
+ }
+
+ inline void CWceFrame::OnCreate()
+ {
+ // Create the Commandbar
+ m_MenuBar.Create(m_hWnd);
+
+ // Set the keyboard accelerators
+ HACCEL hAccel = LoadAccelerators(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_MAIN));
+ GetApp()->SetAccelerators(hAccel, this);
+
+ // Add the toolbar buttons
+ if (m_ToolBarData.size() > 0)
+ SetButtons(m_ToolBarData);
+
+#ifndef SHELL_AYGSHELL
+ // Add close button
+ m_MenuBar.AddAdornments(0);
+#endif
+
+ }
+
+ inline void CWceFrame::OnActivate(WPARAM wParam, LPARAM lParam)
+ {
+#ifdef SHELL_AYGSHELL
+ // Notify shell of our activate message
+ SHHandleWMActivate(m_hWnd, wParam, lParam, &m_sai, FALSE);
+
+ UINT fActive = LOWORD(wParam);
+ if ((fActive == WA_ACTIVE) || (fActive == WA_CLICKACTIVE))
+ {
+ // Reposition the window when it's activated
+ RecalcLayout();
+ }
+#endif
+ }
+
+ inline void CWceFrame::PreCreate(CREATESTRUCT &cs)
+ {
+ cs.style = WS_VISIBLE;
+ m_tsAppName = _T("Win32++ Application");
+
+ // Choose a unique class name for this app
+ if (LoadString(IDW_MAIN) != _T(""))
+ {
+ m_tsAppName = LoadString(IDW_MAIN);
+ }
+
+ cs.lpszClass = m_tsAppName.c_str();
+ }
+
+/* inline BOOL CWceFrame::PreTranslateMessage(MSG* pMsg)
+ {
+ HACCEL hAccelTable = ::LoadAccelerators(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_MAIN));
+ if (WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST)
+ {
+ if (TranslateAccelerator(m_hWnd, hAccelTable, pMsg))
+ return TRUE;
+ }
+ return CWnd::PreTranslateMessage(pMsg);
+ } */
+
+ inline void CWceFrame::RecalcLayout()
+ {
+ HWND hwndCB = m_MenuBar.GetHwnd();
+ if (hwndCB)
+ {
+ CRect rc; // Desktop window size
+ CRect rcMenuBar; // MenuBar window size
+
+ ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
+ ::GetWindowRect(hwndCB, &rcMenuBar);
+ rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);
+
+ MoveWindow(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE);
+ }
+
+ ShowWindow(TRUE);
+ UpdateWindow();
+ }
+
+ inline void CWceFrame::SetButtons(const std::vector<UINT> ToolBarData)
+ // Define the resource IDs for the toolbar like this in the Frame's constructor
+ // m_ToolBarData.push_back ( 0 ); // Separator
+ // m_ToolBarData.clear();
+ // m_ToolBarData.push_back ( IDM_FILE_NEW );
+ // m_ToolBarData.push_back ( IDM_FILE_OPEN );
+ // m_ToolBarData.push_back ( IDM_FILE_SAVE );
+
+ {
+ int iImages = 0;
+ int iNumButtons = (int)ToolBarData.size();
+
+
+ if (iNumButtons > 0)
+ {
+ // Create the TBBUTTON array for each button
+ std::vector<TBBUTTON> vTBB(iNumButtons);
+ TBBUTTON* tbbArray = &vTBB.front();
+
+ for (int j = 0 ; j < iNumButtons; j++)
+ {
+ ZeroMemory(&tbbArray[j], sizeof(TBBUTTON));
+
+ if (ToolBarData[j] == 0)
+ {
+ tbbArray[j].fsStyle = TBSTYLE_SEP;
+ }
+ else
+ {
+ tbbArray[j].iBitmap = iImages++;
+ tbbArray[j].idCommand = ToolBarData[j];
+ tbbArray[j].fsState = TBSTATE_ENABLED;
+ tbbArray[j].fsStyle = TBSTYLE_BUTTON;
+ tbbArray[j].iString = -1;
+ }
+ }
+
+ // Add the bitmap
+ GetMenuBar().AddBitmap(IDW_MAIN, iImages , 16, 16);
+
+ // Add the buttons
+ GetMenuBar().AddButtons(iNumButtons, tbbArray);
+ }
+ }
+
+ inline LRESULT CWceFrame::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ case WM_ACTIVATE:
+ OnActivate(wParam, lParam);
+ break;
+
+#ifdef SHELL_AYGSHELL
+
+ case WM_SETTINGCHANGE:
+ SHHandleWMSettingChange(m_hWnd, wParam, lParam, &m_sai);
+ break;
+#endif
+
+ }
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+
+} // namespace Win32xx
+
+#endif // _WIN32XX_WCEFRAME_H_
+
diff --git a/mmc_updater/depends/win32cpp/wcestddef.h b/mmc_updater/depends/win32cpp/wcestddef.h
new file mode 100644
index 00000000..f7b22833
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/wcestddef.h
@@ -0,0 +1,58 @@
+
+#pragma once
+
+#pragma comment(linker, "/nodefaultlib:libc.lib")
+#pragma comment(linker, "/nodefaultlib:libcd.lib")
+
+
+#include <ceconfig.h>
+#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)
+ #define SHELL_AYGSHELL
+#endif
+
+#ifdef _CE_DCOM
+ #define _ATL_APARTMENT_THREADED
+#endif
+
+#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)
+ #ifndef _DEVICE_RESOLUTION_AWARE
+ #define _DEVICE_RESOLUTION_AWARE
+ #endif
+#endif
+
+
+#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
+ // For Pocket PC 2003
+ #pragma comment(lib, "ccrtrtti.lib")
+#endif
+
+#if _MSC_VER >= 1300
+
+ // NOTE - this value is not strongly correlated to the Windows CE OS version being targeted
+ #undef WINVER
+ #define WINVER _WIN32_WCE
+
+ #ifdef _DEVICE_RESOLUTION_AWARE
+ #include "DeviceResolutionAware.h"
+ #endif
+
+ #if _WIN32_WCE < 0x500 && ( defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP) )
+ #ifdef _X86_
+ #if defined(_DEBUG)
+ #pragma comment(lib, "libcmtx86d.lib")
+ #else
+ #pragma comment(lib, "libcmtx86.lib")
+ #endif
+ #endif
+ #endif
+
+ #include <altcecrt.h>
+
+#endif// _MSC_VER >= 1300
+
+#ifdef SHELL_AYGSHELL
+ #include <aygshell.h>
+ #pragma comment(lib, "aygshell.lib")
+#endif // SHELL_AYGSHELL
+
+// TODO: reference additional headers your program requires here
diff --git a/mmc_updater/depends/win32cpp/webbrowser.h b/mmc_updater/depends/win32cpp/webbrowser.h
new file mode 100644
index 00000000..5a5b5f41
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/webbrowser.h
@@ -0,0 +1,760 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+#ifndef _WIN32XX_WEBBROWSER_H_
+#define _WIN32XX_WEBBROWSER_H_
+
+#include <exdisp.h>
+#include <ocidl.h>
+
+
+namespace Win32xx
+{
+ ///////////////////////////////////////////////////
+ // Declaration of the CAXWindow class
+ // This class implements an ActiveX control container
+ class CAXWindow : public IOleClientSite, public IOleInPlaceSite, public IOleInPlaceFrame,
+ public IOleControlSite, public IDispatch
+ {
+ public:
+ CAXWindow();
+ virtual ~CAXWindow();
+ virtual void Activate(BOOL fFocus);
+ virtual void CreateControl(BSTR bstrClsid);
+ virtual void CreateControl(CLSID clsid);
+ virtual void Remove();
+ virtual void SetParent(HWND hWndParent);
+ virtual void SetLocation(int x, int y, int width, int height);
+ virtual void SetVisible(BOOL fVisible);
+ virtual void SetStatusWindow(HWND hWndStatus);
+ virtual void TranslateKey(MSG msg);
+ IDispatch* GetDispatch();
+ IUnknown* GetUnknown();
+
+ // IUnknown Methods
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IOleClientSite Methods
+ STDMETHODIMP SaveObject();
+ STDMETHODIMP GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMk);
+ STDMETHODIMP GetContainer(LPOLECONTAINER* ppContainer);
+ STDMETHODIMP ShowObject();
+ STDMETHODIMP OnShowWindow(BOOL fShow);
+ STDMETHODIMP RequestNewObjectLayout();
+
+ // IOleWindow Methods
+ STDMETHODIMP GetWindow(HWND* phwnd);
+ STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
+
+ // IOleInPlaceSite Methods
+ STDMETHODIMP CanInPlaceActivate();
+ STDMETHODIMP OnInPlaceActivate();
+ STDMETHODIMP OnUIActivate();
+ STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
+ STDMETHODIMP Scroll(SIZE scrollExtent);
+ STDMETHODIMP OnUIDeactivate(BOOL fUndoable);
+ STDMETHODIMP OnInPlaceDeactivate();
+ STDMETHODIMP DiscardUndoState();
+ STDMETHODIMP DeactivateAndUndo();
+ STDMETHODIMP OnPosRectChange(LPCRECT lprcPosRect);
+
+ // IOleInPlaceUIWindow Methods
+ STDMETHODIMP GetBorder(LPRECT lprectBorder);
+ STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths);
+ STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS lpborderwidths);
+ STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR lpszObjName);
+
+ // IOleInPlaceFrame Methods
+ STDMETHODIMP InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
+ STDMETHODIMP SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
+ STDMETHODIMP RemoveMenus(HMENU hmenuShared);
+ STDMETHODIMP SetStatusText(LPCOLESTR pszStatusText);
+ STDMETHODIMP EnableModeless(BOOL fEnable);
+ STDMETHODIMP TranslateAccelerator(LPMSG lpmsg, WORD wID);
+
+ // IOleControlSite Methods
+ STDMETHODIMP OnControlInfoChanged();
+ STDMETHODIMP LockInPlaceActive(BOOL fLock);
+ STDMETHODIMP GetExtendedControl(IDispatch** ppDisp);
+ STDMETHODIMP TransformCoords(POINTL* pptlHimetric, POINTF* pptfContainer, DWORD dwFlags);
+ STDMETHODIMP TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers);
+ STDMETHODIMP OnFocus(BOOL fGotFocus);
+ STDMETHODIMP ShowPropertyFrame();
+
+ // IDispatch Methods
+ STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, unsigned int cNames, LCID lcid, DISPID* rgdispid);
+ STDMETHODIMP GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo** pptinfo);
+ STDMETHODIMP GetTypeInfoCount(unsigned int* pctinfo);
+ STDMETHODIMP Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexecinfo, unsigned int* puArgErr);
+
+ private:
+ ULONG m_cRefs; // ref count
+ HWND m_hWnd; // window handle of the container
+ HWND m_hWndStatus; // status window handle
+ IUnknown* m_pUnk; // IUnknown of contained object
+ CRect m_rcControl; // size of control
+ };
+
+
+ ///////////////////////////////////////////////
+ // Declaration of the CWebBrowser class
+ // This class uses an AciveX Container provided by
+ // CAXWindow to host the IWebBrower2 interface.
+ class CWebBrowser : public CWnd
+ {
+ public:
+ CWebBrowser();
+ virtual ~CWebBrowser();
+ virtual void AddWebBrowserControl(void);
+ virtual CAXWindow& GetAXWindow() const { return (CAXWindow&)m_AXContainer; }
+ virtual IWebBrowser2* GetIWebBrowser2() const { return m_pIWebBrowser2; }
+ virtual void Navigate(LPCTSTR str);
+
+ protected:
+ virtual void OnCreate();
+ virtual void OnSize(int width, int height);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ private:
+ CAXWindow m_AXContainer; // The ActiveX Container
+ IWebBrowser2* m_pIWebBrowser2;// Interface to the ActiveX web browser control
+ };
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+namespace Win32xx
+{
+ /////////////////////////////////////////
+ // Definitions for the CAXWindow class
+ //
+ inline CAXWindow::CAXWindow() : m_cRefs(1), m_hWnd(NULL), m_pUnk(NULL)
+ {
+ }
+
+ inline CAXWindow::~CAXWindow()
+ {
+ }
+
+ inline void CAXWindow::CreateControl(BSTR bstrClsid)
+ {
+ CLSID clsid;
+ CLSIDFromString(bstrClsid, &clsid);
+ CreateControl(clsid);
+ }
+
+ inline void CAXWindow::Activate(BOOL fFocus)
+ {
+ if (!m_pUnk)
+ return;
+
+ if (fFocus)
+ {
+ IOleObject* pioo;
+ HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo);
+ if (FAILED(hr))
+ return;
+
+ pioo->DoVerb(OLEIVERB_UIACTIVATE, NULL, this, 0, m_hWnd, &m_rcControl);
+ pioo->Release();
+ }
+ }
+
+ inline void CAXWindow::CreateControl(CLSID clsid)
+ {
+ CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&m_pUnk);
+
+ if (!m_pUnk)
+ return;
+
+ IOleObject* pioo;
+ HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo);
+ if (FAILED(hr))
+ return;
+
+ pioo->SetClientSite(this);
+ pioo->Release();
+
+ IPersistStreamInit* ppsi;
+ hr = m_pUnk->QueryInterface(IID_IPersistStreamInit, (void**)&ppsi);
+ if (SUCCEEDED(hr))
+ {
+ ppsi->InitNew();
+ ppsi->Release();
+ }
+ }
+
+ inline STDMETHODIMP_(ULONG) CAXWindow::AddRef()
+ {
+ return ++m_cRefs;
+ }
+
+ inline STDMETHODIMP CAXWindow::CanInPlaceActivate()
+ {
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::ContextSensitiveHelp(BOOL fEnterMode)
+ {
+ UNREFERENCED_PARAMETER(fEnterMode);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::DeactivateAndUndo()
+ {
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::DiscardUndoState()
+ {
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::EnableModeless(BOOL fEnable)
+ {
+ UNREFERENCED_PARAMETER(fEnable);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetBorder(LPRECT lprectBorder)
+ {
+ UNREFERENCED_PARAMETER(lprectBorder);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetContainer(LPOLECONTAINER* ppContainer)
+ {
+ UNREFERENCED_PARAMETER(ppContainer);
+ return E_NOINTERFACE;
+ }
+
+ inline IDispatch* CAXWindow::GetDispatch()
+ {
+ if (!m_pUnk)
+ return NULL;
+
+ HRESULT hr;
+ IDispatch* pdisp;
+
+ hr = m_pUnk->QueryInterface(IID_IDispatch, (void**)&pdisp);
+ return pdisp;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetExtendedControl(IDispatch** ppDisp)
+ {
+ if (ppDisp == NULL)
+ return E_INVALIDARG;
+
+ *ppDisp = (IDispatch*)this;
+ (*ppDisp)->AddRef();
+
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, unsigned int cNames, LCID lcid, DISPID* rgdispid)
+ {
+ UNREFERENCED_PARAMETER((IID)riid); // IID cast required for the MinGW compiler
+ UNREFERENCED_PARAMETER(rgszNames);
+ UNREFERENCED_PARAMETER(cNames);
+ UNREFERENCED_PARAMETER(lcid);
+
+ *rgdispid = DISPID_UNKNOWN;
+ return DISP_E_UNKNOWNNAME;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMk)
+ {
+ UNREFERENCED_PARAMETER(dwAssign);
+ UNREFERENCED_PARAMETER(dwWhichMoniker);
+ UNREFERENCED_PARAMETER(ppMk);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo** pptinfo)
+ {
+ UNREFERENCED_PARAMETER(itinfo);
+ UNREFERENCED_PARAMETER(lcid);
+ UNREFERENCED_PARAMETER(pptinfo);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetTypeInfoCount(unsigned int* pctinfo)
+ {
+ UNREFERENCED_PARAMETER(pctinfo);
+ return E_NOTIMPL;
+ }
+
+ inline IUnknown* CAXWindow::GetUnknown()
+ {
+ if (!m_pUnk)
+ return NULL;
+
+ m_pUnk->AddRef();
+ return m_pUnk;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetWindow(HWND* lphwnd)
+ {
+ if (!IsWindow(m_hWnd))
+ return S_FALSE;
+
+ *lphwnd = m_hWnd;
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::GetWindowContext (IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppIIPUIWin,
+ LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
+ {
+ *ppFrame = (IOleInPlaceFrame*)this;
+ *ppIIPUIWin = NULL;
+
+ RECT rect;
+ GetClientRect(m_hWnd, &rect);
+ lprcPosRect->left = 0;
+ lprcPosRect->top = 0;
+ lprcPosRect->right = rect.right;
+ lprcPosRect->bottom = rect.bottom;
+
+ CopyRect(lprcClipRect, lprcPosRect);
+
+ lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
+ lpFrameInfo->fMDIApp = FALSE;
+ lpFrameInfo->hwndFrame = m_hWnd;
+ lpFrameInfo->haccel = 0;
+ lpFrameInfo->cAccelEntries = 0;
+
+ (*ppFrame)->AddRef();
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
+ {
+ UNREFERENCED_PARAMETER(hmenuShared);
+ UNREFERENCED_PARAMETER(lpMenuWidths);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexecinfo, unsigned int* puArgErr)
+ {
+ UNREFERENCED_PARAMETER(dispid);
+ UNREFERENCED_PARAMETER((IID)riid); // IID cast required for the MinGW compiler
+ UNREFERENCED_PARAMETER(lcid);
+ UNREFERENCED_PARAMETER(wFlags);
+ UNREFERENCED_PARAMETER(pdispparams);
+ UNREFERENCED_PARAMETER(pvarResult);
+ UNREFERENCED_PARAMETER(pexecinfo);
+ UNREFERENCED_PARAMETER(puArgErr);
+ return DISP_E_MEMBERNOTFOUND;
+ }
+
+ inline STDMETHODIMP CAXWindow::LockInPlaceActive(BOOL fLock)
+ {
+ UNREFERENCED_PARAMETER(fLock);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnControlInfoChanged()
+ {
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnFocus(BOOL fGotFocus)
+ {
+ UNREFERENCED_PARAMETER(fGotFocus);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnInPlaceActivate()
+ {
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnInPlaceDeactivate()
+ {
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnPosRectChange(LPCRECT lprcPosRect)
+ {
+ UNREFERENCED_PARAMETER(lprcPosRect);
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnShowWindow(BOOL fShow)
+ {
+ UNREFERENCED_PARAMETER(fShow);
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnUIActivate()
+ {
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::OnUIDeactivate(BOOL fUndoable)
+ {
+ UNREFERENCED_PARAMETER(fUndoable);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::QueryInterface(REFIID riid, void** ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (IsEqualIID(riid, IID_IOleClientSite))
+ *ppvObject = (IOleClientSite*)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceSite))
+ *ppvObject = (IOleInPlaceSite*)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceFrame))
+ *ppvObject = (IOleInPlaceFrame*)this;
+ else if (IsEqualIID(riid, IID_IOleInPlaceUIWindow))
+ *ppvObject = (IOleInPlaceUIWindow*)this;
+ else if (IsEqualIID(riid, IID_IOleControlSite))
+ *ppvObject = (IOleControlSite*)this;
+ else if (IsEqualIID(riid, IID_IOleWindow))
+ *ppvObject = this;
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch*)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+ }
+
+ inline STDMETHODIMP_(ULONG) CAXWindow::Release()
+ {
+ return --m_cRefs;
+ }
+
+ inline void CAXWindow::Remove()
+ {
+ if (!m_pUnk)
+ return;
+
+ IOleObject* pioo;
+ HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo);
+ if (SUCCEEDED(hr))
+ {
+ pioo->Close(OLECLOSE_NOSAVE);
+ pioo->SetClientSite(NULL);
+ pioo->Release();
+ }
+
+ IOleInPlaceObject* pipo;
+ hr = m_pUnk->QueryInterface(IID_IOleInPlaceObject, (void**)&pipo);
+ if (SUCCEEDED(hr))
+ {
+ pipo->UIDeactivate();
+ pipo->InPlaceDeactivate();
+ pipo->Release();
+ }
+
+ m_pUnk->Release();
+ m_pUnk = NULL;
+ }
+
+ inline STDMETHODIMP CAXWindow::RemoveMenus(HMENU hmenuShared)
+ {
+ UNREFERENCED_PARAMETER(hmenuShared);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths)
+ {
+ UNREFERENCED_PARAMETER(lpborderwidths);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::RequestNewObjectLayout()
+ {
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::SaveObject()
+ {
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::Scroll(SIZE scrollExtent)
+ {
+ UNREFERENCED_PARAMETER(scrollExtent);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR lpszObjName)
+ {
+ UNREFERENCED_PARAMETER(pActiveObject);
+ UNREFERENCED_PARAMETER(lpszObjName);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::SetBorderSpace(LPCBORDERWIDTHS lpborderwidths)
+ {
+ UNREFERENCED_PARAMETER(lpborderwidths);
+ return E_NOTIMPL;
+ }
+
+ inline void CAXWindow::SetLocation(int x, int y, int width, int height)
+ {
+ m_rcControl.SetRect(x, y, x + width, y + height);
+
+ if (!m_pUnk)
+ return;
+
+ IOleInPlaceObject* pipo;
+ HRESULT hr = m_pUnk->QueryInterface(IID_IOleInPlaceObject, (void**)&pipo);
+ if (FAILED(hr))
+ return;
+
+ pipo->SetObjectRects(&m_rcControl, &m_rcControl);
+ pipo->Release();
+ }
+
+ inline STDMETHODIMP CAXWindow::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
+ {
+ UNREFERENCED_PARAMETER(hmenuShared);
+ UNREFERENCED_PARAMETER(holemenu);
+ UNREFERENCED_PARAMETER(hwndActiveObject);
+ return E_NOTIMPL;
+ }
+
+ inline void CAXWindow::SetParent(HWND hWndParent)
+ {
+ m_hWnd = hWndParent;
+ }
+
+ inline STDMETHODIMP CAXWindow::SetStatusText(LPCOLESTR pszStatusText)
+ {
+ if (NULL == pszStatusText)
+ return E_POINTER;
+
+ #ifndef _UNICODE
+ char status[MAX_PATH];
+ // Convert the Wide string to char
+ WideCharToMultiByte(CP_ACP, 0, pszStatusText, -1, status, MAX_PATH, NULL, NULL);
+
+ if (IsWindow(m_hWndStatus))
+ SendMessage(m_hWndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)status);
+ #else
+ if (IsWindow(m_hWndStatus))
+ SendMessage(m_hWndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatusText);
+ #endif
+
+ return (S_OK);
+ }
+
+ inline void CAXWindow::SetStatusWindow(HWND hWndStatus)
+ {
+ m_hWndStatus = hWndStatus;
+ }
+
+ inline void CAXWindow::SetVisible(BOOL fVisible)
+ {
+ if (!m_pUnk)
+ return;
+
+ IOleObject* pioo;
+ HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo);
+ if (FAILED(hr))
+ return;
+
+ if (fVisible)
+ {
+ pioo->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, m_hWnd, &m_rcControl);
+ pioo->DoVerb(OLEIVERB_SHOW, NULL, this, 0, m_hWnd, &m_rcControl);
+ }
+ else
+ pioo->DoVerb(OLEIVERB_HIDE, NULL, this, 0, m_hWnd, NULL);
+
+ pioo->Release();
+ }
+
+ inline STDMETHODIMP CAXWindow::ShowObject()
+ {
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::ShowPropertyFrame()
+ {
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::TransformCoords(POINTL* pptlHimetric, POINTF* pptfContainer, DWORD dwFlags)
+ {
+ UNREFERENCED_PARAMETER(pptlHimetric);
+ UNREFERENCED_PARAMETER(pptfContainer);
+ UNREFERENCED_PARAMETER(dwFlags);
+ return E_NOTIMPL;
+ }
+
+ inline STDMETHODIMP CAXWindow::TranslateAccelerator(LPMSG lpmsg, WORD wID)
+ {
+ UNREFERENCED_PARAMETER(lpmsg);
+ UNREFERENCED_PARAMETER(wID);
+ return S_OK;
+ }
+
+ inline STDMETHODIMP CAXWindow::TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers)
+ {
+ UNREFERENCED_PARAMETER(pMsg);
+ UNREFERENCED_PARAMETER(grfModifiers);
+ return S_FALSE;
+ }
+
+ inline void CAXWindow::TranslateKey(MSG msg)
+ {
+ if (!m_pUnk)
+ return;
+
+ IOleInPlaceActiveObject* pao;
+ HRESULT hr = m_pUnk->QueryInterface(IID_IOleInPlaceActiveObject, (void**)&pao);
+ if (FAILED(hr))
+ return;
+
+ pao->TranslateAccelerator(&msg);
+ pao->Release();
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CWebBrowser class
+ //
+ inline CWebBrowser::CWebBrowser() : m_pIWebBrowser2(0)
+ {
+ OleInitialize(NULL);
+ }
+
+ inline CWebBrowser::~CWebBrowser()
+ {
+ if (m_pIWebBrowser2)
+ {
+ m_pIWebBrowser2->Stop();
+ m_pIWebBrowser2->Release();
+ }
+
+ OleUninitialize();
+ }
+
+ inline void CWebBrowser::AddWebBrowserControl()
+ {
+ GetAXWindow().CreateControl(CLSID_WebBrowser);
+ GetAXWindow().SetParent(m_hWnd);
+ GetAXWindow().SetVisible(TRUE);
+ GetAXWindow().Activate(TRUE);
+
+ IUnknown* pUnk = GetAXWindow().GetUnknown();
+ if(pUnk)
+ {
+ // Store the pointer to the WebBrowser control
+ HRESULT hr = pUnk->QueryInterface(IID_IWebBrowser2, (void**)&m_pIWebBrowser2);
+ pUnk->Release();
+
+ // Navigate to an empty page
+ if (SUCCEEDED(hr))
+ {
+ VARIANT vURL;
+ vURL.vt = VT_BSTR;
+ vURL.bstrVal = SysAllocString(L"about:blank");
+ VARIANT ve1, ve2, ve3, ve4;
+ ve1.vt = VT_EMPTY;
+ ve2.vt = VT_EMPTY;
+ ve3.vt = VT_EMPTY;
+ ve4.vt = VT_EMPTY;
+
+ m_pIWebBrowser2->Navigate2(&vURL, &ve1, &ve2, &ve3, &ve4);
+
+ VariantClear(&vURL);
+ }
+ }
+ }
+
+ inline void CWebBrowser::Navigate(LPCTSTR pTChar)
+ {
+ // Navigate to our web page
+ VARIANT vURL;
+ vURL.vt = VT_BSTR;
+ vURL.bstrVal = SysAllocString(T2W(pTChar));
+ VARIANT ve1, ve2, ve3, ve4;
+ ve1.vt = VT_EMPTY;
+ ve2.vt = VT_EMPTY;
+ ve3.vt = VT_EMPTY;
+ ve4.vt = VT_EMPTY;
+
+ GetIWebBrowser2()->Navigate2(&vURL, &ve1, &ve2, &ve3, &ve4);
+
+ VariantClear(&vURL); // Also frees memory allocated by SysAllocateString
+ }
+
+ inline void CWebBrowser::OnCreate()
+ {
+ AddWebBrowserControl();
+ }
+
+ inline void CWebBrowser::OnSize(int width, int height)
+ {
+ // position the container
+ GetAXWindow().SetLocation(0, 0, width, height);
+ }
+
+ inline LRESULT CWebBrowser::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch(uMsg)
+ {
+ case WM_SIZE:
+ OnSize(LOWORD(lParam), HIWORD(lParam));
+ break;
+ case WM_DESTROY:
+ GetAXWindow().Remove();
+ break;
+ }
+
+ return CWnd::WndProcDefault(uMsg, wParam, lParam);
+ }
+
+}
+
+#endif // _WIN32XX_WEBBROWSER_H_
+
diff --git a/mmc_updater/depends/win32cpp/wincore.h b/mmc_updater/depends/win32cpp/wincore.h
new file mode 100644
index 00000000..d6b1f9b6
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/wincore.h
@@ -0,0 +1,2977 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////
+// wincore.h
+// Declaration of the following classes:
+// CWinApp, CWnd, CWinException, CCriticalSection,
+// CPoint, CRect, and CSize
+//
+// This file contains the declarations for the core set of classes required to
+// create simple windows using Win32++.
+//
+// 1) CCriticalSection: This class is used internally to manage thread access
+// to shared resources. You can also use this class to lock and
+// release your own critical sections.
+//
+// 2) CWinException: This class is used internally by Win32++ to handle
+// exceptions. You can also use it to throw and catch exceptions.
+//
+// 3) WinApp: This class is used start Win32++ and run the message loop. You
+// should inherit from this class to start Win32++ in your own
+// application.
+//
+// 4) CWnd: This class is used to represent a window. It provides a means
+// of creating the window, and handling its messages. Inherit
+// from this class to define and control windows.
+//
+//
+// Note: This header file (or another Win32++ header file which includes it)
+// should be included before all other header files. It sets some
+// important macros which need to be set before including Windows.h
+// Including this file first also allows it to disable some pointless
+// warning messages (see below).
+
+
+
+#ifndef _WIN32XX_WINCORE_H_
+#define _WIN32XX_WINCORE_H_
+
+
+// Remove pointless warning messages
+#ifdef _MSC_VER
+ #pragma warning (disable : 4996) // function or variable may be unsafe (deprecated)
+ #ifndef _CRT_SECURE_NO_WARNINGS
+ #define _CRT_SECURE_NO_WARNINGS // eliminate deprecation warnings for VS2005/VS2010
+ #endif
+ #if _MSC_VER < 1500
+ #pragma warning (disable : 4511) // copy operator could not be generated
+ #pragma warning (disable : 4512) // assignment operator could not be generated
+ #pragma warning (disable : 4702) // unreachable code (bugs in Microsoft's STL)
+ #pragma warning (disable : 4786) // identifier was truncated
+ #endif
+#endif
+
+#ifdef __BORLANDC__
+ #pragma option -w-8019 // code has no effect
+ #pragma option -w-8026 // functions with exception specifiations are not expanded inline
+ #pragma option -w-8027 // function not expanded inline
+ #define STRICT 1
+#endif
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic ignored "-Wmissing-braces"
+ #pragma GCC diagnostic ignored "-Wunused-value"
+#endif
+
+#ifdef _WIN32_WCE
+ #include "wcestddef.h"
+#endif
+
+#define _WINSOCKAPI_ // Prevent winsock.h #include's.
+
+#include <assert.h>
+#include <vector>
+#include <algorithm>
+#include <string>
+#include <map>
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <tchar.h>
+#include <shlwapi.h>
+#include "shared_ptr.h"
+//#include "winutils.h" // included later in this file
+//#include "cstring.h" // included later in this file
+//#include "gdi.h" // included later in this file
+//#include "menu.h" // included later in this file
+
+// For compilers lacking Win64 support
+#ifndef GetWindowLongPtr
+ #define GetWindowLongPtr GetWindowLong
+ #define SetWindowLongPtr SetWindowLong
+ #define GWLP_WNDPROC GWL_WNDPROC
+ #define GWLP_HINSTANCE GWL_HINSTANCE
+ #define GWLP_ID GWL_ID
+ #define GWLP_USERDATA GWL_USERDATA
+ #define DWLP_DLGPROC DWL_DLGPROC
+ #define DWLP_MSGRESULT DWL_MSGRESULT
+ #define DWLP_USER DWL_USER
+ #define DWORD_PTR DWORD
+ #define LONG_PTR LONG
+ #define ULONG_PTR LONG
+#endif
+#ifndef GetClassLongPtr
+ #define GetClassLongPtr GetClassLong
+ #define SetClassLongPtr SetClassLong
+ #define GCLP_HBRBACKGROUND GCL_HBRBACKGROUND
+ #define GCLP_HCURSOR GCL_HCURSOR
+ #define GCLP_HICON GCL_HICON
+ #define GCLP_HICONSM GCL_HICONSM
+ #define GCLP_HMODULE GCL_HMODULE
+ #define GCLP_MENUNAME GCL_MENUNAME
+ #define GCLP_WNDPROC GCL_WNDPROC
+#endif
+
+
+// Messages defined by Win32++
+#define UWM_POPUPMENU (WM_APP + 1) // Message - creates the menubar popup menu
+#define UWM_DOCK_START (WM_APP + 2) // Notification - about to start undocking
+#define UWM_DOCK_MOVE (WM_APP + 3) // Notification - undocked docker is being moved
+#define UWM_DOCK_END (WM_APP + 4) // Notification - docker has been docked
+#define UWM_BAR_START (WM_APP + 5) // Notification - docker bar selected for move
+#define UWM_BAR_MOVE (WM_APP + 6) // Notification - docker bar moved
+#define UWM_BAR_END (WM_APP + 7) // Notification - end of docker bar move
+#define UWM_UNDOCKED (WM_APP + 8) // Notification - sent by docker when undocked
+#define UWM_FRAMELOSTFOCUS (WM_APP + 9) // Notification - sent by frame to view window when focus lost
+#define UWM_FRAMEGOTFOCUS (WM_APP + 10) // Notification - sent by frame to view window when focus acquired
+#define UWM_DOCK_DESTROYED (WM_APP + 11) // Message - posted when docker is destroyed
+#define UWM_TAB_CHANGED (WM_APP + 12) // Notification - tab layout changed
+#define UWM_TOOLBAR_RESIZE (WM_APP + 13) // Message - sent by toolbar to parent. Used by the rebar
+#define UWM_UPDATE_COMMAND (WM_APP + 14) // Message - sent before a menu is displayed. Used by OnUpdate
+#define UWM_DOCK_ACTIVATED (WM_APP + 15) // Message - sent to dock ancestor when a docker is activated or deactivated.
+#define UWM_GETMENUTHEME (WM_APP + 16) // Message - returns a pointer to MenuTheme
+#define UWM_GETREBARTHEME (WM_APP + 17) // Message - returns a pointer to CToolBar
+#define UWM_GETTOOLBARTHEME (WM_APP + 18) // Message - returns a pointer to ToolBarTheme
+#define UWM_CLEANUPTEMPS (WM_APP + 19) // Message - posted to cleanup temporary CDCs
+
+
+// Automatically include the Win32xx namespace
+// define NO_USING_NAMESPACE to skip this step
+namespace Win32xx {}
+#ifndef NO_USING_NAMESPACE
+ using namespace Win32xx;
+#endif
+
+// Required for WinCE
+#ifndef TLS_OUT_OF_INDEXES
+ #define TLS_OUT_OF_INDEXES ((DWORD_PTR) -1)
+#endif
+#ifndef WM_PARENTNOTIFY
+ #define WM_PARENTNOTIFY 0x0210
+#endif
+
+
+namespace Win32xx
+{
+
+ ////////////////////////////////////////////////
+ // Forward declarations.
+ // These classes are defined later or elsewhere
+ class CDC;
+ class CGDIObject;
+ class CMenu;
+ class CWinApp;
+ class CWnd;
+ class CBitmap;
+ class CBrush;
+ class CFont;
+ class CPalette;
+ class CPen;
+ class CRgn;
+
+ // tString is a TCHAR std::string
+ typedef std::basic_string<TCHAR> tString;
+
+ // tStringStream is a TCHAR std::stringstream
+ typedef std::basic_stringstream<TCHAR> tStringStream;
+
+ // Some useful smart pointers
+ typedef Shared_Ptr<CDC> DCPtr;
+ typedef Shared_Ptr<CGDIObject> GDIPtr;
+ typedef Shared_Ptr<CMenu> MenuPtr;
+ typedef Shared_Ptr<CWnd> WndPtr;
+ typedef Shared_Ptr<CBitmap> BitmapPtr;
+ typedef Shared_Ptr<CBrush> BrushPtr;
+ typedef Shared_Ptr<CFont> FontPtr;
+ typedef Shared_Ptr<CPalette> PalettePtr;
+ typedef Shared_Ptr<CPen> PenPtr;
+ typedef Shared_Ptr<CRgn> RgnPtr;
+
+ enum Constants // Defines the maximum size for TCHAR strings
+ {
+ MAX_MENU_STRING = 80,
+ MAX_STRING_SIZE = 255,
+ };
+
+ struct CompareHDC // The comparison function object used by CWinApp::m_mapHDC
+ {
+ bool operator()(HDC const a, const HDC b) const
+ {return ((DWORD_PTR)a < (DWORD_PTR)b);}
+ };
+
+ struct CompareGDI // The comparison function object used by CWinApp::m_mapGDI
+ {
+ bool operator()(HGDIOBJ const a, const HGDIOBJ b) const
+ {return ((DWORD_PTR)a < (DWORD_PTR)b);}
+ };
+
+ struct CompareHMENU // The comparison function object used by CWinApp::m_mapHMENU
+ {
+ bool operator()(HMENU const a, const HMENU b) const
+ {return ((DWORD_PTR)a < (DWORD_PTR)b);}
+ };
+
+ struct CompareHWND // The comparison function object used by CWinApp::m_mapHWND
+ {
+ bool operator()(HWND const a, const HWND b) const
+ {return ((DWORD_PTR)a < (DWORD_PTR)b);}
+ };
+
+ struct TLSData // Used for Thread Local Storage (TLS)
+ {
+ CWnd* pCWnd; // pointer to CWnd object for Window creation
+ CWnd* pMenuBar; // pointer to CMenuBar object used for the WH_MSGFILTER hook
+ HHOOK hHook; // WH_MSGFILTER hook for CMenuBar and Modeless Dialogs
+
+ std::vector<DCPtr> vTmpDCs; // A vector of temporary CDC pointers
+ std::vector<GDIPtr> vTmpGDIs; // A vector of temporary CGDIObject pointers
+ std::vector<WndPtr> vTmpWnds; // A vector of temporary CWnd pointers
+ TLSData() : pCWnd(0), pMenuBar(0), hHook(0) {}
+
+#ifndef _WIN32_WCE
+ std::vector<MenuPtr> vTmpMenus; // A vector of temporary CMenu pointers
+#endif
+ };
+
+
+ /////////////////////////////////////////
+ // Declarations for the CCriticalSection class
+ // This class is used for thread synchronisation
+ class CCriticalSection
+ {
+ public:
+ CCriticalSection() { ::InitializeCriticalSection(&m_cs); }
+ ~CCriticalSection() { ::DeleteCriticalSection(&m_cs); }
+
+ void Lock() { ::EnterCriticalSection(&m_cs); }
+ void Release() { ::LeaveCriticalSection(&m_cs); }
+
+ private:
+ CCriticalSection ( const CCriticalSection& );
+ CCriticalSection& operator = ( const CCriticalSection& );
+
+ CRITICAL_SECTION m_cs;
+ };
+
+
+ ////////////////////////////////////////
+ // Declaration of the CWinException class
+ //
+ // Note: Each function guarantees not to throw an exception
+
+ class CWinException : public std::exception
+ {
+ public:
+ CWinException(LPCTSTR pszText) throw ();
+ ~CWinException() throw() {}
+ DWORD GetError() const throw ();
+ LPCTSTR GetErrorString() const throw ();
+ const char * what () const throw ();
+
+ private:
+ DWORD m_Error;
+ LPCTSTR m_pszText;
+ TCHAR m_szErrorString[MAX_STRING_SIZE];
+ };
+
+
+ ///////////////////////////////////
+ // Declaration of the CWinApp class
+ //
+ class CWinApp
+ {
+ // Provide these access to CWinApp's private members:
+ friend class CDC;
+ friend class CDialog;
+ friend class CGDIObject;
+ friend class CMenu;
+ friend class CMenuBar;
+ friend class CPropertyPage;
+ friend class CPropertySheet;
+ friend class CTaskDialog;
+ friend class CWnd;
+ friend CWinApp* GetApp();
+ friend CGDIObject* FromHandle(HGDIOBJ hObject);
+ friend CBitmap* FromHandle(HBITMAP hBitmap);
+ friend CBrush* FromHandle(HBRUSH hBrush);
+ friend CFont* FromHandle(HFONT hFont);
+ friend CPalette* FromHandle(HPALETTE hPalette);
+ friend CPen* FromHandle(HPEN hPen);
+ friend CRgn* FromHandle(HRGN hRgn);
+ friend CDC* FromHandle(HDC hDC);
+ friend CWnd* FromHandle(HWND hWnd);
+#ifndef _WIN32_WCE
+ friend CMenu* FromHandle(HMENU hMenu);
+#endif
+
+ typedef Shared_Ptr<TLSData> TLSDataPtr;
+
+ public:
+ CWinApp();
+ virtual ~CWinApp();
+
+ HACCEL GetAccelerators() const { return m_hAccel; }
+ HINSTANCE GetInstanceHandle() const { return m_hInstance; }
+ HINSTANCE GetResourceHandle() const { return (m_hResource ? m_hResource : m_hInstance); }
+ void SetAccelerators(HACCEL hAccel, CWnd* pWndAccel);
+ void SetResourceHandle(HINSTANCE hResource);
+
+ // These are the functions you might wish to override
+ virtual BOOL InitInstance();
+ virtual int MessageLoop();
+ virtual int Run();
+
+ protected:
+ virtual BOOL OnIdle(LONG lCount);
+ virtual BOOL PreTranslateMessage(MSG Msg);
+
+ private:
+ CWinApp(const CWinApp&); // Disable copy construction
+ CWinApp& operator = (const CWinApp&); // Disable assignment operator
+ CDC* GetCDCFromMap(HDC hDC);
+ CGDIObject* GetCGDIObjectFromMap(HGDIOBJ hObject);
+ CMenu* GetCMenuFromMap(HMENU hMenu);
+ CWnd* GetCWndFromMap(HWND hWnd);
+
+ void AddTmpDC(CDC* pDC);
+ void AddTmpGDI(CGDIObject* pObject);
+ CMenu* AddTmpMenu(HMENU hMenu);
+ CWnd* AddTmpWnd(HWND hWnd);
+ void CleanupTemps();
+ DWORD GetTlsIndex() const {return m_dwTlsIndex;}
+ void SetCallback();
+ TLSData* SetTlsIndex();
+ static CWinApp* SetnGetThis(CWinApp* pThis = 0);
+
+ std::map<HDC, CDC*, CompareHDC> m_mapHDC; // maps device context handles to CDC objects
+ std::map<HGDIOBJ, CGDIObject*, CompareGDI> m_mapGDI; // maps GDI handles to CGDIObjects.
+ std::map<HMENU, CMenu*, CompareHMENU> m_mapHMENU; // maps menu handles to CMenu objects
+ std::map<HWND, CWnd*, CompareHWND> m_mapHWND; // maps window handles to CWnd objects
+ std::vector<TLSDataPtr> m_vTLSData; // vector of TLSData smart pointers, one for each thread
+ CCriticalSection m_csMapLock; // thread synchronisation for m_mapHWND
+ CCriticalSection m_csTLSLock; // thread synchronisation for m_vTLSData
+ CCriticalSection m_csAppStart; // thread synchronisation for application startup
+ HINSTANCE m_hInstance; // handle to the applications instance
+ HINSTANCE m_hResource; // handle to the applications resources
+ DWORD m_dwTlsIndex; // Thread Local Storage index
+ WNDPROC m_Callback; // callback address of CWnd::StaticWndowProc
+ HACCEL m_hAccel; // handle to the accelerator table
+ CWnd* m_pWndAccel; // handle to the window for accelerator keys
+
+ };
+
+}
+
+#include "winutils.h"
+#include "cstring.h"
+
+
+namespace Win32xx
+{
+ ////////////////////////////////
+ // Declaration of the CWnd class
+ //
+ class CWnd
+ {
+ friend class CMDIChild;
+ friend class CDialog;
+ friend class CPropertyPage;
+ friend class CTaskDialog;
+ friend class CWinApp;
+
+ public:
+ CWnd(); // Constructor
+ virtual ~CWnd(); // Destructor
+
+ // These virtual functions can be overridden
+ virtual BOOL Attach(HWND hWnd);
+ virtual BOOL AttachDlgItem(UINT nID, CWnd* pParent);
+ virtual void CenterWindow() const;
+ virtual HWND Create(CWnd* pParent = NULL);
+ virtual HWND CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, CWnd* pParent, CMenu* pMenu, LPVOID lpParam = NULL);
+ virtual HWND CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rc, CWnd* pParent, CMenu* pMenu, LPVOID lpParam = NULL);
+ virtual void Destroy();
+ virtual HWND Detach();
+ virtual HICON SetIconLarge(int nIcon);
+ virtual HICON SetIconSmall(int nIcon);
+
+ // Attributes
+ HWND GetHwnd() const { return m_hWnd; }
+ WNDPROC GetPrevWindowProc() const { return m_PrevWindowProc; }
+
+ // Wrappers for Win32 API functions
+ // These functions aren't virtual, and shouldn't be overridden
+ CDC* BeginPaint(PAINTSTRUCT& ps) const;
+ BOOL BringWindowToTop() const;
+ LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, UINT Msg, WPARAM wParam, LPARAM lParam) const;
+ BOOL CheckDlgButton(int nIDButton, UINT uCheck) const;
+ BOOL CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton) const;
+ CWnd* ChildWindowFromPoint(POINT pt) const;
+ BOOL ClientToScreen(POINT& pt) const;
+ BOOL ClientToScreen(RECT& rc) const;
+ LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) const;
+ HDWP DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const;
+ HDWP DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const;
+ BOOL DrawMenuBar() const;
+ BOOL EnableWindow(BOOL bEnable = TRUE) const;
+ BOOL EndPaint(PAINTSTRUCT& ps) const;
+ CWnd* GetActiveWindow() const;
+ CWnd* GetAncestor(UINT gaFlag = 3 /*= GA_ROOTOWNER*/) const;
+ CWnd* GetCapture() const;
+ ULONG_PTR GetClassLongPtr(int nIndex) const;
+ CString GetClassName() const;
+ CRect GetClientRect() const;
+ CDC* GetDC() const;
+ CDC* GetDCEx(HRGN hrgnClip, DWORD flags) const;
+ CWnd* GetDesktopWindow() const;
+ CWnd* GetDlgItem(int nIDDlgItem) const;
+ UINT GetDlgItemInt(int nIDDlgItem, BOOL* lpTranslated, BOOL bSigned) const;
+ CString GetDlgItemText(int nIDDlgItem) const;
+ CWnd* GetFocus() const;
+ CFont* GetFont() const;
+ HICON GetIcon(BOOL bBigIcon) const;
+ CWnd* GetNextDlgGroupItem(CWnd* pCtl, BOOL bPrevious) const;
+ CWnd* GetNextDlgTabItem(CWnd* pCtl, BOOL bPrevious) const;
+ CWnd* GetParent() const;
+ BOOL GetScrollInfo(int fnBar, SCROLLINFO& si) const;
+ CRect GetUpdateRect(BOOL bErase) const;
+ int GetUpdateRgn(CRgn* pRgn, BOOL bErase) const;
+ CWnd* GetWindow(UINT uCmd) const;
+ CDC* GetWindowDC() const;
+ LONG_PTR GetWindowLongPtr(int nIndex) const;
+ CRect GetWindowRect() const;
+ CString GetWindowText() const;
+ int GetWindowTextLength() const;
+ void Invalidate(BOOL bErase = TRUE) const;
+ BOOL InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE) const;
+ BOOL InvalidateRgn(CRgn* pRgn, BOOL bErase = TRUE) const;
+ BOOL IsChild(CWnd* pChild) const;
+ BOOL IsDialogMessage(LPMSG lpMsg) const;
+ UINT IsDlgButtonChecked(int nIDButton) const;
+ BOOL IsWindow() const;
+ BOOL IsWindowEnabled() const;
+ BOOL IsWindowVisible() const;
+ BOOL KillTimer(UINT_PTR uIDEvent) const;
+ int MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) const;
+ void MapWindowPoints(CWnd* pWndTo, POINT& pt) const;
+ void MapWindowPoints(CWnd* pWndTo, RECT& rc) const;
+ void MapWindowPoints(CWnd* pWndTo, LPPOINT ptArray, UINT nCount) const;
+ BOOL MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE) const;
+ BOOL MoveWindow(const RECT& rc, BOOL bRepaint = TRUE) const;
+ BOOL PostMessage(UINT uMsg, WPARAM wParam = 0L, LPARAM lParam = 0L) const;
+ BOOL PostMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const;
+ BOOL RedrawWindow(LPCRECT lpRectUpdate = NULL, CRgn* pRgn = NULL, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ALLCHILDREN) const;
+ int ReleaseDC(CDC* pDC) const;
+ BOOL ScreenToClient(POINT& Point) const;
+ BOOL ScreenToClient(RECT& rc) const;
+ LRESULT SendDlgItemMessage(int nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam) const;
+ LRESULT SendMessage(UINT uMsg, WPARAM wParam = 0L, LPARAM lParam = 0L) const;
+ LRESULT SendMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const;
+ BOOL SendNotifyMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const;
+ CWnd* SetActiveWindow() const;
+ CWnd* SetCapture() const;
+ ULONG_PTR SetClassLongPtr(int nIndex, LONG_PTR dwNewLong) const;
+ BOOL SetDlgItemInt(int nIDDlgItem, UINT uValue, BOOL bSigned) const;
+ BOOL SetDlgItemText(int nIDDlgItem, LPCTSTR lpString) const;
+ CWnd* SetFocus() const;
+ void SetFont(CFont* pFont, BOOL bRedraw = TRUE) const;
+ BOOL SetForegroundWindow() const;
+ HICON SetIcon(HICON hIcon, BOOL bBigIcon) const;
+ CWnd* SetParent(CWnd* pWndParent) const;
+ BOOL SetRedraw(BOOL bRedraw = TRUE) const;
+ int SetScrollInfo(int fnBar, const SCROLLINFO& si, BOOL fRedraw) const;
+ UINT_PTR SetTimer(UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc) const;
+ LONG_PTR SetWindowLongPtr(int nIndex, LONG_PTR dwNewLong) const;
+ BOOL SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const;
+ BOOL SetWindowPos(HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const;
+ int SetWindowRgn(CRgn* pRgn, BOOL bRedraw = TRUE) const;
+ BOOL SetWindowText(LPCTSTR lpString) const;
+ HRESULT SetWindowTheme(LPCWSTR pszSubAppName, LPCWSTR pszSubIdList) const;
+ BOOL ShowWindow(int nCmdShow = SW_SHOWNORMAL) const;
+ BOOL UpdateWindow() const;
+ BOOL ValidateRect(LPCRECT prc) const;
+ BOOL ValidateRgn(CRgn* pRgn) const;
+ static CWnd* WindowFromPoint(POINT pt);
+
+ #ifndef _WIN32_WCE
+ BOOL CloseWindow() const;
+ int DlgDirList(LPTSTR lpPathSpec, int nIDListBox, int nIDStaticPath, UINT uFileType) const;
+ int DlgDirListComboBox(LPTSTR lpPathSpec, int nIDComboBox, int nIDStaticPath, UINT uFiletype) const;
+ BOOL DlgDirSelectEx(LPTSTR lpString, int nCount, int nIDListBox) const;
+ BOOL DlgDirSelectComboBoxEx(LPTSTR lpString, int nCount, int nIDComboBox) const;
+ BOOL DrawAnimatedRects(int idAni, RECT& rcFrom, RECT& rcTo) const;
+ BOOL DrawCaption(CDC* pDC, RECT& rc, UINT uFlags) const;
+ BOOL EnableScrollBar(UINT uSBflags, UINT uArrows) const;
+ CWnd* GetLastActivePopup() const;
+ CMenu* GetMenu() const;
+ int GetScrollPos(int nBar) const;
+ BOOL GetScrollRange(int nBar, int& MinPos, int& MaxPos) const;
+ CMenu* GetSystemMenu(BOOL bRevert) const;
+ CWnd* GetTopWindow() const;
+ BOOL GetWindowPlacement(WINDOWPLACEMENT& pWndpl) const;
+ BOOL HiliteMenuItem(CMenu* pMenu, UINT uItemHilite, UINT uHilite) const;
+ BOOL IsIconic() const;
+ BOOL IsZoomed() const;
+ BOOL LockWindowUpdate() const;
+ BOOL OpenIcon() const;
+ void Print(CDC* pDC, DWORD dwFlags) const;
+ BOOL SetMenu(CMenu* pMenu) const;
+ BOOL ScrollWindow(int XAmount, int YAmount, LPCRECT lprcScroll, LPCRECT lprcClip) const;
+ int ScrollWindowEx(int dx, int dy, LPCRECT lprcScroll, LPCRECT lprcClip, CRgn* prgnUpdate, LPRECT lprcUpdate, UINT flags) const;
+ int SetScrollPos(int nBar, int nPos, BOOL bRedraw) const;
+ BOOL SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw) const;
+ BOOL SetWindowPlacement(const WINDOWPLACEMENT& wndpl) const;
+ BOOL ShowOwnedPopups(BOOL fShow) const;
+ BOOL ShowScrollBar(int nBar, BOOL bShow) const;
+ BOOL ShowWindowAsync(int nCmdShow) const;
+ BOOL UnLockWindowUpdate() const;
+ CWnd* WindowFromDC(CDC* pDC) const;
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ void DragAcceptFiles(BOOL fAccept) const;
+ #endif
+ #endif
+
+ static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ operator HWND() const { return m_hWnd; }
+
+ protected:
+ // Override these functions as required
+ virtual LRESULT FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+ virtual void OnCreate();
+ virtual void OnDraw(CDC* pDC);
+ virtual BOOL OnEraseBkgnd(CDC* pDC);
+ virtual void OnInitialUpdate();
+ virtual void OnMenuUpdate(UINT nID);
+ virtual LRESULT OnMessageReflect(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam);
+ virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam);
+ virtual void PreCreate(CREATESTRUCT& cs);
+ virtual void PreRegisterClass(WNDCLASS& wc);
+ virtual BOOL PreTranslateMessage(MSG* pMsg);
+ virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ HWND m_hWnd; // handle to this object's window
+
+ private:
+ CWnd(const CWnd&); // Disable copy construction
+ CWnd& operator = (const CWnd&); // Disable assignment operator
+ void AddToMap();
+ void Cleanup();
+ LRESULT MessageReflect(HWND hwndParent, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ BOOL RegisterClass(WNDCLASS& wc);
+ BOOL RemoveFromMap();
+ void Subclass(HWND hWnd);
+
+ Shared_Ptr<WNDCLASS> m_pwc; // defines initialisation parameters for PreRegisterClass
+ Shared_Ptr<CREATESTRUCT> m_pcs; // defines initialisation parameters for PreCreate and Create
+ WNDPROC m_PrevWindowProc; // pre-subclassed Window Procedure
+ BOOL m_IsTmpWnd; // True if this CWnd is a TmpWnd
+
+ }; // class CWnd
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#include "gdi.h"
+#include "menu.h"
+
+namespace Win32xx
+{
+
+ //////////////////////////////////////////
+ // Definitions for the CWinException class
+ //
+ inline CWinException::CWinException(LPCTSTR pszText) throw () : m_Error(::GetLastError()), m_pszText(pszText)
+ {
+ memset(m_szErrorString, 0, MAX_STRING_SIZE * sizeof(TCHAR));
+
+ if (m_Error != 0)
+ {
+ DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ ::FormatMessage(dwFlags, NULL, m_Error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), m_szErrorString, MAX_STRING_SIZE-1, NULL);
+ }
+ }
+
+ inline DWORD CWinException::GetError() const throw ()
+ {
+ return m_Error;
+ }
+
+ inline LPCTSTR CWinException::GetErrorString() const throw ()
+ {
+ return m_szErrorString;
+ }
+
+ inline const char * CWinException::what() const throw ()
+ {
+ // Sends the last error string to the debugger (typically displayed in the IDE's output window).
+ ::OutputDebugString(m_szErrorString);
+ return "CWinException thrown";
+ }
+
+
+ ////////////////////////////////////
+ // Definitions for the CWinApp class
+ //
+
+ // To begin Win32++, inherit your application class from this one.
+ // You must run only one instance of the class inherited from this.
+ inline CWinApp::CWinApp() : m_Callback(NULL), m_hAccel(0), m_pWndAccel(0)
+ {
+ try
+ {
+ m_csAppStart.Lock();
+ assert( 0 == SetnGetThis() ); // Test if this is the first instance of CWinApp
+
+ m_dwTlsIndex = ::TlsAlloc();
+ if (m_dwTlsIndex == TLS_OUT_OF_INDEXES)
+ {
+ // We only get here in the unlikely event that all TLS indexes are already allocated by this app
+ // At least 64 TLS indexes per process are allowed. Win32++ requires only one TLS index.
+ m_csAppStart.Release();
+ throw CWinException(_T("CWinApp::CWinApp Failed to allocate TLS Index"));
+ }
+
+ SetnGetThis(this);
+ m_csAppStart.Release();
+
+ // Set the instance handle
+ #ifdef _WIN32_WCE
+ m_hInstance = (HINSTANCE)GetModuleHandle(0);
+ #else
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ VirtualQuery( (LPCVOID)SetnGetThis, &mbi, sizeof(mbi) );
+ assert(mbi.AllocationBase);
+ m_hInstance = (HINSTANCE)mbi.AllocationBase;
+ #endif
+
+ m_hResource = m_hInstance;
+ SetCallback();
+ }
+
+ catch (const CWinException &e)
+ {
+ e.what();
+ throw;
+ }
+ }
+
+ inline CWinApp::~CWinApp()
+ {
+ std::vector<TLSDataPtr>::iterator iter;
+ for (iter = m_vTLSData.begin(); iter < m_vTLSData.end(); ++iter)
+ {
+ (*iter)->vTmpDCs.clear();
+#ifndef _WIN32_WCE
+ (*iter)->vTmpMenus.clear();
+#endif
+ (*iter)->vTmpWnds.clear();
+ }
+
+ // Check that all CWnd windows are destroyed
+ std::map<HWND, CWnd*, CompareHWND>::iterator m;
+ for (m = m_mapHWND.begin(); m != m_mapHWND.end(); ++m)
+ {
+ HWND hWnd = (*m).first;
+ if (::IsWindow(hWnd))
+ ::DestroyWindow(hWnd);
+ }
+ m_mapHWND.clear();
+ m_mapGDI.clear();
+ m_mapHDC.clear();
+ m_mapHMENU.clear();
+
+ // Do remaining tidy up
+ if (m_dwTlsIndex != TLS_OUT_OF_INDEXES)
+ {
+ ::TlsSetValue(GetTlsIndex(), NULL);
+ ::TlsFree(m_dwTlsIndex);
+ }
+
+ SetnGetThis((CWinApp*)-1);
+ }
+
+ inline void CWinApp::AddTmpDC(CDC* pDC)
+ {
+ // The TmpMenus are created by GetSybMenu.
+ // They are removed by CleanupTemps
+ assert(pDC);
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+ pTLSData->vTmpDCs.push_back(pDC); // save pDC as a smart pointer
+ }
+
+ inline void CWinApp::AddTmpGDI(CGDIObject* pObject)
+ {
+ // The temporary CGDIObjects are removed by CleanupTemps
+ assert(pObject);
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+ pTLSData->vTmpGDIs.push_back(pObject); // save pObject as a smart pointer
+ }
+
+#ifndef _WIN32_WCE
+ inline CMenu* CWinApp::AddTmpMenu(HMENU hMenu)
+ {
+ // The TmpMenus are created by GetSybMenu.
+ // They are removed by CleanupTemps
+ assert(::IsMenu(hMenu));
+ assert(!GetCMenuFromMap(hMenu));
+
+ CMenu* pMenu = new CMenu;
+ pMenu->m_hMenu = hMenu;
+ m_csMapLock.Lock();
+ m_mapHMENU.insert(std::make_pair(hMenu, pMenu));
+ m_csMapLock.Release();
+ pMenu->m_IsTmpMenu = TRUE;
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+ pTLSData->vTmpMenus.push_back(pMenu); // save pMenu as a smart pointer
+ return pMenu;
+ }
+#endif
+
+ inline CWnd* CWinApp::AddTmpWnd(HWND hWnd)
+ {
+ // TmpWnds are created if required to support functions like CWnd::GetParent.
+ // They are removed by CleanupTemps
+ assert(::IsWindow(hWnd));
+ assert(!GetCWndFromMap(hWnd));
+
+ CWnd* pWnd = new CWnd;
+ pWnd->m_hWnd = hWnd;
+ pWnd->AddToMap();
+ pWnd->m_IsTmpWnd = TRUE;
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+ pTLSData->vTmpWnds.push_back(pWnd); // save pWnd as a smart pointer
+ return pWnd;
+ }
+
+ inline void CWinApp::CleanupTemps()
+ // Removes all Temporary CWnds and CMenus belonging to this thread
+ {
+ // Retrieve the pointer to the TLS Data
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ assert(pTLSData);
+
+ pTLSData->vTmpDCs.clear();
+ pTLSData->vTmpGDIs.clear();
+ pTLSData->vTmpWnds.clear();
+
+
+ #ifndef _WIN32_WCE
+ pTLSData->vTmpMenus.clear();
+ #endif
+ }
+
+ inline CDC* CWinApp::GetCDCFromMap(HDC hDC)
+ {
+ // Allocate an iterator for our HWND map
+ std::map<HDC, CDC*, CompareHDC>::iterator m;
+
+ // Find the CDC pointer mapped to this HDC
+ CDC* pDC = 0;
+ m_csMapLock.Lock();
+ m = m_mapHDC.find(hDC);
+
+ if (m != m_mapHDC.end())
+ pDC = m->second;
+
+ m_csMapLock.Release();
+ return pDC;
+ }
+
+ inline CGDIObject* CWinApp::GetCGDIObjectFromMap(HGDIOBJ hObject)
+ {
+ // Allocate an iterator for our HWND map
+ std::map<HGDIOBJ, CGDIObject*, CompareGDI>::iterator m;
+
+ // Find the CGDIObject pointer mapped to this HGDIOBJ
+ CGDIObject* pObject = 0;
+ m_csMapLock.Lock();
+ m = m_mapGDI.find(hObject);
+
+ if (m != m_mapGDI.end())
+ pObject = m->second;
+
+ m_csMapLock.Release();
+ return pObject;
+ }
+
+ inline CMenu* CWinApp::GetCMenuFromMap(HMENU hMenu)
+ {
+ std::map<HMENU, CMenu*, CompareHMENU>::iterator m;
+
+ // Find the CMenu pointer mapped to this HMENU
+ CMenu* pMenu = 0;
+ m_csMapLock.Lock();
+ m = m_mapHMENU.find(hMenu);
+
+ if (m != m_mapHMENU.end())
+ pMenu = m->second;
+
+ m_csMapLock.Release();
+ return pMenu;
+ }
+
+ inline CWnd* CWinApp::GetCWndFromMap(HWND hWnd)
+ {
+ // Allocate an iterator for our HWND map
+ std::map<HWND, CWnd*, CompareHWND>::iterator m;
+
+ // Find the CWnd pointer mapped to this HWND
+ CWnd* pWnd = 0;
+ m_csMapLock.Lock();
+ m = m_mapHWND.find(hWnd);
+
+ if (m != m_mapHWND.end())
+ pWnd = m->second;
+
+ m_csMapLock.Release();
+ return pWnd;
+ }
+
+ inline BOOL CWinApp::InitInstance()
+ {
+ // InitInstance contains the initialization code for your application
+ // You should override this function with the code to run when the application starts.
+
+ // return TRUE to indicate success. FALSE will end the application
+ return TRUE;
+ }
+
+ inline int CWinApp::MessageLoop()
+ {
+ // This gets any messages queued for the application, and dispatches them.
+ MSG Msg = {0};
+ int status = 1;
+ LONG lCount = 0;
+
+ while (status != 0)
+ {
+ // While idle, perform idle processing until OnIdle returns FALSE
+ while (!::PeekMessage(&Msg, 0, 0, 0, PM_NOREMOVE) && OnIdle(lCount) == TRUE)
+ {
+ ++lCount;
+ }
+
+ lCount = 0;
+
+ // Now wait until we get a message
+ if ((status = ::GetMessage(&Msg, NULL, 0, 0)) == -1)
+ return -1;
+
+ if (!PreTranslateMessage(Msg))
+ {
+ ::TranslateMessage(&Msg);
+ ::DispatchMessage(&Msg);
+ }
+ }
+
+ return LOWORD(Msg.wParam);
+ }
+
+ inline BOOL CWinApp::OnIdle(LONG lCount)
+ {
+ if (lCount == 0)
+ CleanupTemps();
+
+ return FALSE;
+ }
+
+ inline BOOL CWinApp::PreTranslateMessage(MSG Msg)
+ {
+ // This functions is called by the MessageLoop. It processes the
+ // keyboard accelerator keys and calls CWnd::PreTranslateMessage for
+ // keyboard and mouse events.
+
+ BOOL Processed = FALSE;
+
+ // only pre-translate mouse and keyboard input events
+ if ((Msg.message >= WM_KEYFIRST && Msg.message <= WM_KEYLAST) ||
+ (Msg.message >= WM_MOUSEFIRST && Msg.message <= WM_MOUSELAST))
+ {
+ // Process keyboard accelerators
+ if (m_pWndAccel && ::TranslateAccelerator(*m_pWndAccel, m_hAccel, &Msg))
+ Processed = TRUE;
+ else
+ {
+ // Search the chain of parents for pretranslated messages.
+ for (HWND hWnd = Msg.hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
+ {
+ CWnd* pWnd = GetCWndFromMap(hWnd);
+ if (pWnd)
+ {
+ Processed = pWnd->PreTranslateMessage(&Msg);
+ if(Processed)
+ break;
+ }
+ }
+ }
+ }
+
+ return Processed;
+ }
+
+ inline int CWinApp::Run()
+ {
+ // InitInstance runs the App's initialization code
+ if (InitInstance())
+ {
+ // Dispatch the window messages
+ return MessageLoop();
+ }
+ else
+ {
+ TRACE(_T("InitInstance failed! Terminating program\n"));
+ ::PostQuitMessage(-1);
+ return -1;
+ }
+ }
+
+ inline void CWinApp::SetAccelerators(HACCEL hAccel, CWnd* pWndAccel)
+ // nID is the resource ID of the accelerator table
+ // pWndAccel is the window pointer for translated messages
+ {
+ assert (hAccel);
+ assert (pWndAccel);
+
+ m_pWndAccel = pWndAccel;
+ m_hAccel = hAccel;
+ }
+
+ inline void CWinApp::SetCallback()
+ {
+ // Registers a temporary window class so we can get the callback
+ // address of CWnd::StaticWindowProc.
+ // This technique works for all Window versions, including WinCE.
+
+ WNDCLASS wcDefault = {0};
+
+ LPCTSTR szClassName = _T("Win32++ Temporary Window Class");
+ wcDefault.hInstance = GetInstanceHandle();
+ wcDefault.lpfnWndProc = CWnd::StaticWindowProc;
+ wcDefault.lpszClassName = szClassName;
+
+ ::RegisterClass(&wcDefault);
+
+ // Retrieve the class information
+ ZeroMemory(&wcDefault, sizeof(wcDefault));
+ ::GetClassInfo(GetInstanceHandle(), szClassName, &wcDefault);
+
+ // Save the callback address of CWnd::StaticWindowProc
+ assert(wcDefault.lpfnWndProc); // Assert fails when running UNICODE build on ANSI OS.
+ m_Callback = wcDefault.lpfnWndProc;
+ ::UnregisterClass(szClassName, GetInstanceHandle());
+ }
+
+ inline CWinApp* CWinApp::SetnGetThis(CWinApp* pThis /*= 0*/)
+ {
+ // This function stores the 'this' pointer in a static variable.
+ // Once stored, it can be used later to return the 'this' pointer.
+ // CWinApp's Destructor calls this function with a value of -1.
+
+ static CWinApp* pWinApp = 0;
+
+ if ((CWinApp*)-1 == pThis)
+ pWinApp = 0;
+ else if (0 == pWinApp)
+ pWinApp = pThis;
+
+ return pWinApp;
+ }
+
+ inline void CWinApp::SetResourceHandle(HINSTANCE hResource)
+ {
+ // This function can be used to load a resource dll.
+ // A resource dll can be used to define resources in different languages.
+ // To use this function, place code like this in InitInstance
+ //
+ // HINSTANCE hResource = LoadLibrary(_T("MyResourceDLL.dll"));
+ // SetResourceHandle(hResource);
+
+ m_hResource = hResource;
+ }
+
+ inline TLSData* CWinApp::SetTlsIndex()
+ {
+ TLSData* pTLSData = (TLSData*)::TlsGetValue(GetTlsIndex());
+ if (NULL == pTLSData)
+ {
+ pTLSData = new TLSData;
+
+ m_csTLSLock.Lock();
+ m_vTLSData.push_back(pTLSData); // store as a Shared_Ptr
+ m_csTLSLock.Release();
+
+ ::TlsSetValue(GetTlsIndex(), pTLSData);
+ }
+
+ return pTLSData;
+ }
+
+
+ ////////////////////////////////////////
+ // Definitions for the CWnd class
+ //
+ inline CWnd::CWnd() : m_hWnd(NULL), m_PrevWindowProc(NULL), m_IsTmpWnd(FALSE)
+ {
+ // Note: m_hWnd is set in CWnd::CreateEx(...)
+ m_pcs = new CREATESTRUCT; // store the CREATESTRICT in a smart pointer
+ m_pwc = new WNDCLASS; // store the WNDCLASS in a smart pointer
+ ::ZeroMemory(m_pcs.get(), sizeof(CREATESTRUCT));
+ ::ZeroMemory(m_pwc.get(), sizeof(WNDCLASS));
+ }
+
+ inline CWnd::~CWnd()
+ {
+ // Destroys the window for this object and cleans up resources.
+ Destroy();
+ }
+
+ inline void CWnd::AddToMap()
+ // Store the window handle and CWnd pointer in the HWND map
+ {
+ assert( GetApp() );
+ GetApp()->m_csMapLock.Lock();
+
+ assert(::IsWindow(m_hWnd));
+ assert(!GetApp()->GetCWndFromMap(m_hWnd));
+
+ GetApp()->m_mapHWND.insert(std::make_pair(m_hWnd, this));
+ GetApp()->m_csMapLock.Release();
+ }
+
+ inline BOOL CWnd::Attach(HWND hWnd)
+ // Subclass an existing window and attach it to a CWnd
+ {
+ assert( GetApp() );
+ assert(::IsWindow(hWnd));
+
+ // Ensure this thread has the TLS index set
+ // Note: Perform the attach from the same thread as the window's message loop
+ GetApp()->SetTlsIndex();
+
+ if (m_PrevWindowProc)
+ Detach();
+
+ Subclass(hWnd);
+
+ // Store the CWnd pointer in the HWND map
+ AddToMap();
+ OnCreate();
+ OnInitialUpdate();
+
+ return TRUE;
+ }
+
+ inline BOOL CWnd::AttachDlgItem(UINT nID, CWnd* pParent)
+ // Converts a dialog item to a CWnd object
+ {
+ assert(pParent->IsWindow());
+
+ HWND hWnd = ::GetDlgItem(pParent->GetHwnd(), nID);
+ return Attach(hWnd);
+ }
+
+ inline void CWnd::CenterWindow() const
+ // Centers this window over it's parent
+ {
+
+ // required for multi-monitor support with Dev-C++ and VC6
+ #ifndef _WIN32_WCE
+ #ifndef MONITOR_DEFAULTTONEAREST
+ #define MONITOR_DEFAULTTONEAREST 0x00000002
+ #endif
+ #ifndef HMONITOR
+ DECLARE_HANDLE(HMONITOR);
+ #endif
+ #ifndef MONITORINFO
+ typedef struct tagMONITORINFO
+ {
+ DWORD cbSize;
+ RECT rcMonitor;
+ RECT rcWork;
+ DWORD dwFlags;
+ } MONITORINFO, *LPMONITORINFO;
+ #endif // MONITOR_DEFAULTTONEAREST
+ #endif // _WIN32_WCE
+
+ assert(::IsWindow(m_hWnd));
+
+ CRect rc = GetWindowRect();
+ CRect rcParent;
+ CRect rcDesktop;
+
+ // Get screen dimensions excluding task bar
+ ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0);
+
+ // Get the parent window dimensions (parent could be the desktop)
+ if (GetParent() != NULL) rcParent = GetParent()->GetWindowRect();
+ else rcParent = rcDesktop;
+
+ #ifndef _WIN32_WCE
+ // Import the GetMonitorInfo and MonitorFromWindow functions
+ HMODULE hUser32 = LoadLibrary(_T("USER32.DLL"));
+ typedef BOOL (WINAPI* LPGMI)(HMONITOR hMonitor, LPMONITORINFO lpmi);
+ typedef HMONITOR (WINAPI* LPMFW)(HWND hwnd, DWORD dwFlags);
+ LPMFW pfnMonitorFromWindow = (LPMFW)::GetProcAddress(hUser32, "MonitorFromWindow");
+ #ifdef _UNICODE
+ LPGMI pfnGetMonitorInfo = (LPGMI)::GetProcAddress(hUser32, "GetMonitorInfoW");
+ #else
+ LPGMI pfnGetMonitorInfo = (LPGMI)::GetProcAddress(hUser32, "GetMonitorInfoA");
+ #endif
+
+ // Take multi-monitor systems into account
+ if (pfnGetMonitorInfo && pfnMonitorFromWindow)
+ {
+ HMONITOR hActiveMonitor = pfnMonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi = { sizeof(mi), 0};
+
+ if(pfnGetMonitorInfo(hActiveMonitor, &mi))
+ {
+ rcDesktop = mi.rcWork;
+ if (GetParent() == NULL) rcParent = mi.rcWork;
+ }
+ }
+ FreeLibrary(hUser32);
+ #endif
+
+ // Calculate point to center the dialog over the portion of parent window on this monitor
+ rcParent.IntersectRect(rcParent, rcDesktop);
+ int x = rcParent.left + (rcParent.Width() - rc.Width())/2;
+ int y = rcParent.top + (rcParent.Height() - rc.Height())/2;
+
+ // Keep the dialog wholly on the monitor display
+ x = (x < rcDesktop.left)? rcDesktop.left : x;
+ x = (x > rcDesktop.right - rc.Width())? rcDesktop.right - rc.Width() : x;
+ y = (y < rcDesktop.top) ? rcDesktop.top: y;
+ y = (y > rcDesktop.bottom - rc.Height())? rcDesktop.bottom - rc.Height() : y;
+
+ SetWindowPos(HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
+ }
+
+ inline void CWnd::Cleanup()
+ // Returns the CWnd to its default state
+ {
+ if ( GetApp() ) RemoveFromMap();
+ m_hWnd = NULL;
+ m_PrevWindowProc = NULL;
+ m_IsTmpWnd = FALSE;
+ }
+
+ inline HWND CWnd::Create(CWnd* pParent /* = NULL */)
+ // Creates the window. This is the default method of window creation.
+ {
+
+ // Test if Win32++ has been started
+ assert( GetApp() );
+
+ // Set the WNDCLASS parameters
+ PreRegisterClass(*m_pwc);
+ if (m_pwc->lpszClassName)
+ {
+ RegisterClass(*m_pwc);
+ m_pcs->lpszClass = m_pwc->lpszClassName;
+ }
+
+ // Set the CREATESTRUCT parameters
+ PreCreate(*m_pcs);
+
+ // Set the Window Class Name
+ if (!m_pcs->lpszClass)
+ m_pcs->lpszClass = _T("Win32++ Window");
+
+ // Set Parent
+ HWND hWndParent = pParent? pParent->GetHwnd() : 0;
+ if (!hWndParent && m_pcs->hwndParent)
+ hWndParent = m_pcs->hwndParent;
+
+ // Set the window style
+ DWORD dwStyle;
+ DWORD dwOverlappedStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
+ if (m_pcs->style)
+ dwStyle = m_pcs->style;
+ else
+ dwStyle = WS_VISIBLE | ((hWndParent)? WS_CHILD : dwOverlappedStyle);
+
+ // Set window size and position
+ int x = (m_pcs->cx || m_pcs->cy)? m_pcs->x : CW_USEDEFAULT;
+ int cx = (m_pcs->cx || m_pcs->cy)? m_pcs->cx : CW_USEDEFAULT;
+ int y = (m_pcs->cx || m_pcs->cy)? m_pcs->y : CW_USEDEFAULT;
+ int cy = (m_pcs->cx || m_pcs->cy)? m_pcs->cy : CW_USEDEFAULT;
+
+ // Create the window
+#ifndef _WIN32_WCE
+ CreateEx(m_pcs->dwExStyle, m_pcs->lpszClass, m_pcs->lpszName, dwStyle, x, y,
+ cx, cy, pParent, FromHandle(m_pcs->hMenu), m_pcs->lpCreateParams);
+#else
+ CreateEx(m_pcs->dwExStyle, m_pcs->lpszClass, m_pcs->lpszName, dwStyle, x, y,
+ cx, cy, pParent, 0, m_pcs->lpCreateParams);
+#endif
+
+ return m_hWnd;
+ }
+
+ inline HWND CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rc, CWnd* pParent, CMenu* pMenu, LPVOID lpParam /*= NULL*/)
+ // Creates the window by specifying all the window creation parameters
+ {
+ int x = rc.left;
+ int y = rc.top;
+ int cx = rc.right - rc.left;
+ int cy = rc.bottom - rc.top;
+ return CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, x, y, cx, cy, pParent, pMenu, lpParam);
+ }
+
+ inline HWND CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, CWnd* pParent, CMenu* pMenu, LPVOID lpParam /*= NULL*/)
+ // Creates the window by specifying all the window creation parameters
+ {
+
+ assert( GetApp() ); // Test if Win32++ has been started
+ assert(!::IsWindow(m_hWnd)); // Only one window per CWnd instance allowed
+
+ try
+ {
+ // Prepare the CWnd if it has been reused
+ Destroy();
+
+ // Ensure a window class is registered
+ std::vector<TCHAR> vTChar( MAX_STRING_SIZE+1, _T('\0') );
+ TCHAR* ClassName = &vTChar[0];
+ if (0 == lpszClassName || 0 == lstrlen(lpszClassName) )
+ lstrcpyn (ClassName, _T("Win32++ Window"), MAX_STRING_SIZE);
+ else
+ // Create our own local copy of szClassName.
+ lstrcpyn(ClassName, lpszClassName, MAX_STRING_SIZE);
+
+ WNDCLASS wc = {0};
+ wc.lpszClassName = ClassName;
+ wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
+ wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+
+ // Register the window class (if not already registered)
+ if (!RegisterClass(wc))
+ throw CWinException(_T("Failed to register window class"));
+
+ HWND hWndParent = pParent? pParent->GetHwnd() : 0;
+
+ // Ensure this thread has the TLS index set
+ TLSData* pTLSData = GetApp()->SetTlsIndex();
+
+ // Store the CWnd pointer in thread local storage
+ pTLSData->pCWnd = this;
+
+ // Create window
+#ifdef _WIN32_WCE
+ m_hWnd = ::CreateWindowEx(dwExStyle, ClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight,
+ hWndParent, 0, GetApp()->GetInstanceHandle(), lpParam);
+#else
+ HMENU hMenu = pMenu? pMenu->GetHandle() : NULL;
+ m_hWnd = ::CreateWindowEx(dwExStyle, ClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight,
+ hWndParent, hMenu, GetApp()->GetInstanceHandle(), lpParam);
+#endif
+
+ // Now handle window creation failure
+ if (!m_hWnd)
+ throw CWinException(_T("Failed to Create Window"));
+
+ // Automatically subclass predefined window class types
+ ::GetClassInfo(GetApp()->GetInstanceHandle(), lpszClassName, &wc);
+ if (wc.lpfnWndProc != GetApp()->m_Callback)
+ {
+ Subclass(m_hWnd);
+
+ // Send a message to force the HWND to be added to the map
+ SendMessage(WM_NULL, 0L, 0L);
+
+ OnCreate(); // We missed the WM_CREATE message, so call OnCreate now
+ }
+
+ // Clear the CWnd pointer from TLS
+ pTLSData->pCWnd = NULL;
+ }
+
+ catch (const CWinException &e)
+ {
+ TRACE(_T("\n*** Failed to create window ***\n"));
+ e.what(); // Display the last error message.
+
+ // eat the exception (don't rethrow)
+ }
+
+ // Window creation is complete. Now call OnInitialUpdate
+ OnInitialUpdate();
+
+ return m_hWnd;
+ }
+
+ inline void CWnd::Destroy()
+ // Destroys the window and returns the CWnd back to its default state, ready for reuse.
+ {
+ if (m_IsTmpWnd)
+ m_hWnd = NULL;
+
+ if (IsWindow())
+ ::DestroyWindow(m_hWnd);
+
+ // Return the CWnd to its default state
+ Cleanup();
+ }
+
+ inline HWND CWnd::Detach()
+ // Reverse an Attach
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(0 != m_PrevWindowProc); // Only a subclassed window can be detached
+
+ SetWindowLongPtr(GWLP_WNDPROC, (LONG_PTR)m_PrevWindowProc);
+ HWND hWnd = m_hWnd;
+ Cleanup();
+
+ return hWnd;
+ }
+
+ inline LRESULT CWnd::FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // Pass messages on to the appropriate default window procedure
+ // CMDIChild and CMDIFrame override this function
+ {
+ return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
+ }
+
+ inline CWnd* CWnd::GetAncestor(UINT gaFlags /*= GA_ROOTOWNER*/) const
+ // The GetAncestor function retrieves a pointer to the ancestor (root parent)
+ // of the window. Supports Win95.
+ {
+ assert(::IsWindow(m_hWnd));
+ HWND hWnd;
+
+#if (WINVER < 0x0500) // Win2000 and above
+ UNREFERENCED_PARAMETER(gaFlags);
+ hWnd = m_hWnd;
+ HWND hWndParent = ::GetParent(hWnd);
+ while (::IsChild(hWndParent, hWnd))
+ {
+ hWnd = hWndParent;
+ hWndParent = ::GetParent(hWnd);
+ }
+#else
+ hWnd = ::GetAncestor(m_hWnd, gaFlags);
+#endif
+
+ return FromHandle(hWnd);
+
+ }
+
+ inline CString CWnd::GetClassName() const
+ // Retrieves the name of the class to which the specified window belongs.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ CString str;
+ LPTSTR szStr = str.GetBuffer(MAX_STRING_SIZE+1);
+ ::GetClassName(m_hWnd, szStr, MAX_STRING_SIZE+1);
+ str.ReleaseBuffer();
+ return str;
+ }
+
+ inline CString CWnd::GetDlgItemText(int nIDDlgItem) const
+ // Retrieves the title or text associated with a control in a dialog box.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int nLength = ::GetWindowTextLength(::GetDlgItem(m_hWnd, nIDDlgItem));
+ CString str;
+ LPTSTR szStr = str.GetBuffer(nLength+1);
+ ::GetDlgItemText(m_hWnd, nIDDlgItem, szStr, nLength+1);
+ str.ReleaseBuffer();
+ return str;
+ }
+
+ inline CString CWnd::GetWindowText() const
+ // Retrieves the text of the window's title bar.
+ {
+ assert(::IsWindow(m_hWnd));
+
+ int nLength = ::GetWindowTextLength(m_hWnd);
+ CString str;
+ LPTSTR szStr = str.GetBuffer(nLength+1);
+ ::GetWindowText(m_hWnd, szStr, nLength+1);
+ str.ReleaseBuffer();
+ return str;
+ }
+
+ inline BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ // Override this to handle WM_COMMAND messages, for example
+
+ // switch (LOWORD(wParam))
+ // {
+ // case IDM_FILE_NEW:
+ // OnFileNew();
+ // TRUE; // return TRUE for handled commands
+ // }
+
+ // return FALSE for unhandled commands
+ return FALSE;
+ }
+
+ inline void CWnd::OnCreate()
+ {
+ // This function is called when a WM_CREATE message is recieved
+ // Override it in your derived class to automatically perform tasks
+ // during window creation.
+ }
+
+ inline void CWnd::OnDraw(CDC* pDC)
+ // Called when part of the client area of the window needs to be drawn
+ {
+ UNREFERENCED_PARAMETER(pDC);
+
+ // Override this function in your derived class to perform drawing tasks.
+ }
+
+ inline BOOL CWnd::OnEraseBkgnd(CDC* pDC)
+ // Called when the background of the window's client area needs to be erased.
+ {
+ UNREFERENCED_PARAMETER(pDC);
+
+ // Override this function in your derived class to perform drawing tasks.
+
+ // Return Value: Return FALSE to also permit default erasure of the background
+ // Return TRUE to prevent default erasure of the background
+
+ return FALSE;
+ }
+
+
+ inline void CWnd::OnInitialUpdate()
+ {
+ // This function is called automatically once the window is created
+ // Override it in your derived class to automatically perform tasks
+ // after window creation.
+ }
+
+ inline LRESULT CWnd::MessageReflect(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // A function used to call OnMessageReflect. You shouldn't need to call or
+ // override this function.
+
+ HWND hWnd = NULL;
+ switch (uMsg)
+ {
+ case WM_COMMAND:
+ case WM_CTLCOLORBTN:
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLORLISTBOX:
+ case WM_CTLCOLORSCROLLBAR:
+ case WM_CTLCOLORSTATIC:
+ case WM_CHARTOITEM:
+ case WM_VKEYTOITEM:
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ hWnd = (HWND)lParam;
+ break;
+
+ case WM_DRAWITEM:
+ case WM_MEASUREITEM:
+ case WM_DELETEITEM:
+ case WM_COMPAREITEM:
+ hWnd = ::GetDlgItem(hWndParent, (int)wParam);
+ break;
+
+ case WM_PARENTNOTIFY:
+ switch(LOWORD(wParam))
+ {
+ case WM_CREATE:
+ case WM_DESTROY:
+ hWnd = (HWND)lParam;
+ break;
+ }
+ }
+
+ CWnd* Wnd = GetApp()->GetCWndFromMap(hWnd);
+
+ if (Wnd != NULL)
+ return Wnd->OnMessageReflect(uMsg, wParam, lParam);
+
+ return 0L;
+ }
+
+ inline LRESULT CWnd::OnMessageReflect(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(uMsg);
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ // This function processes those special messages (see above) sent
+ // by some older controls, and reflects them back to the originating CWnd object.
+ // Override this function in your derrived class to handle these special messages.
+
+ // Your overriding function should look like this ...
+
+ // switch (uMsg)
+ // {
+ // Handle your reflected messages here
+ // }
+
+ // return 0L for unhandled messages
+ return 0L;
+ }
+
+ inline LRESULT CWnd::OnNotify(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ // You can use either OnNotifyReflect or OnNotify to handle notifications
+ // Override OnNotifyReflect to handle notifications in the CWnd class that
+ // generated the notification. OR
+ // Override OnNotify to handle notifications in the PARENT of the CWnd class
+ // that generated the notification.
+
+ // Your overriding function should look like this ...
+
+ // switch (((LPNMHDR)lParam)->code)
+ // {
+ // Handle your notifications from the CHILD window here
+ // Return the value recommended by the Windows API documentation.
+ // For many notifications, the return value doesn't matter, but for some it does.
+ // }
+
+ // return 0L for unhandled notifications
+ return 0L;
+ }
+
+ inline LRESULT CWnd::OnNotifyReflect(WPARAM wParam, LPARAM lParam)
+ {
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ // Override OnNotifyReflect to handle notifications in the CWnd class that
+ // generated the notification.
+
+ // Your overriding function should look like this ...
+
+ // switch (((LPNMHDR)lParam)->code)
+ // {
+ // Handle your notifications from this window here
+ // Return the value recommended by the Windows API documentation.
+ // }
+
+ // return 0L for unhandled notifications
+ return 0L;
+ }
+
+ inline void CWnd::OnMenuUpdate(UINT nID)
+ // Called when menu items are about to be displayed
+ {
+ UNREFERENCED_PARAMETER(nID);
+
+ // Override this function to modify the behaviour of menu items,
+ // such as adding or removing checkmarks
+ }
+
+ inline void CWnd::PreCreate(CREATESTRUCT& cs)
+ // Called by CWnd::Create to set some window parameters
+ {
+ // Test if Win32++ has been started
+ assert(GetApp()); // Test if Win32++ has been started
+
+ m_pcs->cx = cs.cx;
+ m_pcs->cy = cs.cy;
+ m_pcs->dwExStyle = cs.dwExStyle;
+ m_pcs->hInstance = GetApp()->GetInstanceHandle();
+ m_pcs->hMenu = cs.hMenu;
+ m_pcs->hwndParent = cs.hwndParent;
+ m_pcs->lpCreateParams = cs.lpCreateParams;
+ m_pcs->lpszClass = cs.lpszClass;
+ m_pcs->lpszName = cs.lpszName;
+ m_pcs->style = cs.style;
+ m_pcs->x = cs.x;
+ m_pcs->y = cs.y;
+
+ // Overide this function in your derived class to set the
+ // CREATESTRUCT values prior to window creation.
+ // The cs.lpszClass parameter should NOT be specified if the
+ // PreRegisterClass function is used to create a window class.
+ }
+
+ inline void CWnd::PreRegisterClass(WNDCLASS& wc)
+ // Called by CWnd::Create to set some window parameters
+ // Useful for setting the background brush and cursor
+ {
+ // Test if Win32++ has been started
+ assert( GetApp() );
+
+ m_pwc->style = wc.style;
+ m_pwc->lpfnWndProc = CWnd::StaticWindowProc;
+ m_pwc->cbClsExtra = wc.cbClsExtra;
+ m_pwc->cbWndExtra = wc.cbWndExtra;
+ m_pwc->hInstance = GetApp()->GetInstanceHandle();
+ m_pwc->hIcon = wc.hIcon;
+ m_pwc->hCursor = wc.hCursor;
+ m_pwc->hbrBackground = wc.hbrBackground;
+ m_pwc->lpszMenuName = wc.lpszMenuName;
+ m_pwc->lpszClassName = wc.lpszClassName;
+
+ // Overide this function in your derived class to set the
+ // WNDCLASS values prior to window creation.
+
+ // ADDITIONAL NOTES:
+ // 1) The lpszClassName must be set for this function to take effect.
+ // 2) The lpfnWndProc is always CWnd::StaticWindowProc.
+ // 3) No other defaults are set, so the following settings might prove useful
+ // wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+ // wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
+ // wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
+ // 4) The styles that can be set here are WNDCLASS styles. These are a different
+ // set of styles to those set by CREATESTRUCT (used in PreCreate).
+ // 5) RegisterClassEx is not used because its not supported on WinCE.
+ // To set a small icon for the window, use SetIconSmall.
+ }
+
+ inline BOOL CWnd::PreTranslateMessage(MSG* pMsg)
+ {
+ UNREFERENCED_PARAMETER(pMsg);
+
+ // Override this function if your class requires input messages to be
+ // translated before normal processing. Function which translate messages
+ // include TranslateAccelerator, TranslateMDISysAccel and IsDialogMessage.
+ // Return TRUE if the message is translated.
+
+ return FALSE;
+ }
+
+ inline BOOL CWnd::RegisterClass(WNDCLASS& wc)
+ // A private function used by the PreRegisterClass function to register a
+ // window class prior to window creation
+ {
+ assert( GetApp() );
+ assert( (0 != lstrlen(wc.lpszClassName) && ( lstrlen(wc.lpszClassName) <= MAX_STRING_SIZE) ) );
+
+ // Check to see if this classname is already registered
+ WNDCLASS wcTest = {0};
+ BOOL Done = FALSE;
+
+ if (::GetClassInfo(GetApp()->GetInstanceHandle(), wc.lpszClassName, &wcTest))
+ {
+ wc = wcTest;
+ Done = TRUE;
+ }
+
+ if (!Done)
+ {
+ // Set defaults
+ wc.hInstance = GetApp()->GetInstanceHandle();
+ wc.lpfnWndProc = CWnd::StaticWindowProc;
+
+ // Register the WNDCLASS structure
+ if ( !::RegisterClass(&wc) )
+ throw CWinException(_T("Failed to register window class"));
+
+ Done = TRUE;
+ }
+
+ return Done;
+ }
+
+ inline BOOL CWnd::RemoveFromMap()
+ {
+ BOOL Success = FALSE;
+
+ if (GetApp())
+ {
+
+ // Allocate an iterator for our HWND map
+ std::map<HWND, CWnd*, CompareHWND>::iterator m;
+
+ CWinApp* pApp = GetApp();
+ if (pApp)
+ {
+ // Erase the CWnd pointer entry from the map
+ pApp->m_csMapLock.Lock();
+ for (m = pApp->m_mapHWND.begin(); m != pApp->m_mapHWND.end(); ++m)
+ {
+ if (this == m->second)
+ {
+ pApp->m_mapHWND.erase(m);
+ Success = TRUE;
+ break;
+ }
+ }
+
+ pApp->m_csMapLock.Release();
+ }
+ }
+
+ return Success;
+ }
+
+ inline HICON CWnd::SetIconLarge(int nIcon)
+ // Sets the large icon associated with the window
+ {
+ assert( GetApp() );
+ assert(::IsWindow(m_hWnd));
+
+ HICON hIconLarge = (HICON) (::LoadImage (GetApp()->GetResourceHandle(), MAKEINTRESOURCE (nIcon), IMAGE_ICON,
+ ::GetSystemMetrics (SM_CXICON), ::GetSystemMetrics (SM_CYICON), 0));
+
+ if (hIconLarge)
+ SendMessage (WM_SETICON, WPARAM (ICON_BIG), LPARAM (hIconLarge));
+ else
+ TRACE(_T("**WARNING** SetIconLarge Failed\n"));
+
+ return hIconLarge;
+ }
+
+ inline HICON CWnd::SetIconSmall(int nIcon)
+ // Sets the small icon associated with the window
+ {
+ assert( GetApp() );
+ assert(::IsWindow(m_hWnd));
+
+ HICON hIconSmall = (HICON) (::LoadImage (GetApp()->GetResourceHandle(), MAKEINTRESOURCE (nIcon), IMAGE_ICON,
+ ::GetSystemMetrics (SM_CXSMICON), ::GetSystemMetrics (SM_CYSMICON), 0));
+
+ if (hIconSmall)
+ SendMessage (WM_SETICON, WPARAM (ICON_SMALL), LPARAM (hIconSmall));
+ else
+ TRACE(_T("**WARNING** SetIconSmall Failed\n"));
+
+ return hIconSmall;
+ }
+
+ inline LRESULT CALLBACK CWnd::StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // All CWnd windows direct their messages here. This function redirects the message
+ // to the CWnd's WndProc function.
+ {
+ assert( GetApp() );
+
+ CWnd* w = GetApp()->GetCWndFromMap(hWnd);
+ if (0 == w)
+ {
+ // The CWnd pointer wasn't found in the map, so add it now
+
+ // Retrieve the pointer to the TLS Data
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ assert(pTLSData);
+
+ // Retrieve pointer to CWnd object from Thread Local Storage TLS
+ w = pTLSData->pCWnd;
+ assert(w); // pTLSData->pCWnd is assigned in CreateEx
+ pTLSData->pCWnd = NULL;
+
+ // Store the CWnd pointer in the HWND map
+ w->m_hWnd = hWnd;
+ w->AddToMap();
+ }
+
+ return w->WndProc(uMsg, wParam, lParam);
+
+ } // LRESULT CALLBACK StaticWindowProc(...)
+
+ inline void CWnd::Subclass(HWND hWnd)
+ // A private function used by CreateEx, Attach and AttachDlgItem
+ {
+ assert(::IsWindow(hWnd));
+
+ m_PrevWindowProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)CWnd::StaticWindowProc);
+ m_hWnd = hWnd;
+ }
+
+ inline LRESULT CWnd::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Override this function in your class derrived from CWnd to handle
+ // window messages. A typical function might look like this:
+
+ // switch (uMsg)
+ // {
+ // case MESSAGE1: // Some Windows API message
+ // OnMessage1(); // A user defined function
+ // break; // Also do default processing
+ // case MESSAGE2:
+ // OnMessage2();
+ // return x; // Don't do default processing, but instead return
+ // // a value recommended by the Windows API documentation
+ // }
+
+ // Always pass unhandled messages on to WndProcDefault
+ return WndProcDefault(uMsg, wParam, lParam);
+ }
+
+ inline LRESULT CWnd::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
+ // All WndProc functions should pass unhandled window messages to this function
+ {
+ LRESULT lr = 0L;
+
+ switch (uMsg)
+ {
+ case UWM_CLEANUPTEMPS:
+ {
+ TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex());
+ pTLSData->vTmpWnds.clear();
+ }
+ break;
+ case WM_COMMAND:
+ {
+ // Refelect this message if it's from a control
+ CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam);
+ if (pWnd != NULL)
+ lr = pWnd->OnCommand(wParam, lParam);
+
+ // Handle user commands
+ if (!lr)
+ lr = OnCommand(wParam, lParam);
+
+ if (lr) return 0L;
+ }
+ break; // Note: Some MDI commands require default processing
+ case WM_CREATE:
+ OnCreate();
+ break;
+ // An example of how to end the application when the window closes
+ // If needed, put this in the class you inherit from CWnd
+ // case WM_DESTROY:
+ // ::PostQuitMessage(0);
+ // return 0L;
+ case WM_NOTIFY:
+ {
+ // Do Notification reflection if it came from a CWnd object
+ HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom;
+ CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom);
+
+ if (lstrcmp(GetClassName(), _T("ReBarWindow32")) != 0) // Skip notification reflection for rebars to avoid double handling
+ {
+ if (pWndFrom != NULL)
+ lr = pWndFrom->OnNotifyReflect(wParam, lParam);
+ else
+ {
+ // Some controls (eg ListView) have child windows.
+ // Reflect those notifications too.
+ CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom));
+ if (pWndFromParent != NULL)
+ lr = pWndFromParent->OnNotifyReflect(wParam, lParam);
+ }
+ }
+
+ // Handle user notifications
+ if (!lr) lr = OnNotify(wParam, lParam);
+ if (lr) return lr;
+ }
+ break;
+
+ case WM_PAINT:
+ {
+ // Subclassed controls expect to do their own painting.
+ // CustomDraw or OwnerDraw are normally used to modify the drawing of controls.
+ if (m_PrevWindowProc) break;
+
+ if (::GetUpdateRect(m_hWnd, NULL, FALSE))
+ {
+ CPaintDC dc(this);
+ OnDraw(&dc);
+ }
+ else
+ // RedrawWindow can require repainting without an update rect
+ {
+ CClientDC dc(this);
+ OnDraw(&dc);
+ }
+ }
+ return 0L;
+
+ case WM_ERASEBKGND:
+ {
+ CDC dc((HDC)wParam);
+ BOOL bResult = OnEraseBkgnd(&dc);
+ dc.Detach();
+ if (bResult) return TRUE;
+ }
+ break;
+
+ // A set of messages to be reflected back to the control that generated them
+ case WM_CTLCOLORBTN:
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLORLISTBOX:
+ case WM_CTLCOLORSCROLLBAR:
+ case WM_CTLCOLORSTATIC:
+ case WM_DRAWITEM:
+ case WM_MEASUREITEM:
+ case WM_DELETEITEM:
+ case WM_COMPAREITEM:
+ case WM_CHARTOITEM:
+ case WM_VKEYTOITEM:
+ case WM_HSCROLL:
+ case WM_VSCROLL:
+ case WM_PARENTNOTIFY:
+ {
+ // if (m_PrevWindowProc) break; // Suppress for subclassed windows
+
+ LRESULT lr = MessageReflect(m_hWnd, uMsg, wParam, lParam);
+ if (lr) return lr; // Message processed so return
+ }
+ break; // Do default processing when message not already processed
+
+ case UWM_UPDATE_COMMAND:
+ OnMenuUpdate((UINT)wParam); // Perform menu updates
+ break;
+
+ } // switch (uMsg)
+
+ // Now hand all messages to the default procedure
+ if (m_PrevWindowProc)
+ return ::CallWindowProc(m_PrevWindowProc, m_hWnd, uMsg, wParam, lParam);
+ else
+ return FinalWindowProc(uMsg, wParam, lParam);
+
+ } // LRESULT CWnd::WindowProc(...)
+
+
+ //
+ // Wrappers for Win32 API functions
+ //
+
+ inline CDC* CWnd::BeginPaint(PAINTSTRUCT& ps) const
+ // The BeginPaint function prepares the specified window for painting and fills a PAINTSTRUCT structure with
+ // information about the painting.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle(::BeginPaint(m_hWnd, &ps));
+ }
+
+ inline BOOL CWnd::BringWindowToTop() const
+ // The BringWindowToTop function brings the specified window to the top
+ // of the Z order. If the window is a top-level window, it is activated.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::BringWindowToTop(m_hWnd);
+ }
+
+ inline LRESULT CWnd::CallWindowProc(WNDPROC lpPrevWndFunc, UINT Msg, WPARAM wParam, LPARAM lParam) const
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::CallWindowProc(lpPrevWndFunc, m_hWnd, Msg, wParam, lParam);
+ }
+
+ inline BOOL CWnd::CheckDlgButton(int nIDButton, UINT uCheck) const
+ // The CheckDlgButton function changes the check state of a button control.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::CheckDlgButton(m_hWnd, nIDButton, uCheck);
+ }
+
+ inline BOOL CWnd::CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton) const
+ // The CheckRadioButton function adds a check mark to (checks) a specified radio button in a group
+ // and removes a check mark from (clears) all other radio buttons in the group.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::CheckRadioButton(m_hWnd, nIDFirstButton, nIDLastButton, nIDCheckButton);
+ }
+
+ inline CWnd* CWnd::ChildWindowFromPoint(POINT pt) const
+ // determines which, if any, of the child windows belonging to a parent window contains
+ // the specified point. The search is restricted to immediate child windows.
+ // Grandchildren, and deeper descendant windows are not searched.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle(::ChildWindowFromPoint(m_hWnd, pt));
+ }
+
+ inline BOOL CWnd::ClientToScreen(POINT& pt) const
+ // The ClientToScreen function converts the client-area coordinates of a specified point to screen coordinates.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ClientToScreen(m_hWnd, &pt);
+ }
+
+ inline BOOL CWnd::ClientToScreen(RECT& rc) const
+ // The ClientToScreen function converts the client-area coordinates of a specified RECT to screen coordinates.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)::MapWindowPoints(m_hWnd, NULL, (LPPOINT)&rc, 2);
+ }
+
+ inline HDWP CWnd::DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const
+ // The DeferWindowPos function updates the specified multiple-window – position structure for the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DeferWindowPos(hWinPosInfo, m_hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
+ }
+
+ inline HDWP CWnd::DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const
+ // The DeferWindowPos function updates the specified multiple-window – position structure for the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DeferWindowPos(hWinPosInfo, m_hWnd, hWndInsertAfter, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, uFlags);
+ }
+
+ inline LRESULT CWnd::DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) const
+ // This function provides default processing for any window messages that an application does not process.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
+ }
+
+ inline BOOL CWnd::DrawMenuBar() const
+ // The DrawMenuBar function redraws the menu bar of the specified window. If the menu bar changes after
+ // the system has created the window, this function must be called to draw the changed menu bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DrawMenuBar(m_hWnd);
+ }
+
+ inline BOOL CWnd::EnableWindow(BOOL bEnable /*= TRUE*/) const
+ // The EnableWindow function enables or disables mouse and
+ // keyboard input to the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::EnableWindow(m_hWnd, bEnable);
+ }
+
+ inline BOOL CWnd::EndPaint(PAINTSTRUCT& ps) const
+ // The EndPaint function marks the end of painting in the specified window. This function is required for
+ // each call to the BeginPaint function, but only after painting is complete.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::EndPaint(m_hWnd, &ps);
+ }
+
+ inline CWnd* CWnd::GetActiveWindow() const
+ // The GetActiveWindow function retrieves a pointer to the active window attached to the calling
+ // thread's message queue.
+ {
+ return FromHandle( ::GetActiveWindow() );
+ }
+
+ inline CWnd* CWnd::GetCapture() const
+ // The GetCapture function retrieves a pointer to the window (if any) that has captured the mouse.
+ {
+ return FromHandle( ::GetCapture() );
+ }
+
+ inline ULONG_PTR CWnd::GetClassLongPtr(int nIndex) const
+ // The GetClassLongPtr function retrieves the specified value from the
+ // WNDCLASSEX structure associated with the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetClassLongPtr(m_hWnd, nIndex);
+ }
+
+ inline CRect CWnd::GetClientRect() const
+ // The GetClientRect function retrieves the coordinates of a window's client area.
+ // The client coordinates specify the upper-left and lower-right corners of the
+ // client area. Because client coordinates are relative to the upper-left corner
+ // of a window's client area, the coordinates of the upper-left corner are (0,0).
+ {
+ assert(::IsWindow(m_hWnd));
+ CRect rc;
+ ::GetClientRect(m_hWnd, &rc);
+ return rc;
+ }
+
+ inline CDC* CWnd::GetDC() const
+ // The GetDC function retrieves a handle to a display device context (DC) for the
+ // client area of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return CDC::AddTempHDC(::GetDC(m_hWnd), m_hWnd);
+ }
+
+ inline CDC* CWnd::GetDCEx(HRGN hrgnClip, DWORD flags) const
+ // The GetDCEx function retrieves a handle to a display device context (DC) for the
+ // client area or entire area of a window
+ {
+ assert(::IsWindow(m_hWnd));
+ return CDC::AddTempHDC(::GetDCEx(m_hWnd, hrgnClip, flags), m_hWnd);
+ }
+
+ inline CWnd* CWnd::GetDesktopWindow() const
+ // The GetDesktopWindow function retrieves a pointer to the desktop window.
+ {
+ return FromHandle( ::GetDesktopWindow() );
+ }
+
+ inline CWnd* CWnd::GetDlgItem(int nIDDlgItem) const
+ // The GetDlgItem function retrieves a handle to a control in the dialog box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::GetDlgItem(m_hWnd, nIDDlgItem) );
+ }
+
+ inline UINT CWnd::GetDlgItemInt(int nIDDlgItem, BOOL* lpTranslated, BOOL bSigned) const
+ // The GetDlgItemInt function translates the text of a specified control in a dialog box into an integer value.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetDlgItemInt(m_hWnd, nIDDlgItem, lpTranslated, bSigned);
+ }
+
+ inline CWnd* CWnd::GetFocus() const
+ // The GetFocus function retrieves a pointer to the window that has the keyboard focus, if the window
+ // is attached to the calling thread's message queue.
+ {
+ return FromHandle( ::GetFocus() );
+ }
+
+ inline CFont* CWnd::GetFont() const
+ // Retrieves the font with which the window is currently drawing its text.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle((HFONT)SendMessage(WM_GETFONT, 0, 0));
+ }
+
+ inline HICON CWnd::GetIcon(BOOL bBigIcon) const
+ // Retrieves a handle to the large or small icon associated with a window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage(WM_GETICON, (WPARAM)bBigIcon, 0);
+ }
+
+ inline CWnd* CWnd::GetNextDlgGroupItem(CWnd* pCtl, BOOL bPrevious) const
+ // The GetNextDlgGroupItem function retrieves a pointer to the first control in a group of controls that
+ // precedes (or follows) the specified control in a dialog box.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pCtl);
+ return FromHandle(::GetNextDlgGroupItem(m_hWnd, pCtl->GetHwnd(), bPrevious));
+ }
+
+ inline CWnd* CWnd::GetNextDlgTabItem(CWnd* pCtl, BOOL bPrevious) const
+ // The GetNextDlgTabItem function retrieves a pointer to the first control that has the WS_TABSTOP style
+ // that precedes (or follows) the specified control.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pCtl);
+ return FromHandle(::GetNextDlgTabItem(m_hWnd, pCtl->GetHwnd(), bPrevious));
+ }
+
+ inline CWnd* CWnd::GetParent() const
+ // The GetParent function retrieves a pointer to the specified window's parent or owner.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::GetParent(m_hWnd) );
+ }
+
+ inline LONG_PTR CWnd::GetWindowLongPtr(int nIndex) const
+ // The GetWindowLongPtr function retrieves information about the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetWindowLongPtr(m_hWnd, nIndex);
+ }
+
+ inline BOOL CWnd::GetScrollInfo(int fnBar, SCROLLINFO& si) const
+ // The GetScrollInfo function retrieves the parameters of a scroll bar, including
+ // the minimum and maximum scrolling positions, the page size, and the position
+ // of the scroll box (thumb).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetScrollInfo(m_hWnd, fnBar, &si);
+ }
+
+ inline CRect CWnd::GetUpdateRect(BOOL bErase) const
+ // The GetUpdateRect function retrieves the coordinates of the smallest rectangle that completely
+ // encloses the update region of the specified window.
+ {
+ assert(::IsWindow(m_hWnd));
+ CRect rc;
+ ::GetUpdateRect(m_hWnd, &rc, bErase);
+ return rc;
+ }
+
+ inline int CWnd::GetUpdateRgn(CRgn* pRgn, BOOL bErase) const
+ // The GetUpdateRgn function retrieves the update region of a window by copying it into the specified region.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pRgn);
+ HRGN hRgn = (HRGN)pRgn->GetHandle();
+ return ::GetUpdateRgn(m_hWnd, hRgn, bErase);
+ }
+
+ inline CWnd* CWnd::GetWindow(UINT uCmd) const
+ // The GetWindow function retrieves a pointer to a window that has the specified
+ // relationship (Z-Order or owner) to the specified window.
+ // Possible uCmd values: GW_CHILD, GW_ENABLEDPOPUP, GW_HWNDFIRST, GW_HWNDLAST,
+ // GW_HWNDNEXT, GW_HWNDPREV, GW_OWNER
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::GetWindow(m_hWnd, uCmd) );
+ }
+
+ inline CDC* CWnd::GetWindowDC() const
+ // The GetWindowDC function retrieves the device context (DC) for the entire
+ // window, including title bar, menus, and scroll bars.
+ {
+ assert(::IsWindow(m_hWnd));
+ return CDC::AddTempHDC(::GetWindowDC(m_hWnd), m_hWnd);
+ }
+
+ inline CRect CWnd::GetWindowRect() const
+ // retrieves the dimensions of the bounding rectangle of the window.
+ // The dimensions are given in screen coordinates that are relative to the
+ // upper-left corner of the screen.
+ {
+ assert(::IsWindow(m_hWnd));
+ CRect rc;
+ ::GetWindowRect(m_hWnd, &rc);
+ return rc;
+ }
+
+ inline int CWnd::GetWindowTextLength() const
+ // The GetWindowTextLength function retrieves the length, in characters, of the specified window's
+ // title bar text (if the window has a title bar).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetWindowTextLength(m_hWnd);
+ }
+
+ inline void CWnd::Invalidate(BOOL bErase /*= TRUE*/) const
+ // The Invalidate function adds the entire client area the window's update region.
+ // The update region represents the portion of the window's client area that must be redrawn.
+ {
+ assert(::IsWindow(m_hWnd));
+ ::InvalidateRect(m_hWnd, NULL, bErase);
+ }
+
+ inline BOOL CWnd::InvalidateRect(LPCRECT lpRect, BOOL bErase /*= TRUE*/) const
+ // The InvalidateRect function adds a rectangle to the window's update region.
+ // The update region represents the portion of the window's client area that must be redrawn.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::InvalidateRect(m_hWnd, lpRect, bErase);
+ }
+
+ inline BOOL CWnd::InvalidateRgn(CRgn* pRgn, BOOL bErase /*= TRUE*/) const
+ // The InvalidateRgn function invalidates the client area within the specified region
+ // by adding it to the current update region of a window. The invalidated region,
+ // along with all other areas in the update region, is marked for painting when the
+ // next WM_PAINT message occurs.
+ {
+ assert(::IsWindow(m_hWnd));
+ HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL;
+ return ::InvalidateRgn(m_hWnd, hRgn, bErase);
+ }
+
+ inline BOOL CWnd::IsChild(CWnd* pChild) const
+ // The IsChild function tests whether a window is a child window or descendant window
+ // of a parent window's CWnd.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsChild(m_hWnd, pChild->GetHwnd());
+ }
+
+ inline BOOL CWnd::IsDialogMessage(LPMSG lpMsg) const
+ // The IsDialogMessage function determines whether a message is intended for the specified dialog box and,
+ // if it is, processes the message.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsDialogMessage(m_hWnd, lpMsg);
+ }
+
+ inline UINT CWnd::IsDlgButtonChecked(int nIDButton) const
+ // The IsDlgButtonChecked function determines whether a button control has a check mark next to it
+ // or whether a three-state button control is grayed, checked, or neither.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsDlgButtonChecked(m_hWnd, nIDButton);
+ }
+
+ inline BOOL CWnd::IsWindowEnabled() const
+ // The IsWindowEnabled function determines whether the window is enabled
+ // for mouse and keyboard input.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsWindowEnabled(m_hWnd);
+ }
+
+ inline BOOL CWnd::IsWindowVisible() const
+ // The IsWindowVisible function retrieves the visibility state of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsWindowVisible(m_hWnd);
+ }
+
+ inline BOOL CWnd::IsWindow() const
+ // The IsWindow function determines whether the window exists.
+ {
+ return ::IsWindow(m_hWnd);
+ }
+
+ inline void CWnd::MapWindowPoints(CWnd* pWndTo, POINT& pt) const
+ // The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one
+ // window to a coordinate space relative to another window.
+ {
+ assert (m_hWnd);
+ if(pWndTo)
+ {
+ assert (pWndTo->GetHwnd());
+ ::MapWindowPoints(m_hWnd, pWndTo->GetHwnd(), &pt, 1);
+ }
+ else
+ ::MapWindowPoints(m_hWnd, NULL, &pt, 1);
+ }
+
+ inline void CWnd::MapWindowPoints(CWnd* pWndTo, RECT& rc) const
+ // The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one
+ // window to a coordinate space relative to another window.
+ {
+ assert (m_hWnd);
+ if(pWndTo)
+ {
+ assert (pWndTo->GetHwnd());
+ ::MapWindowPoints(m_hWnd, pWndTo->GetHwnd(), (LPPOINT)&rc, 2);
+ }
+ else
+ ::MapWindowPoints(m_hWnd, NULL, (LPPOINT)&rc, 2);
+ }
+
+ inline void CWnd::MapWindowPoints(CWnd* pWndTo, LPPOINT ptArray, UINT nCount) const
+ // The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one
+ // window to a coordinate space relative to another window.
+ {
+ assert (m_hWnd);
+ if (pWndTo)
+ {
+ assert (pWndTo->GetHwnd());
+ ::MapWindowPoints(m_hWnd, pWndTo->GetHwnd(), (LPPOINT)ptArray, nCount);
+ }
+ else
+ ::MapWindowPoints(m_hWnd, NULL, (LPPOINT)ptArray, nCount);
+ }
+
+ inline int CWnd::MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) const
+ // The MessageBox function creates, displays, and operates a message box.
+ // Possible combinations of uType values include: MB_OK, MB_HELP, MB_OKCANCEL, MB_RETRYCANCEL,
+ // MB_YESNO, MB_YESNOCANCEL, MB_ICONEXCLAMATION, MB_ICONWARNING, MB_ICONERROR (+ many others).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::MessageBox(m_hWnd, lpText, lpCaption, uType);
+ }
+
+ inline BOOL CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint /* = TRUE*/) const
+ // The MoveWindow function changes the position and dimensions of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint = TRUE);
+ }
+
+ inline BOOL CWnd::MoveWindow(const RECT& rc, BOOL bRepaint /* = TRUE*/) const
+ // The MoveWindow function changes the position and dimensions of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::MoveWindow(m_hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, bRepaint);
+ }
+
+ inline BOOL CWnd::PostMessage(UINT uMsg, WPARAM wParam /*= 0L*/, LPARAM lParam /*= 0L*/) const
+ // The PostMessage function places (posts) a message in the message queue
+ // associated with the thread that created the window and returns without
+ // waiting for the thread to process the message.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::PostMessage(m_hWnd, uMsg, wParam, lParam);
+ }
+
+ inline BOOL CWnd::PostMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const
+ // Required by by some macros
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::PostMessage(hWnd, uMsg, wParam, lParam);
+ }
+
+ inline BOOL CWnd::RedrawWindow(LPCRECT lpRectUpdate, CRgn* pRgn, UINT flags) const
+ // The RedrawWindow function updates the specified rectangle or region in a window's client area.
+ {
+ assert(::IsWindow(m_hWnd));
+ HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL;
+ return ::RedrawWindow(m_hWnd, lpRectUpdate, hRgn, flags);
+ }
+
+ inline int CWnd::ReleaseDC(CDC* pDC) const
+ // The ReleaseDC function releases a device context (DC), freeing it for use
+ // by other applications.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pDC);
+ return ::ReleaseDC(m_hWnd, pDC->GetHDC());
+ }
+
+ inline BOOL CWnd::ScreenToClient(POINT& Point) const
+ // The ScreenToClient function converts the screen coordinates of a specified point on the screen to client-area coordinates.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ScreenToClient(m_hWnd, &Point);
+ }
+
+ inline BOOL CWnd::ScreenToClient(RECT& rc) const
+ // The ScreenToClient function converts the screen coordinates of a specified RECT on the screen to client-area coordinates.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rc, 2);
+ }
+
+ inline LRESULT CWnd::SendDlgItemMessage(int nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam) const
+ // The SendDlgItemMessage function sends a message to the specified control in a dialog box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SendDlgItemMessage(m_hWnd, nIDDlgItem, Msg, wParam, lParam);
+ }
+
+ inline LRESULT CWnd::SendMessage(UINT uMsg, WPARAM wParam /*= 0L*/, LPARAM lParam /*= 0L*/) const
+ // The SendMessage function sends the specified message to a window or windows.
+ // It calls the window procedure for the window and does not return until the
+ // window procedure has processed the message.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SendMessage(m_hWnd, uMsg, wParam, lParam);
+ }
+
+ inline LRESULT CWnd::SendMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const
+ // Required by by some macros
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SendMessage(hWnd, uMsg, wParam, lParam);
+ }
+
+ inline BOOL CWnd::SendNotifyMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const
+ // The SendNotifyMessage function sends the specified message to a window or windows. If the window was created by the
+ // calling thread, SendNotifyMessage calls the window procedure for the window and does not return until the window procedure
+ // has processed the message. If the window was created by a different thread, SendNotifyMessage passes the message to the
+ // window procedure and returns immediately; it does not wait for the window procedure to finish processing the message.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SendNotifyMessage(m_hWnd, Msg, wParam, lParam);
+ }
+
+ inline CWnd* CWnd::SetActiveWindow() const
+ // The SetActiveWindow function activates the window, but
+ // not if the application is in the background.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::SetActiveWindow(m_hWnd) );
+ }
+
+ inline CWnd* CWnd::SetCapture() const
+ // The SetCapture function sets the mouse capture to the window.
+ // SetCapture captures mouse input either when the mouse is over the capturing
+ // window, or when the mouse button was pressed while the mouse was over the
+ // capturing window and the button is still down.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::SetCapture(m_hWnd) );
+ }
+
+ inline ULONG_PTR CWnd::SetClassLongPtr(int nIndex, LONG_PTR dwNewLong) const
+ // The SetClassLongPtr function replaces the specified value at the specified offset in the
+ // extra class memory or the WNDCLASSEX structure for the class to which the window belongs.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetClassLongPtr(m_hWnd, nIndex, dwNewLong);
+ }
+
+ inline CWnd* CWnd::SetFocus() const
+ // The SetFocus function sets the keyboard focus to the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::SetFocus(m_hWnd) );
+ }
+
+ inline void CWnd::SetFont(CFont* pFont, BOOL bRedraw /* = TRUE*/) const
+ // Specifies the font that the window will use when drawing text.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pFont);
+ SendMessage(WM_SETFONT, (WPARAM)pFont->GetHandle(), (LPARAM)bRedraw);
+ }
+
+ inline HICON CWnd::SetIcon(HICON hIcon, BOOL bBigIcon) const
+ // Associates a new large or small icon with a window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (HICON)SendMessage(WM_SETICON, (WPARAM)bBigIcon, (LPARAM)hIcon);
+ }
+
+ inline BOOL CWnd::SetForegroundWindow() const
+ // The SetForegroundWindow function puts the thread that created the window into the
+ // foreground and activates the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetForegroundWindow(m_hWnd);
+ }
+
+ inline CWnd* CWnd::SetParent(CWnd* pWndParent) const
+ // The SetParent function changes the parent window of the child window.
+ {
+ assert(::IsWindow(m_hWnd));
+ if (pWndParent)
+ {
+ HWND hParent = pWndParent->GetHwnd();
+ return FromHandle(::SetParent(m_hWnd, hParent));
+ }
+ else
+ return FromHandle(::SetParent(m_hWnd, 0));
+ }
+
+ inline BOOL CWnd::SetRedraw(BOOL bRedraw /*= TRUE*/) const
+ // This function allows changes in that window to be redrawn or prevents changes
+ // in that window from being redrawn.
+ {
+ assert(::IsWindow(m_hWnd));
+ return (BOOL)::SendMessage(m_hWnd, WM_SETREDRAW, (WPARAM)bRedraw, 0L);
+ }
+
+ inline LONG_PTR CWnd::SetWindowLongPtr(int nIndex, LONG_PTR dwNewLong) const
+ // The SetWindowLongPtr function changes an attribute of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetWindowLongPtr(m_hWnd, nIndex, dwNewLong);
+ }
+
+ inline BOOL CWnd::SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const
+ // The SetWindowPos function changes the size, position, and Z order of a child, pop-up,
+ // or top-level window. The hWndInsertAfter can be a HWND or one of:
+ // HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOP, HWND_TOPMOST
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetWindowPos(m_hWnd, hWndInsertAfter, x, y, cx, cy, uFlags);
+ }
+
+ inline BOOL CWnd::SetWindowPos(HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const
+ // The SetWindowPos function changes the size, position, and Z order of a child, pop-up,
+ // or top-level window. The hWndInsertAfter can be a HWND or one of:
+ // HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOP, HWND_TOPMOST
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetWindowPos(m_hWnd, hWndInsertAfter, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, uFlags);
+ }
+
+ inline int CWnd::SetWindowRgn(CRgn* pRgn, BOOL bRedraw /*= TRUE*/) const
+ // The SetWindowRgn function sets the window region of the window.
+ // The window region determines the area within the window where the system permits drawing.
+ {
+ assert(::IsWindow(m_hWnd));
+ HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL;
+ int iResult = ::SetWindowRgn(m_hWnd, hRgn, bRedraw);
+ if (iResult && pRgn)
+ pRgn->Detach(); // The system owns the region now
+ return iResult;
+ }
+
+ inline BOOL CWnd::SetDlgItemInt(int nIDDlgItem, UINT uValue, BOOL bSigned) const
+ // The SetDlgItemInt function sets the text of a control in a dialog box to the string representation of a specified integer value.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetDlgItemInt(m_hWnd, nIDDlgItem, uValue, bSigned);
+ }
+
+ inline BOOL CWnd::SetDlgItemText(int nIDDlgItem, LPCTSTR lpString) const
+ // The SetDlgItemText function sets the title or text of a control in a dialog box.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetDlgItemText(m_hWnd, nIDDlgItem, lpString);
+ }
+
+ inline UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc) const
+ // Creates a timer with the specified time-out value.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetTimer(m_hWnd, nIDEvent, uElapse, lpTimerFunc);
+ }
+
+ inline BOOL CWnd::SetWindowText(LPCTSTR lpString) const
+ // The SetWindowText function changes the text of the window's title bar (if it has one).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetWindowText(m_hWnd, lpString);
+ }
+
+ inline HRESULT CWnd::SetWindowTheme(LPCWSTR pszSubAppName, LPCWSTR pszSubIdList) const
+ // Set the XP Theme for a window.
+ // Exampes:
+ // SetWindowTheme(NULL, NULL); // Reverts the window's XP theme back to default
+ // SetWindowTheme(L" ", L" "); // Disables XP theme for the window
+ {
+ HRESULT hr = E_NOTIMPL;
+
+#ifndef _WIN32_WCE
+
+ HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll"));
+ if(hMod)
+ {
+ typedef HRESULT (__stdcall *PFNSETWINDOWTHEME)(HWND hWnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
+ PFNSETWINDOWTHEME pfn = (PFNSETWINDOWTHEME)GetProcAddress(hMod, "SetWindowTheme");
+
+ hr = (*pfn)(m_hWnd, pszSubAppName, pszSubIdList);
+
+ ::FreeLibrary(hMod);
+ }
+
+#endif
+
+ return hr;
+ }
+
+ inline BOOL CWnd::ShowWindow(int nCmdShow /*= SW_SHOWNORMAL*/) const
+ // The ShowWindow function sets the window's show state.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ShowWindow(m_hWnd, nCmdShow);
+ }
+
+ inline BOOL CWnd::UpdateWindow() const
+ // The UpdateWindow function updates the client area of the window by sending a
+ // WM_PAINT message to the window if the window's update region is not empty.
+ // If the update region is empty, no message is sent.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::UpdateWindow(m_hWnd);
+ }
+
+ inline BOOL CWnd::ValidateRect(LPCRECT prc) const
+ // The ValidateRect function validates the client area within a rectangle by
+ // removing the rectangle from the update region of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ValidateRect(m_hWnd, prc);
+ }
+
+ inline BOOL CWnd::ValidateRgn(CRgn* pRgn) const
+ // The ValidateRgn function validates the client area within a region by
+ // removing the region from the current update region of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL;
+ return ::ValidateRgn(m_hWnd, hRgn);
+ }
+
+ inline CWnd* CWnd::WindowFromPoint(POINT pt)
+ // Retrieves the window that contains the specified point (in screen coodinates).
+ {
+ return FromHandle(::WindowFromPoint(pt));
+ }
+
+ //
+ // These functions aren't supported on WinCE
+ //
+ #ifndef _WIN32_WCE
+ inline BOOL CWnd::CloseWindow() const
+ // The CloseWindow function minimizes (but does not destroy) the window.
+ // To destroy a window, an application can use the Destroy function.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::CloseWindow(m_hWnd);
+ }
+
+ inline int CWnd::DlgDirList(LPTSTR lpPathSpec, int nIDListBox, int nIDStaticPath, UINT uFileType) const
+ // The DlgDirList function replaces the contents of a list box with the names of the subdirectories and files
+ // in a specified directory. You can filter the list of names by specifying a set of file attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DlgDirList(m_hWnd, lpPathSpec, nIDListBox, nIDStaticPath, uFileType);
+ }
+
+ inline int CWnd::DlgDirListComboBox(LPTSTR lpPathSpec, int nIDComboBox, int nIDStaticPath, UINT uFiletype) const
+ // The DlgDirListComboBox function replaces the contents of a combo box with the names of the subdirectories
+ // and files in a specified directory. You can filter the list of names by specifying a set of file attributes.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DlgDirListComboBox(m_hWnd, lpPathSpec, nIDComboBox, nIDStaticPath, uFiletype);
+ }
+
+ inline BOOL CWnd::DlgDirSelectEx(LPTSTR lpString, int nCount, int nIDListBox) const
+ // The DlgDirSelectEx function retrieves the current selection from a single-selection list box. It assumes that the list box
+ // has been filled by the DlgDirList function and that the selection is a drive letter, filename, or directory name.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DlgDirSelectEx(m_hWnd, lpString, nCount, nIDListBox);
+ }
+
+ inline BOOL CWnd::DlgDirSelectComboBoxEx(LPTSTR lpString, int nCount, int nIDComboBox) const
+ // The DlgDirSelectComboBoxEx function retrieves the current selection from a combo box filled by using the
+ // DlgDirListComboBox function. The selection is interpreted as a drive letter, a file, or a directory name.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DlgDirSelectComboBoxEx(m_hWnd, lpString, nCount, nIDComboBox);
+ }
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ inline void CWnd::DragAcceptFiles(BOOL fAccept) const
+ // Registers whether a window accepts dropped files.
+ {
+ assert(::IsWindow(m_hWnd));
+ ::DragAcceptFiles(m_hWnd, fAccept);
+ }
+ #endif
+
+ inline BOOL CWnd::DrawAnimatedRects(int idAni, RECT& rcFrom, RECT& rcTo) const
+ // The DrawAnimatedRects function draws a wire-frame rectangle and animates it to indicate the opening of
+ // an icon or the minimizing or maximizing of a window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::DrawAnimatedRects(m_hWnd, idAni, &rcFrom, &rcTo);
+ }
+
+ inline BOOL CWnd::DrawCaption(CDC* pDC, RECT& rc, UINT uFlags) const
+ // The DrawCaption function draws a window caption.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pDC);
+ return ::DrawCaption(m_hWnd, pDC->GetHDC(), &rc, uFlags);
+ }
+
+ inline BOOL CWnd::EnableScrollBar(UINT uSBflags, UINT uArrows) const
+ // The EnableScrollBar function enables or disables one or both scroll bar arrows.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::EnableScrollBar(m_hWnd, uSBflags, uArrows);
+ }
+
+ inline CWnd* CWnd::GetLastActivePopup() const
+ // The GetLastActivePopup function determines which pop-up window owned by the specified window was most recently active.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::GetLastActivePopup(m_hWnd) );
+ }
+
+ inline CMenu* CWnd::GetMenu() const
+ // The GetMenu function retrieves a handle to the menu assigned to the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle(::GetMenu(m_hWnd));
+ }
+
+ inline int CWnd::GetScrollPos(int nBar) const
+ // The GetScrollPos function retrieves the current position of the scroll box
+ // (thumb) in the specified scroll bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetScrollPos(m_hWnd, nBar);
+ }
+
+ inline BOOL CWnd::GetScrollRange(int nBar, int& MinPos, int& MaxPos) const
+ // The GetScrollRange function retrieves the current minimum and maximum scroll box
+ // (thumb) positions for the specified scroll bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetScrollRange(m_hWnd, nBar, &MinPos, &MaxPos );
+ }
+
+ inline CMenu* CWnd::GetSystemMenu(BOOL bRevert) const
+ // The GetSystemMenu function allows the application to access the window menu (also known as the system menu
+ // or the control menu) for copying and modifying.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle(::GetSystemMenu(m_hWnd, bRevert));
+ }
+
+ inline CWnd* CWnd::GetTopWindow() const
+ // The GetTopWindow function examines the Z order of the child windows associated with the parent window and
+ // retrieves a handle to the child window at the top of the Z order.
+ {
+ assert(::IsWindow(m_hWnd));
+ return FromHandle( ::GetTopWindow(m_hWnd) );
+ }
+
+ inline BOOL CWnd::GetWindowPlacement(WINDOWPLACEMENT& wndpl) const
+ // The GetWindowPlacement function retrieves the show state and the restored,
+ // minimized, and maximized positions of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::GetWindowPlacement(m_hWnd, &wndpl);
+ }
+
+ inline BOOL CWnd::HiliteMenuItem(CMenu* pMenu, UINT uItemHilite, UINT uHilite) const
+ // The HiliteMenuItem function highlights or removes the highlighting from an item in a menu bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pMenu);
+ return ::HiliteMenuItem(m_hWnd, pMenu->GetHandle(), uItemHilite, uHilite);
+ }
+
+ inline BOOL CWnd::IsIconic() const
+ // The IsIconic function determines whether the window is minimized (iconic).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsIconic(m_hWnd);
+ }
+
+ inline BOOL CWnd::IsZoomed() const
+ // The IsZoomed function determines whether the window is maximized.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::IsZoomed(m_hWnd);
+ }
+
+ inline BOOL CWnd::KillTimer(UINT_PTR uIDEvent) const
+ // Destroys the specified timer.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::KillTimer(m_hWnd, uIDEvent);
+ }
+
+ inline BOOL CWnd::LockWindowUpdate() const
+ // Disables drawing in the window. Only one window can be locked at a time.
+ // Use UnLockWindowUpdate to re-enable drawing in the window
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::LockWindowUpdate(m_hWnd);
+ }
+
+ inline BOOL CWnd::OpenIcon() const
+ // The OpenIcon function restores a minimized (iconic) window to its previous size and position.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::OpenIcon(m_hWnd);
+ }
+
+ inline void CWnd::Print(CDC* pDC, DWORD dwFlags) const
+ // Requests that the window draw itself in the specified device context, most commonly in a printer device context.
+ {
+ assert(::IsWindow(m_hWnd));
+ assert(pDC);
+ SendMessage(m_hWnd, WM_PRINT, (WPARAM)pDC, (LPARAM)dwFlags);
+ }
+
+ inline BOOL CWnd::ScrollWindow(int XAmount, int YAmount, LPCRECT lprcScroll, LPCRECT lprcClip) const
+ // The ScrollWindow function scrolls the contents of the specified window's client area.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ScrollWindow(m_hWnd, XAmount, YAmount, lprcScroll, lprcClip);
+ }
+
+ inline int CWnd::ScrollWindowEx(int dx, int dy, LPCRECT lprcScroll, LPCRECT lprcClip, CRgn* prgnUpdate, LPRECT lprcUpdate, UINT flags) const
+ // The ScrollWindow function scrolls the contents of the window's client area.
+ {
+ assert(::IsWindow(m_hWnd));
+ HRGN hrgnUpdate = prgnUpdate? (HRGN)prgnUpdate->GetHandle() : NULL;
+ return ::ScrollWindowEx(m_hWnd, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate, flags);
+ }
+
+ inline BOOL CWnd::SetMenu(CMenu* pMenu) const
+ // The SetMenu function assigns a menu to the specified window.
+ // A hMenu of NULL removes the menu.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetMenu(m_hWnd, pMenu? pMenu->GetHandle() : NULL);
+ }
+
+ inline int CWnd::SetScrollInfo(int fnBar, const SCROLLINFO& si, BOOL fRedraw) const
+ // The SetScrollInfo function sets the parameters of a scroll bar, including
+ // the minimum and maximum scrolling positions, the page size, and the
+ // position of the scroll box (thumb).
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetScrollInfo(m_hWnd, fnBar, &si, fRedraw);
+ }
+
+ inline int CWnd::SetScrollPos(int nBar, int nPos, BOOL bRedraw) const
+ // The SetScrollPos function sets the position of the scroll box (thumb) in
+ // the specified scroll bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetScrollPos(m_hWnd, nBar, nPos, bRedraw);
+ }
+
+ inline BOOL CWnd::SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw) const
+ // The SetScrollRange function sets the minimum and maximum scroll box positions for the scroll bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetScrollRange(m_hWnd, nBar, nMinPos, nMaxPos, bRedraw);
+ }
+
+ inline BOOL CWnd::SetWindowPlacement(const WINDOWPLACEMENT& wndpl) const
+ // The SetWindowPlacement function sets the show state and the restored, minimized,
+ // and maximized positions of the window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::SetWindowPlacement(m_hWnd, &wndpl);
+ }
+
+ inline BOOL CWnd::ShowOwnedPopups(BOOL fShow) const
+ // The ShowOwnedPopups function shows or hides all pop-up windows owned by the specified window.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ShowOwnedPopups(m_hWnd, fShow);
+ }
+
+ inline BOOL CWnd::ShowScrollBar(int nBar, BOOL bShow) const
+ // The ShowScrollBar function shows or hides the specified scroll bar.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ShowScrollBar(m_hWnd, nBar, bShow);
+ }
+
+ inline BOOL CWnd::ShowWindowAsync(int nCmdShow) const
+ // The ShowWindowAsync function sets the show state of a window created by a different thread.
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::ShowWindowAsync(m_hWnd, nCmdShow);
+ }
+
+ inline BOOL CWnd::UnLockWindowUpdate() const
+ // Enables drawing in the window. Only one window can be locked at a time.
+ // Use LockWindowUpdate to disable drawing in the window
+ {
+ assert(::IsWindow(m_hWnd));
+ return ::LockWindowUpdate(0);
+ }
+
+ inline CWnd* CWnd::WindowFromDC(CDC* pDC) const
+ // The WindowFromDC function returns a handle to the window associated with the specified display device context (DC).
+ {
+ assert(pDC);
+ return FromHandle( ::WindowFromDC(pDC->GetHDC()) );
+ }
+
+ #endif
+
+}; // namespace Win32xx
+
+
+#endif // _WIN32XX_WINCORE_H_
+
diff --git a/mmc_updater/depends/win32cpp/winutils.h b/mmc_updater/depends/win32cpp/winutils.h
new file mode 100644
index 00000000..94ba2d80
--- /dev/null
+++ b/mmc_updater/depends/win32cpp/winutils.h
@@ -0,0 +1,649 @@
+// Win32++ Version 7.2
+// Released: 5th AUgust 2011
+//
+// David Nash
+// email: dnash@bigpond.net.au
+// url: https://sourceforge.net/projects/win32-framework
+//
+//
+// Copyright (c) 2005-2011 David Nash
+//
+// Permission is hereby granted, free of charge, to
+// any person obtaining a copy of this software and
+// associated documentation files (the "Software"),
+// to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+//
+////////////////////////////////////////////////////////
+
+#ifndef _WIN32XX_WINUTILS_H_
+#define _WIN32XX_WINUTILS_H_
+
+
+// define useful macros from WindowsX.h
+#ifndef GET_X_LPARAM
+ #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
+#endif
+#ifndef GET_Y_LPARAM
+ #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
+#endif
+
+// Define our own MIN and MAX macros
+// this avoids inconsistencies with Dev-C++ and other compilers, and
+// avoids conflicts between typical min/max macros and std::min/std::max
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+
+namespace Win32xx
+{
+ // Forward declarations
+ class CPoint;
+ class CRect;
+ CWinApp* GetApp();
+ void TRACE(LPCTSTR str);
+
+
+ /////////////////////////////////////////
+ // Definition of the CSize class
+ // This class can be used to replace the SIZE structure
+ class CSize : public SIZE
+ {
+ public:
+ CSize() { cx = 0; cy = 0; }
+ CSize(int CX, int CY) { cx = CX; cy = CY; }
+ CSize(SIZE sz) { cx = sz.cx; cy = sz.cy; }
+ CSize(POINT pt) { cx = pt.x; cy = pt.y; }
+ CSize(DWORD dw) { cx = (short)LOWORD(dw); cy = (short)HIWORD(dw); }
+ void SetSize(int CX, int CY) { cx = CX; cy = CY; }
+
+ // Operators
+ operator LPSIZE() { return this; }
+ BOOL operator == (SIZE sz) const { return (cx == sz.cx && cy == sz.cy); }
+ BOOL operator != (SIZE sz) const { return (cx != sz.cx || cy != sz.cy); }
+ void operator += (SIZE sz) { cx += sz.cx; cy += sz.cy; }
+ void operator -= (SIZE sz) { cx -= sz.cx; cy -= sz.cy; }
+
+ // Operators returning CSize
+ CSize operator - () const { return CSize (-cx, -cy); }
+ CSize operator + (SIZE sz) const { return CSize (cx + sz.cx, cy + sz.cy); }
+ CSize operator - (SIZE sz) const { return CSize (cx - sz.cx, cy - sz.cy); }
+
+ // Operators returning CPoint
+ CPoint operator + (POINT point) const;
+ CPoint operator - (POINT point) const;
+
+ // Operators returning CRect
+ CRect operator + (RECT rc) const;
+ CRect operator - (RECT rc) const;
+ };
+
+
+ /////////////////////////////////////////
+ // Definition of the CPoint class
+ // This class can be used to replace the POINT structure
+ class CPoint : public POINT
+ {
+ public:
+ CPoint() { x = 0; y = 0; }
+ CPoint(int X, int Y) { x = X; y = Y; }
+ CPoint(POINT pt) { x = pt.x ; y = pt.y; }
+ CPoint(POINTS pts) { x = pts.x; y = pts.y; }
+ CPoint(SIZE sz) { x = sz.cx; y = sz.cy; }
+ CPoint(DWORD dw) { x = (short) LOWORD(dw); y = (short) HIWORD(dw); }
+
+ void Offset(int dx, int dy) { x += dx; y += dy; }
+ void Offset(POINT pt) { x += pt.x; y += pt.y; }
+ void Offset(SIZE sz) { x += sz.cx; y += sz.cy; }
+ void SetPoint(int X, int Y) { x = X; y = Y; }
+
+ // Operators
+ operator LPPOINT() { return this; }
+ BOOL operator == (POINT pt) const { return ((x == pt.x) && (y == pt.y)); }
+ BOOL operator != (POINT pt) const { return ((x != pt.x) || (y != pt.y)); }
+ void operator += (SIZE sz) { x += sz.cx; y += sz.cy; }
+ void operator -= (SIZE sz) { x -= sz.cx; y -= sz.cy; }
+ void operator += (POINT pt) { x += pt.x; y += pt.y; }
+ void operator -= (POINT pt) { x -= pt.x; y -= pt.y; }
+
+ // Operators returning CPoint
+ CPoint operator - () const { return CPoint(-x, -y); }
+ CPoint operator + (SIZE sz) const { return CPoint(x + sz.cx, y + sz.cy); }
+ CPoint operator - (SIZE sz) const { return CPoint(x - sz.cx, y - sz.cy); }
+ CPoint operator + (POINT pt) const { return CPoint(x + pt.x, y + pt.y); }
+ CPoint operator - (POINT pt) const { return CPoint(x - pt.x, y - pt.y); }
+
+ // Operators returning CRect
+ CRect operator + (RECT rc) const;
+ CRect operator - (RECT rc) const;
+ };
+
+
+ /////////////////////////////////////////
+ // Definition of the CRect class
+ // This class can be used to replace the RECT structure.
+ class CRect : public RECT
+ {
+ public:
+ CRect() { left = top = right = bottom = 0; }
+ CRect(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; }
+ CRect(RECT rc) { left = rc.left; top = rc.top; right = rc.right; bottom = rc.bottom; }
+ CRect(POINT pt, SIZE sz) { right = (left = pt.x) + sz.cx; bottom = (top = pt.y) + sz.cy; }
+ CRect(POINT topLeft, POINT bottomRight) { left = topLeft.x; top = topLeft.y; right = bottomRight.x; bottom = bottomRight.y; }
+
+ BOOL CopyRect(RECT rc) { return ::CopyRect(this, &rc); }
+ BOOL DeflateRect(int x, int y) { return ::InflateRect(this, -x, -y); }
+ BOOL DeflateRect(SIZE size) { return ::InflateRect(this, -size.cx, -size.cy); }
+ BOOL DeflateRect(RECT rc) { return ::InflateRect(this, rc.left - rc.right, rc.top - rc.bottom); }
+ BOOL DeflateRect(int l, int t, int r, int b){ return ::InflateRect(this, l - r, t - b); }
+ BOOL EqualRect(RECT rc) const { return ::EqualRect(&rc, this); }
+ BOOL InflateRect(int dx, int dy) { return ::InflateRect(this, dx, dy); }
+ BOOL InflateRect(SIZE sz) { return ::InflateRect(this, sz.cx, sz.cy); }
+ BOOL InflateRect(RECT rc) { return ::InflateRect(this, rc.right - rc.left, rc.bottom - rc.top); }
+ BOOL InflateRect(int l, int t, int r, int b){ return ::InflateRect(this, r - l, b - t); }
+ BOOL IntersectRect(RECT rc1, RECT rc2) { return ::IntersectRect(this, &rc1, &rc2); }
+ BOOL IsRectEmpty() const { return ::IsRectEmpty(this);}
+ BOOL IsRectNull() const { return (left == 0 && right == 0 && top == 0 && bottom == 0); }
+ CRect MulDiv(int nMult, int nDiv) const { return CRect ((left * nMult) / nDiv, (top * nMult) / nDiv,
+ (right * nMult) / nDiv, (bottom * nMult) / nDiv); }
+ void NormalizeRect() { int nTemp; if (left > right) { nTemp = left; left = right; right = nTemp; }
+ if (top > bottom) { nTemp = top; top = bottom; bottom = nTemp; } }
+ BOOL OffsetRect(int dx, int dy) { return ::OffsetRect(this, dx, dy); }
+ BOOL OffsetRect(POINT pt) { return ::OffsetRect(this, pt.x, pt.y); }
+ BOOL OffsetRect(SIZE size) { return ::OffsetRect(this, size.cx, size.cy); }
+ BOOL PtInRect(POINT pt) const { return ::PtInRect(this, pt); }
+ BOOL SetRect(int l, int t, int r, int b) { return ::SetRect(this, l, t, r, b); }
+ BOOL SetRect(POINT TopLeft, POINT BtmRight) { return ::SetRect(this, TopLeft.x, TopLeft.y, BtmRight.x, BtmRight.y); }
+ BOOL SetRectEmpty() { return ::SetRectEmpty(this); }
+ BOOL SubtractRect(RECT rc1, RECT rc2) { return ::SubtractRect(this, &rc1, &rc2); }
+ BOOL UnionRect(RECT rc1, RECT rc2) { return ::UnionRect(this, &rc1, &rc2); }
+
+ // Reposition rectangle
+ void MoveToX (int x) { right = Width() + x; left = x; }
+ void MoveToY (int y) { bottom = Height() + y; top = y; }
+ void MoveToXY (int x, int y) { MoveToX(x); MoveToY(y); }
+ void MoveToXY (POINT pt) { MoveToX (pt.x); MoveToY (pt.y); }
+
+ // Attributes
+ int Height() const { return bottom - top; }
+ int Width() const { return right - left; }
+ CSize Size() const { return CSize(Width(), Height()); }
+ CPoint CenterPoint() const { return CPoint((left + right) / 2, (top + bottom) / 2); }
+ CPoint TopLeft() const { return CPoint(left, top); }
+ CPoint BottomRight() const { return CPoint(right, bottom); }
+
+ // operators
+ operator LPRECT() { return this; }
+ BOOL operator == (RECT rc) const { return ::EqualRect(this, &rc); }
+ BOOL operator != (RECT rc) const { return !::EqualRect(this, &rc); }
+ void operator += (POINT pt) { ::OffsetRect(this, pt.x, pt.y); }
+ void operator += (SIZE size) { ::OffsetRect(this, size.cx, size.cy); }
+ void operator += (RECT rc) { ::InflateRect(this, rc.right - rc.left, rc.bottom - rc.top); }
+ void operator -= (RECT rc) { ::InflateRect(this, rc.left - rc.right, rc.top - rc.bottom); }
+ void operator -= (POINT pt) { ::OffsetRect(this, -pt.x, -pt.y); }
+ void operator -= (SIZE sz) { ::OffsetRect(this, -sz.cx, -sz.cy); }
+ void operator &= (RECT rc) { ::IntersectRect(this, this, &rc); }
+ void operator |= (RECT rc) { ::UnionRect(this, this, &rc); }
+
+ // Operators returning CRect
+ CRect operator + (POINT pt) const { CRect rc(*this); ::OffsetRect(&rc, pt.x, pt.y); return rc; }
+ CRect operator - (POINT pt) const { CRect rc(*this); ::OffsetRect(&rc, -pt.x, -pt.y); return rc; }
+ CRect operator + (SIZE sz) const { CRect rc(*this); ::OffsetRect(&rc, sz.cx, sz.cy); return rc; }
+ CRect operator - (SIZE sz) const { CRect rc(*this); ::OffsetRect(&rc, -sz.cx, -sz.cy); return rc; }
+ CRect operator + (RECT rc) const { CRect rc1(*this); rc1.InflateRect(rc); return rc1; }
+ CRect operator - (RECT rc) const { CRect rc1(*this); rc1.DeflateRect(rc); return rc1; }
+ CRect operator & (RECT rc) const { CRect rc1; ::IntersectRect(&rc1, this, &rc); return rc1; }
+ CRect operator | (RECT rc) const { CRect rc1; ::UnionRect(&rc1, this, &rc); return rc1; }
+ };
+
+ // CSize member function definitions
+ inline CPoint CSize::operator + (POINT pt) const { return CPoint(pt) + *this; }
+ inline CPoint CSize::operator - (POINT pt) const { return CPoint(pt) - *this; }
+ inline CRect CSize::operator + (RECT rc) const { return CRect(rc) + *this; }
+ inline CRect CSize::operator - (RECT rc) const { return CRect(rc) - *this; }
+
+ // CPoint member function definitions
+ inline CRect CPoint::operator + (RECT rc) const { return CRect(rc) + *this; }
+ inline CRect CPoint::operator - (RECT rc) const { return CRect(rc) - *this; }
+
+
+ ////////////////////////////////////////////////////////
+ // Classes and functions (typedefs) for text conversions
+ //
+ // This section defines the following text conversions:
+ // A2BSTR ANSI to BSTR
+ // A2OLE ANSI to OLE
+ // A2T ANSI to TCHAR
+ // A2W ANSI to WCHAR
+ // OLE2A OLE to ANSI
+ // OLE2T OLE to TCHAR
+ // OLE2W OLE to WCHAR
+ // T2A TCHAR to ANSI
+ // T2BSTR TCHAR to BSTR
+ // T2OLE TCHAR to OLE
+ // T2W TCHAR to WCHAR
+ // W2A WCHAR to ANSI
+ // W2BSTR WCHAR to BSTR
+ // W2OLE WCHAR to OLE
+ // W2T WCHAR to TCHAR
+
+ // About different character and string types:
+ // ------------------------------------------
+ // char (or CHAR) character types are ANSI (8 bits).
+ // wchar_t (or WCHAR) character types are Unicode (16 bits).
+ // TCHAR characters are Unicode if the _UNICODE macro is defined, otherwise they are ANSI.
+ // BSTR (Basic String) is a type of string used in Visual Basic and COM programming.
+ // OLE is the same as WCHAR. It is used in Visual Basic and COM programming.
+
+
+ // Forward declarations of our classes. They are defined later.
+ class CA2A;
+ class CA2W;
+ class CW2A;
+ class CW2W;
+ class CA2BSTR;
+ class CW2BSTR;
+
+ // typedefs for the well known text conversions
+ typedef CA2W A2W;
+ typedef CW2A W2A;
+ typedef CW2BSTR W2BSTR;
+ typedef CA2BSTR A2BSTR;
+ typedef CW2A BSTR2A;
+ typedef CW2W BSTR2W;
+
+#ifdef _UNICODE
+ typedef CA2W A2T;
+ typedef CW2A T2A;
+ typedef CW2W T2W;
+ typedef CW2W W2T;
+ typedef CW2BSTR T2BSTR;
+ typedef BSTR2W BSTR2T;
+#else
+ typedef CA2A A2T;
+ typedef CA2A T2A;
+ typedef CA2W T2W;
+ typedef CW2A W2T;
+ typedef CA2BSTR T2BSTR;
+ typedef BSTR2A BSTR2T;
+#endif
+
+ typedef A2W A2OLE;
+ typedef T2W T2OLE;
+ typedef CW2W W2OLE;
+ typedef W2A OLE2A;
+ typedef W2T OLE2T;
+ typedef CW2W OLE2W;
+
+ class CA2W
+ {
+ public:
+ CA2W(LPCSTR pStr) : m_pStr(pStr)
+ {
+ if (pStr)
+ {
+ // Resize the vector and assign null WCHAR to each element
+ int length = (int)strlen(pStr)+1;
+ m_vWideArray.assign(length, L'\0');
+
+ // Fill our vector with the converted WCHAR array
+ MultiByteToWideChar(CP_ACP, 0, pStr, -1, &m_vWideArray[0], length);
+ }
+ }
+ ~CA2W() {}
+ operator LPCWSTR() { return m_pStr? &m_vWideArray[0] : NULL; }
+ operator LPOLESTR() { return m_pStr? (LPOLESTR)&m_vWideArray[0] : (LPOLESTR)NULL; }
+ operator LPBSTR() { return m_pStr? (LPBSTR)&m_vWideArray[0] : (LPBSTR)NULL; }
+
+ private:
+ CA2W(const CA2W&);
+ CA2W& operator= (const CA2W&);
+ std::vector<wchar_t> m_vWideArray;
+ LPCSTR m_pStr;
+ };
+
+ class CW2A
+ {
+ public:
+ CW2A(LPCWSTR pWStr) : m_pWStr(pWStr)
+ {
+ // Resize the vector and assign null char to each element
+ int length = (int)wcslen(pWStr)+1;
+ m_vAnsiArray.assign(length, '\0');
+
+ // Fill our vector with the converted char array
+ WideCharToMultiByte(CP_ACP, 0, pWStr, -1, &m_vAnsiArray[0], length, NULL,NULL);
+ }
+
+ ~CW2A() {}
+ operator LPCSTR() { return m_pWStr? &m_vAnsiArray[0] : NULL; }
+
+ private:
+ CW2A(const CW2A&);
+ CW2A& operator= (const CW2A&);
+ std::vector<char> m_vAnsiArray;
+ LPCWSTR m_pWStr;
+ };
+
+ class CW2W
+ {
+ public:
+ CW2W(LPCWSTR pWStr) : m_pWStr(pWStr) {}
+ operator LPCWSTR() { return (LPWSTR)m_pWStr; }
+ operator LPOLESTR() { return (LPOLESTR)m_pWStr; }
+
+ private:
+ CW2W(const CW2W&);
+ CW2W& operator= (const CW2W&);
+
+ LPCWSTR m_pWStr;
+ };
+
+ class CA2A
+ {
+ public:
+ CA2A(LPCSTR pStr) : m_pStr(pStr) {}
+ operator LPCSTR() { return (LPSTR)m_pStr; }
+
+ private:
+ CA2A(const CA2A&);
+ CA2A& operator= (const CA2A&);
+
+ LPCSTR m_pStr;
+ };
+
+ class CW2BSTR
+ {
+ public:
+ CW2BSTR(LPCWSTR pWStr) { m_bstrString = ::SysAllocString(pWStr); }
+ ~CW2BSTR() { ::SysFreeString(m_bstrString); }
+ operator BSTR() { return m_bstrString;}
+
+ private:
+ CW2BSTR(const CW2BSTR&);
+ CW2BSTR& operator= (const CW2BSTR&);
+ BSTR m_bstrString;
+ };
+
+ class CA2BSTR
+ {
+ public:
+ CA2BSTR(LPCSTR pStr) { m_bstrString = ::SysAllocString(A2W(pStr)); }
+ ~CA2BSTR() { ::SysFreeString(m_bstrString); }
+ operator BSTR() { return m_bstrString;}
+
+ private:
+ CA2BSTR(const CA2BSTR&);
+ CA2BSTR& operator= (const CA2BSTR&);
+ BSTR m_bstrString;
+ };
+
+
+ ////////////////////////////////////////
+ // Global Functions
+ //
+
+ inline CWnd* FromHandle(HWND hWnd)
+ // Returns the CWnd object associated with the window handle
+ {
+ assert( GetApp() );
+ CWnd* pWnd = GetApp()->GetCWndFromMap(hWnd);
+ if (::IsWindow(hWnd) && pWnd == 0)
+ {
+ GetApp()->AddTmpWnd(hWnd);
+ pWnd = GetApp()->GetCWndFromMap(hWnd);
+ ::PostMessage(hWnd, UWM_CLEANUPTEMPS, 0, 0);
+ }
+
+ return pWnd;
+ }
+
+
+ inline CWinApp* GetApp()
+ // Returns a pointer to the CWinApp derrived class
+ {
+ return CWinApp::SetnGetThis();
+ }
+
+ inline CPoint GetCursorPos()
+ {
+ CPoint pt;
+ ::GetCursorPos(&pt);
+ return pt;
+ }
+
+ inline HBITMAP LoadBitmap (LPCTSTR lpszName)
+ {
+ assert(GetApp());
+
+ HBITMAP hBitmap = (HBITMAP)::LoadImage (GetApp()->GetResourceHandle(), lpszName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
+ return hBitmap;
+ }
+
+ inline HBITMAP LoadBitmap (int nID)
+ {
+ return LoadBitmap(MAKEINTRESOURCE(nID));
+ }
+
+
+ inline void TRACE(LPCTSTR str)
+ // TRACE sends a string to the debug/output pane, or an external debugger
+ {
+ #ifdef _DEBUG
+ OutputDebugString(str);
+ #else
+ UNREFERENCED_PARAMETER(str); // no-op
+ #endif
+ }
+
+ #ifndef _WIN32_WCE // for Win32/64 operating systems, not WinCE
+
+ inline int GetWinVersion()
+ {
+ DWORD dwVersion = GetVersion();
+ int Platform = (dwVersion < 0x80000000)? 2:1;
+ int MajorVer = LOBYTE(LOWORD(dwVersion));
+ int MinorVer = HIBYTE(LOWORD(dwVersion));
+
+ int nVersion = 1000*Platform + 100*MajorVer + MinorVer;
+
+ // Return values and window versions:
+ // 1400 Windows 95
+ // 1410 Windows 98
+ // 1490 Windows ME
+ // 2400 Windows NT
+ // 2500 Windows 2000
+ // 2501 Windows XP
+ // 2502 Windows Server 2003
+ // 2600 Windows Vista and Windows Server 2008
+ // 2601 Windows 7
+
+ return nVersion;
+ }
+
+ inline int GetComCtlVersion()
+ {
+ // Load the Common Controls DLL
+ HMODULE hComCtl = ::LoadLibraryA("COMCTL32.DLL");
+ if (!hComCtl)
+ return 0;
+
+ int ComCtlVer = 400;
+
+ if (::GetProcAddress(hComCtl, "InitCommonControlsEx"))
+ {
+ // InitCommonControlsEx is unique to 4.7 and later
+ ComCtlVer = 470;
+
+ if (::GetProcAddress(hComCtl, "DllGetVersion"))
+ {
+ typedef HRESULT CALLBACK DLLGETVERSION(DLLVERSIONINFO*);
+ DLLGETVERSION* pfnDLLGetVersion = NULL;
+
+ pfnDLLGetVersion = (DLLGETVERSION*)::GetProcAddress(hComCtl, "DllGetVersion");
+ if(pfnDLLGetVersion)
+ {
+ DLLVERSIONINFO dvi;
+ dvi.cbSize = sizeof dvi;
+ if(NOERROR == pfnDLLGetVersion(&dvi))
+ {
+ DWORD dwVerMajor = dvi.dwMajorVersion;
+ DWORD dwVerMinor = dvi.dwMinorVersion;
+ ComCtlVer = 100 * dwVerMajor + dwVerMinor;
+ }
+ }
+ }
+ else if (::GetProcAddress(hComCtl, "InitializeFlatSB"))
+ ComCtlVer = 471; // InitializeFlatSB is unique to version 4.71
+ }
+
+ ::FreeLibrary(hComCtl);
+
+ // return values and DLL versions
+ // 400 dll ver 4.00 Windows 95/Windows NT 4.0
+ // 470 dll ver 4.70 Internet Explorer 3.x
+ // 471 dll ver 4.71 Internet Explorer 4.0
+ // 472 dll ver 4.72 Internet Explorer 4.01 and Windows 98
+ // 580 dll ver 5.80 Internet Explorer 5
+ // 581 dll ver 5.81 Windows 2000 and Windows ME
+ // 582 dll ver 5.82 Windows XP or Vista without XP themes
+ // 600 dll ver 6.00 Windows XP with XP themes
+ // 610 dll ver 6.10 Windows Vista with XP themes
+ // 616 dll ver 6.16 Windows Vista SP1 or Windows 7 with XP themes
+
+ return ComCtlVer;
+ }
+
+ inline UINT GetSizeofMenuItemInfo()
+ {
+ UINT uSize = sizeof(MENUITEMINFO);
+ // For Win95 and NT, cbSize needs to be 44
+ if (1400 == (GetWinVersion()) || (2400 == GetWinVersion()))
+ uSize = 44;
+
+ return uSize;
+ }
+
+ inline UINT GetSizeofNonClientMetrics()
+ {
+ // This function correctly determines the sizeof NONCLIENTMETRICS
+ UINT uSize = sizeof (NONCLIENTMETRICS);
+
+ #if (WINVER >= 0x0600)
+ if (GetWinVersion() < 2600 && (uSize > 500)) // Is OS version less than Vista
+ uSize -= sizeof(int); // Adjust size back to correct value
+ #endif
+
+ return uSize;
+ }
+
+
+
+ // A global function to report the state of the left mouse button
+ inline BOOL IsLeftButtonDown()
+ {
+ SHORT state;
+ if (GetSystemMetrics(SM_SWAPBUTTON))
+ // Mouse buttons are swapped
+ state = GetAsyncKeyState(VK_RBUTTON);
+ else
+ // Mouse buttons are not swapped
+ state = GetAsyncKeyState(VK_LBUTTON);
+
+ // returns true if the left mouse button is down
+ return (state & 0x8000);
+ }
+
+ inline BOOL IsAeroThemed()
+ {
+ BOOL bIsAeroThemed = FALSE;
+
+ // Test if Windows version is XP or greater
+ if (GetWinVersion() >= 2501)
+ {
+ HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll"));
+ if(hMod)
+ {
+ // Declare pointers to IsCompositionActive function
+ FARPROC pIsCompositionActive = ::GetProcAddress(hMod, "IsCompositionActive");
+
+ if(pIsCompositionActive)
+ {
+ if(pIsCompositionActive())
+ {
+ bIsAeroThemed = TRUE;
+ }
+ }
+ ::FreeLibrary(hMod);
+ }
+ }
+
+ return bIsAeroThemed;
+ }
+
+ inline BOOL IsXPThemed()
+ {
+ BOOL bIsXPThemed = FALSE;
+
+ // Test if Windows version is XP or greater
+ if (GetWinVersion() >= 2501)
+ {
+ HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll"));
+ if(hMod)
+ {
+ // Declare pointers to functions
+ FARPROC pIsAppThemed = ::GetProcAddress(hMod, "IsAppThemed");
+ FARPROC pIsThemeActive = ::GetProcAddress(hMod, "IsThemeActive");
+
+ if(pIsAppThemed && pIsThemeActive)
+ {
+ if(pIsAppThemed() && pIsThemeActive())
+ {
+ // Test if ComCtl32 dll used is version 6 or later
+ bIsXPThemed = (GetComCtlVersion() >= 600);
+ }
+ }
+ ::FreeLibrary(hMod);
+ }
+ }
+
+ return bIsXPThemed;
+ }
+
+ #endif // #ifndef _WIN32_WCE
+
+ // Required for WinCE
+ #ifndef lstrcpyn
+ inline LPTSTR lstrcpyn(LPTSTR lpstrDest, LPCTSTR lpstrSrc, int nLength)
+ {
+ if(NULL == lpstrDest || NULL == lpstrSrc || nLength <= 0)
+ return NULL;
+ int nLen = MIN((int)lstrlen(lpstrSrc), nLength - 1);
+ LPTSTR lpstrRet = (LPTSTR)memcpy(lpstrDest, lpstrSrc, nLen * sizeof(TCHAR));
+ lpstrDest[nLen] = _T('\0');
+ return lpstrRet;
+ }
+ #endif // !lstrcpyn
+
+}
+
+
+#endif // _WIN32XX_WINUTILS_H_
diff --git a/mmc_updater/src/AppInfo.cpp b/mmc_updater/src/AppInfo.cpp
new file mode 100644
index 00000000..a5a9bb63
--- /dev/null
+++ b/mmc_updater/src/AppInfo.cpp
@@ -0,0 +1,23 @@
+#include "AppInfo.h"
+
+#include "FileUtils.h"
+#include "Platform.h"
+#include "StringUtils.h"
+#include "StandardDirs.h"
+
+#include <iostream>
+
+std::string AppInfo::logFilePath()
+{
+ return StandardDirs::appDataPath(organizationName(),appName()) + '/' + "update-log.txt";
+}
+
+std::string AppInfo::updateErrorMessage(const std::string& details)
+{
+ std::string result = "There was a problem installing the update:\n\n";
+ result += details;
+ result += "\n\nYou can try downloading and installing the latest version of "
+ "MultiMC from http://multimc.org/";
+ return result;
+}
+
diff --git a/mmc_updater/src/AppInfo.h b/mmc_updater/src/AppInfo.h
new file mode 100644
index 00000000..51d95886
--- /dev/null
+++ b/mmc_updater/src/AppInfo.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <string>
+
+/** This class provides project-specific updater properties,
+ * such as the name of the application being updated and
+ * the path to log details of the update install to.
+ */
+class AppInfo
+{
+ public:
+ // Basic application information
+ static std::string name();
+ static std::string appName();
+ static std::string organizationName();
+
+ static std::string logFilePath();
+
+ /** Returns a message to display to the user in the event
+ * of a problem installing the update.
+ */
+ static std::string updateErrorMessage(const std::string& details);
+};
+
+inline std::string AppInfo::name()
+{
+ return "MultiMC Updater";
+}
+
+inline std::string AppInfo::appName()
+{
+ return "MultiMC";
+}
+
+inline std::string AppInfo::organizationName()
+{
+ return "MultiMC Contributors";
+}
+
diff --git a/mmc_updater/src/CMakeLists.txt b/mmc_updater/src/CMakeLists.txt
new file mode 100644
index 00000000..d1a32755
--- /dev/null
+++ b/mmc_updater/src/CMakeLists.txt
@@ -0,0 +1,121 @@
+
+add_subdirectory(tests)
+
+find_package(Threads REQUIRED)
+include(GenerateCppResourceFile)
+
+set (UPDATER_SOURCES
+ AppInfo.cpp
+ AppInfo.h
+ DirIterator.cpp
+ DirIterator.h
+ FileUtils.cpp
+ FileUtils.h
+ Log.cpp
+ Log.h
+ ProcessUtils.cpp
+ ProcessUtils.h
+ StandardDirs.cpp
+ StandardDirs.h
+ UpdateDialog.cpp
+ UpdateInstaller.cpp
+ UpdateInstaller.h
+ UpdateScript.cpp
+ UpdateScript.h
+ UpdaterOptions.cpp
+ UpdaterOptions.h
+)
+
+add_definitions(-DTIXML_USE_STL)
+
+if (WIN32)
+ set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogWin32.cpp UpdateDialogWin32.h)
+endif()
+
+if (UNIX)
+ set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogAscii.cpp UpdateDialogAscii.h)
+ add_definitions(-Wall -Wconversion)
+if (APPLE)
+ set(MAC_DOCK_ICON_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_dock_icon.cpp)
+ set(MAC_INFO_PLIST_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_info_plist.cpp)
+ generate_cpp_resource_file(resource_macdockicon ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac.icns ${MAC_DOCK_ICON_CPP_FILE})
+ generate_cpp_resource_file(resource_macplist ${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist ${MAC_INFO_PLIST_FILE})
+ set(UPDATER_SOURCES ${UPDATER_SOURCES}
+ MacBundle.h
+ MacBundle.cpp
+ StandardDirs.mm
+ StlSymbolsLeopard.cpp
+ UpdateDialogCocoa.mm
+ UpdateDialogCocoa.h
+ mac_dock_icon.cpp
+ mac_info_plist.cpp
+ )
+else() # linuxes and other similar systems
+ find_package(GTK2 REQUIRED gtk)
+ include_directories(${GTK2_INCLUDE_DIRS})
+ add_library(updatergtk SHARED UpdateDialogGtk.cpp UpdateDialogGtk.h)
+ target_link_libraries(updatergtk ${GTK2_LIBRARIES})
+
+ # embed the GTK helper library into the updater binary.
+ # At runtime it will be extracted and loaded if the
+ # GTK libraries are available
+ get_property(GTK_UPDATER_LIB TARGET updatergtk PROPERTY LOCATION)
+ set(GTK_BIN_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/libupdatergtk.cpp)
+ generate_cpp_resource_file(resource_updatergtk ${GTK_UPDATER_LIB} ${GTK_BIN_CPP_FILE})
+ add_dependencies(resource_updatergtk updatergtk)
+
+ set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogGtkFactory.cpp UpdateDialogGtkFactory.h ${GTK_BIN_CPP_FILE})
+endif()
+endif()
+
+add_library(updatershared STATIC ${UPDATER_SOURCES})
+
+target_link_libraries(updatershared
+ anyoption
+ tinyxml
+)
+
+if (UNIX)
+ if (APPLE)
+ find_library(COCOA_LIBRARY Cocoa)
+ find_library(SECURITY_LIBRARY Security)
+ target_link_libraries(updatershared ${SECURITY_LIBRARY} ${COCOA_LIBRARY})
+ else()
+ add_dependencies(updatershared resource_updatergtk)
+ endif()
+ target_link_libraries(updatershared pthread dl)
+endif()
+
+if (WIN32)
+ set(EXE_FLAGS WIN32 resources/updater.rc)
+endif()
+
+add_executable(updater ${EXE_FLAGS} main.cpp)
+
+target_link_libraries(updater
+ updatershared
+)
+
+
+#### Updater Executable ####
+IF(WIN32)
+INSTALL(TARGETS updater
+ BUNDLE DESTINATION . COMPONENT Runtime
+ LIBRARY DESTINATION . COMPONENT Runtime
+ RUNTIME DESTINATION . COMPONENT Runtime
+)
+ENDIF()
+IF(UNIX)
+IF(APPLE)
+INSTALL(TARGETS updater
+ BUNDLE DESTINATION . COMPONENT Runtime
+ RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime
+)
+ELSE()
+INSTALL(TARGETS updater
+ BUNDLE DESTINATION . COMPONENT Runtime
+ RUNTIME DESTINATION bin COMPONENT Runtime
+)
+ENDIF()
+ENDIF()
+
diff --git a/mmc_updater/src/DirIterator.cpp b/mmc_updater/src/DirIterator.cpp
new file mode 100644
index 00000000..a4604f05
--- /dev/null
+++ b/mmc_updater/src/DirIterator.cpp
@@ -0,0 +1,85 @@
+#include "DirIterator.h"
+
+#include "Log.h"
+#include "StringUtils.h"
+
+#ifdef PLATFORM_UNIX
+ #include <dirent.h>
+#endif
+
+#include <string.h>
+
+DirIterator::DirIterator(const char* path)
+{
+ m_path = path;
+
+#ifdef PLATFORM_UNIX
+ m_dir = opendir(path);
+ m_entry = 0;
+#else
+ // to list the contents of a directory, the first
+ // argument to FindFirstFile needs to be a wildcard
+ // of the form: C:\path\to\dir\*
+ std::string searchPath = m_path;
+ if (!endsWith(searchPath,"/"))
+ {
+ searchPath.append("/");
+ }
+ searchPath.append("*");
+ m_findHandle = FindFirstFile(searchPath.c_str(),&m_findData);
+ m_firstEntry = true;
+#endif
+}
+
+DirIterator::~DirIterator()
+{
+#ifdef PLATFORM_UNIX
+ closedir(m_dir);
+#else
+ FindClose(m_findHandle);
+#endif
+}
+
+bool DirIterator::next()
+{
+#ifdef PLATFORM_UNIX
+ m_entry = readdir(m_dir);
+ return m_entry != 0;
+#else
+ bool result;
+ if (m_firstEntry)
+ {
+ m_firstEntry = false;
+ return m_findHandle != INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ result = FindNextFile(m_findHandle,&m_findData);
+ }
+ return result;
+#endif
+}
+
+std::string DirIterator::fileName() const
+{
+#ifdef PLATFORM_UNIX
+ return m_entry->d_name;
+#else
+ return m_findData.cFileName;
+#endif
+}
+
+std::string DirIterator::filePath() const
+{
+ return m_path + '/' + fileName();
+}
+
+bool DirIterator::isDir() const
+{
+#ifdef PLATFORM_UNIX
+ return m_entry->d_type == DT_DIR;
+#else
+ return (m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+#endif
+}
+
diff --git a/mmc_updater/src/DirIterator.h b/mmc_updater/src/DirIterator.h
new file mode 100644
index 00000000..f3fbb955
--- /dev/null
+++ b/mmc_updater/src/DirIterator.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "Platform.h"
+
+#include <string>
+
+#ifdef PLATFORM_UNIX
+#include <dirent.h>
+#endif
+
+/** Simple class for iterating over the files in a directory
+ * and reporting their names and types.
+ */
+class DirIterator
+{
+ public:
+ DirIterator(const char* path);
+ ~DirIterator();
+
+ // iterate to the next entry in the directory
+ bool next();
+
+ // methods to return information about
+ // the current entry
+ std::string fileName() const;
+ std::string filePath() const;
+ bool isDir() const;
+
+ private:
+ std::string m_path;
+
+#ifdef PLATFORM_UNIX
+ DIR* m_dir;
+ dirent* m_entry;
+#endif
+
+#ifdef PLATFORM_WINDOWS
+ HANDLE m_findHandle;
+ WIN32_FIND_DATA m_findData;
+ bool m_firstEntry;
+#endif
+};
+
diff --git a/mmc_updater/src/FileUtils.cpp b/mmc_updater/src/FileUtils.cpp
new file mode 100644
index 00000000..712c0c5d
--- /dev/null
+++ b/mmc_updater/src/FileUtils.cpp
@@ -0,0 +1,517 @@
+#include "FileUtils.h"
+
+#include "DirIterator.h"
+#include "Log.h"
+#include "Platform.h"
+#include "StringUtils.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <string.h>
+#include <fstream>
+#include <iostream>
+// this actually works with mingw32, which we use.
+#include <libgen.h>
+
+#ifdef PLATFORM_UNIX
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#endif
+
+FileUtils::IOException::IOException(const std::string& error)
+{
+ init(errno,error);
+}
+
+FileUtils::IOException::IOException(int errorCode, const std::string& error)
+{
+ init(errorCode,error);
+}
+
+void FileUtils::IOException::init(int errorCode, const std::string& error)
+{
+ m_error = error;
+
+#ifdef PLATFORM_UNIX
+ m_errorCode = errorCode;
+
+ if (m_errorCode > 0)
+ {
+ m_error += " details: " + std::string(strerror(m_errorCode));
+ }
+#endif
+
+#ifdef PLATFORM_WINDOWS
+ m_errorCode = 0;
+ m_error += " GetLastError returned: " + intToStr(GetLastError());
+#endif
+}
+
+FileUtils::IOException::~IOException() throw ()
+{
+}
+
+FileUtils::IOException::Type FileUtils::IOException::type() const
+{
+#ifdef PLATFORM_UNIX
+ switch (m_errorCode)
+ {
+ case 0:
+ return NoError;
+ case EROFS:
+ return ReadOnlyFileSystem;
+ case ENOSPC:
+ return DiskFull;
+ default:
+ return Unknown;
+ }
+#else
+ return Unknown;
+#endif
+}
+
+bool FileUtils::fileExists(const char* path) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ struct stat fileInfo;
+ if (lstat(path,&fileInfo) != 0)
+ {
+ if (errno == ENOENT)
+ {
+ return false;
+ }
+ else
+ {
+ throw IOException("Error checking for file " + std::string(path));
+ }
+ }
+ return true;
+#else
+ DWORD result = GetFileAttributes(path);
+ if (result == INVALID_FILE_ATTRIBUTES)
+ {
+ return false;
+ }
+ return true;
+#endif
+}
+
+int FileUtils::fileMode(const char* path) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ struct stat fileInfo;
+ if (stat(path,&fileInfo) != 0)
+ {
+ throw IOException("Error reading file permissions for " + std::string(path));
+ }
+ return fileInfo.st_mode;
+#else
+ // not implemented for Windows
+ return 0;
+#endif
+}
+
+void FileUtils::chmod(const char* path, int mode) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (::chmod(path,static_cast<mode_t>(mode)) != 0)
+ {
+ throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(mode));
+ }
+#else
+ // TODO - Not implemented under Windows - all files
+ // get default permissions
+#endif
+}
+
+void FileUtils::moveFile(const char* src, const char* dest) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (rename(src,dest) != 0)
+ {
+ throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest));
+ }
+#else
+ if (!MoveFile(src,dest))
+ {
+ throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest));
+ }
+#endif
+}
+
+void FileUtils::mkpath(const char* dir) throw (IOException)
+{
+ std::string currentPath;
+ std::istringstream stream(dir);
+ while (!stream.eof())
+ {
+ std::string segment;
+ std::getline(stream,segment,'/');
+ currentPath += segment;
+ if (!currentPath.empty() && !fileExists(currentPath.c_str()))
+ {
+ mkdir(currentPath.c_str());
+ }
+ currentPath += '/';
+ }
+}
+
+void FileUtils::mkdir(const char* dir) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (::mkdir(dir,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
+ {
+ throw IOException("Unable to create directory " + std::string(dir));
+ }
+#else
+ if (!CreateDirectory(dir,0 /* default security attributes */))
+ {
+ throw IOException("Unable to create directory " + std::string(dir));
+ }
+#endif
+}
+
+void FileUtils::rmdir(const char* dir) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (::rmdir(dir) != 0)
+ {
+ throw IOException("Unable to remove directory " + std::string(dir));
+ }
+#else
+ if (!RemoveDirectory(dir))
+ {
+ throw IOException("Unable to remove directory " + std::string(dir));
+ }
+#endif
+}
+
+void FileUtils::createSymLink(const char* link, const char* target) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (symlink(target,link) != 0)
+ {
+ throw IOException("Unable to create symlink " + std::string(link) + " to " + std::string(target));
+ }
+#else
+ // symlinks are not supported under Windows (at least, not universally.
+ // Windows Vista and later do actually support symlinks)
+ LOG(Warn,"Skipping symlink creation - not implemented in Windows");
+#endif
+}
+
+void FileUtils::removeFile(const char* src) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (unlink(src) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ throw IOException("Unable to remove file " + std::string(src));
+ }
+ }
+#else
+ if (!DeleteFile(src))
+ {
+ if (GetLastError() == ERROR_ACCESS_DENIED)
+ {
+ // if another process is using the file, try moving it to
+ // a temporary directory and then
+ // scheduling it for deletion on reboot
+ std::string tempDeletePathBase = tempPath();
+ tempDeletePathBase += '/';
+ tempDeletePathBase += fileName(src);
+
+ int suffix = 0;
+ std::string tempDeletePath = tempDeletePathBase;
+ while (fileExists(tempDeletePath.c_str()))
+ {
+ ++suffix;
+ tempDeletePath = tempDeletePathBase + '_' + intToStr(suffix);
+ }
+
+ LOG(Warn,"Unable to remove file " + std::string(src) + " - it may be in use. Moving to "
+ + tempDeletePath + " and scheduling delete on reboot.");
+ moveFile(src,tempDeletePath.c_str());
+ MoveFileEx(tempDeletePath.c_str(),0,MOVEFILE_DELAY_UNTIL_REBOOT);
+ }
+ else if (GetLastError() != ERROR_FILE_NOT_FOUND)
+ {
+ throw IOException("Unable to remove file " + std::string(src));
+ }
+ }
+#endif
+}
+
+std::string FileUtils::fileName(const char* path)
+{
+ char* pathCopy = strdup(path);
+ std::string basename = ::basename(pathCopy);
+ free(pathCopy);
+ return basename;
+}
+
+std::string FileUtils::dirname(const char* path)
+{
+ char* pathCopy = strdup(path);
+ std::string dirname = ::dirname(pathCopy);
+ free(pathCopy);
+ return dirname;
+}
+
+void FileUtils::touch(const char* path) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ // see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
+ //
+ // we use utimes/futimes instead of utimensat/futimens for compatibility
+ // with older Linux and Mac
+
+ if (fileExists(path))
+ {
+ utimes(path,0 /* use current date/time */);
+ }
+ else
+ {
+ int fd = creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (fd != -1)
+ {
+ futimes(fd,0 /* use current date/time */);
+ close(fd);
+ }
+ else
+ {
+ throw IOException("Unable to touch file " + std::string(path));
+ }
+ }
+#else
+ HANDLE result = CreateFile(path,GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ 0,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+ if (result == INVALID_HANDLE_VALUE)
+ {
+ throw IOException("Unable to touch file " + std::string(path));
+ }
+ else
+ {
+ CloseHandle(result);
+ }
+#endif
+}
+
+void FileUtils::rmdirRecursive(const char* path) throw (IOException)
+{
+ // remove dir contents
+ DirIterator dir(path);
+ while (dir.next())
+ {
+ std::string name = dir.fileName();
+ if (name != "." && name != "..")
+ {
+ if (dir.isDir())
+ {
+ rmdir(dir.filePath().c_str());
+ }
+ else
+ {
+ removeFile(dir.filePath().c_str());
+ }
+ }
+ }
+
+ // remove the directory itself
+ rmdir(path);
+}
+
+std::string FileUtils::canonicalPath(const char* path)
+{
+#ifdef PLATFORM_UNIX
+ // on Linux and Mac OS 10.6, realpath() can allocate the required
+ // amount of memory automatically, however Mac OS 10.5 does not support
+ // this, so we used a fixed-sized buffer on all platforms
+ char canonicalPathBuffer[PATH_MAX+1];
+ if (realpath(path,canonicalPathBuffer) != 0)
+ {
+ return std::string(canonicalPathBuffer);
+ }
+ else
+ {
+ throw IOException("Error reading canonical path for " + std::string(path));
+ }
+#else
+ throw IOException("canonicalPath() not implemented");
+#endif
+}
+
+std::string FileUtils::toWindowsPathSeparators(const std::string& str)
+{
+ std::string result = str;
+ std::replace(result.begin(),result.end(),'/','\\');
+ return result;
+}
+
+std::string FileUtils::toUnixPathSeparators(const std::string& str)
+{
+ std::string result = str;
+ std::replace(result.begin(),result.end(),'\\','/');
+ return result;
+}
+
+std::string FileUtils::tempPath()
+{
+#ifdef PLATFORM_UNIX
+ std::string tmpDir(notNullString(getenv("TMPDIR")));
+ if (tmpDir.empty())
+ {
+ tmpDir = "/tmp";
+ }
+ return tmpDir;
+#else
+ char buffer[MAX_PATH+1];
+ GetTempPath(MAX_PATH+1,buffer);
+ return toUnixPathSeparators(buffer);
+#endif
+}
+
+bool startsWithDriveLetter(const char* path)
+{
+ return strlen(path) >= 2 &&
+ (isalpha(path[0])) &&
+ path[1] == ':';
+}
+
+bool FileUtils::isRelative(const char* path)
+{
+#ifdef PLATFORM_UNIX
+ return strlen(path) == 0 || path[0] != '/';
+#else
+ // on Windows, a path is relative if it does not start with:
+ // - '\\' (a UNC name)
+ // - '[Drive Letter]:\'
+ // - A single backslash
+ //
+ // the input path is assumed to have already been converted to use
+ // Unix-style path separators
+
+ std::string pathStr(path);
+
+ if ((!pathStr.empty() && pathStr.at(0) == '/') ||
+ (startsWith(pathStr,"//")) ||
+ (startsWithDriveLetter(pathStr.c_str())))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+#endif
+}
+
+void FileUtils::writeFile(const char* path, const char* data, int length) throw (IOException)
+{
+ std::ofstream stream(path,std::ios::binary | std::ios::trunc);
+ stream.write(data,length);
+}
+
+std::string FileUtils::readFile(const char* path) throw (IOException)
+{
+ std::ifstream inputFile(path, std::ios::in | std::ios::binary);
+ std::string content;
+ inputFile.seekg(0, std::ios::end);
+ content.resize(static_cast<unsigned int>(inputFile.tellg()));
+ inputFile.seekg(0, std::ios::beg);
+ inputFile.read(&content[0], static_cast<int>(content.size()));
+ return content;
+}
+
+void FileUtils::copyFile(const char* src, const char* dest) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ std::ifstream inputFile(src,std::ios::binary);
+ std::ofstream outputFile(dest,std::ios::binary | std::ios::trunc);
+
+ if (!inputFile.good())
+ {
+ throw IOException("Failed to read file " + std::string(src));
+ }
+ if (!outputFile.good())
+ {
+ throw IOException("Failed to write file " + std::string(dest));
+ }
+
+ outputFile << inputFile.rdbuf();
+
+ if (inputFile.bad())
+ {
+ throw IOException("Error reading file " + std::string(src));
+ }
+ if (outputFile.bad())
+ {
+ throw IOException("Error writing file " + std::string(dest));
+ }
+
+ chmod(dest,fileMode(src));
+#else
+ if (!CopyFile(src,dest,FALSE))
+ {
+ throw IOException("Failed to copy " + std::string(src) + " to " + std::string(dest));
+ }
+#endif
+}
+
+std::string FileUtils::makeAbsolute(const char* path, const char* basePath)
+{
+ if (isRelative(path))
+ {
+ assert(!isRelative(basePath));
+ return std::string(basePath) + '/' + std::string(path);
+ }
+ else
+ {
+ return path;
+ }
+}
+
+void FileUtils::chdir(const char* path) throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ if (::chdir(path) != 0)
+ {
+ throw FileUtils::IOException("Unable to change directory");
+ }
+#else
+ if (!SetCurrentDirectory(path))
+ {
+ throw FileUtils::IOException("Unable to change directory");
+ }
+#endif
+}
+
+std::string FileUtils::getcwd() throw (IOException)
+{
+#ifdef PLATFORM_UNIX
+ char path[PATH_MAX];
+ if (!::getcwd(path,PATH_MAX))
+ {
+ throw FileUtils::IOException("Failed to get current directory");
+ }
+ return std::string(path);
+#else
+ char path[MAX_PATH];
+ if (GetCurrentDirectory(MAX_PATH,path) == 0)
+ {
+ throw FileUtils::IOException("Failed to get current directory");
+ }
+ return toUnixPathSeparators(std::string(path));
+#endif
+}
+
diff --git a/mmc_updater/src/FileUtils.h b/mmc_updater/src/FileUtils.h
new file mode 100644
index 00000000..cb5830ae
--- /dev/null
+++ b/mmc_updater/src/FileUtils.h
@@ -0,0 +1,141 @@
+#pragma once
+
+#include <exception>
+#include <string>
+
+#include "Platform.h"
+#include "StringUtils.h"
+
+
+/** A set of functions for performing common operations
+ * on files, throwing exceptions if an operation fails.
+ *
+ * Path arguments to FileUtils functions should use Unix-style path
+ * separators.
+ */
+class FileUtils
+{
+ public:
+ /** Base class for exceptions reported by
+ * FileUtils methods if an operation fails.
+ */
+ class IOException : public std::exception
+ {
+ public:
+ IOException(const std::string& error);
+ IOException(int errorCode, const std::string& error);
+
+ virtual ~IOException() throw ();
+
+ enum Type
+ {
+ NoError,
+ /** Unknown error type. Call what() to get the description
+ * provided by the OS.
+ */
+ Unknown,
+ ReadOnlyFileSystem,
+ DiskFull
+ };
+
+ virtual const char* what() const throw ()
+ {
+ return m_error.c_str();
+ }
+
+ Type type() const;
+
+ private:
+ void init(int errorCode, const std::string& error);
+
+ std::string m_error;
+ int m_errorCode;
+ };
+
+ /** Remove a file. Throws an exception if the file
+ * could not be removed.
+ *
+ * On Unix, a file can be removed even if it is in use if the user
+ * has the necessary permissions. removeFile() tries to simulate
+ * this behavior on Windows. If a file cannot be removed on Windows
+ * because it is in use it will be moved to a temporary directory and
+ * scheduled for deletion on the next restart.
+ */
+ static void removeFile(const char* src) throw (IOException);
+
+ /** Set the permissions of a file. @p permissions uses the standard
+ * Unix mode_t values.
+ */
+ static void chmod(const char* path, int permissions) throw (IOException);
+
+ /** Returns true if the file at @p path exists. If @p path is a symlink,
+ * returns true if the symlink itself exists, not the target.
+ */
+ static bool fileExists(const char* path) throw (IOException);
+
+ /** Returns the Unix mode flags of @p path. If @p path is a symlink,
+ * returns the mode flags of the target.
+ */
+ static int fileMode(const char* path) throw (IOException);
+
+ static void moveFile(const char* src, const char* dest) throw (IOException);
+ static void mkdir(const char* dir) throw (IOException);
+ static void rmdir(const char* dir) throw (IOException);
+ static void createSymLink(const char* link, const char* target) throw (IOException);
+ static void touch(const char* path) throw (IOException);
+ static void copyFile(const char* src, const char* dest) throw (IOException);
+
+ /** Create all the directories in @p path which do not yet exist.
+ * @p path may be relative or absolute.
+ */
+ static void mkpath(const char* path) throw (IOException);
+
+ /** Returns the file name part of a file path, including the extension. */
+ static std::string fileName(const char* path);
+
+ /** Returns the directory part of a file path.
+ * On Windows this includes the drive letter, if present in @p path.
+ */
+ static std::string dirname(const char* path);
+
+ /** Remove a directory and all of its contents. */
+ static void rmdirRecursive(const char* dir) throw (IOException);
+
+ /** Return the full, absolute path to a file, resolving any
+ * symlinks and removing redundant sections.
+ */
+ static std::string canonicalPath(const char* path);
+
+ /** Returns the path to a directory for storing temporary files. */
+ static std::string tempPath();
+
+ /** Returns a copy of the path 'str' with Windows-style '\'
+ * dir separators converted to Unix-style '/' separators
+ */
+ static std::string toUnixPathSeparators(const std::string& str);
+
+ static std::string toWindowsPathSeparators(const std::string& str);
+
+ /** Returns true if the provided path is relative.
+ * Or false if absolute.
+ */
+ static bool isRelative(const char* path);
+
+ /** Converts @p path to an absolute path. If @p path is already absolute,
+ * just returns @p path, otherwise prefixes it with @p basePath to make it absolute.
+ *
+ * @p basePath should be absolute.
+ */
+ static std::string makeAbsolute(const char* path, const char* basePath);
+
+ static void writeFile(const char* path, const char* data, int length) throw (IOException);
+
+ static std::string readFile(const char* path) throw (IOException);
+
+ /** Changes the current working directory to @p path */
+ static void chdir(const char* path) throw (IOException);
+
+ /** Returns the current working directory of the application. */
+ static std::string getcwd() throw (IOException);
+};
+
diff --git a/mmc_updater/src/Log.cpp b/mmc_updater/src/Log.cpp
new file mode 100644
index 00000000..d4e5a214
--- /dev/null
+++ b/mmc_updater/src/Log.cpp
@@ -0,0 +1,65 @@
+#include "Log.h"
+
+#include "Platform.h"
+#include "StringUtils.h"
+#include "ProcessUtils.h"
+
+#include <string.h>
+#include <iostream>
+
+Log m_globalLog;
+
+Log* Log::instance()
+{
+ return &m_globalLog;
+}
+
+Log::Log()
+{
+}
+
+Log::~Log()
+{
+}
+
+void Log::open(const std::string& path)
+{
+ m_mutex.lock();
+ m_output.open(path.c_str(),std::ios_base::out | std::ios_base::app);
+ m_mutex.unlock();
+}
+
+void Log::writeToStream(std::ostream& stream, Type type, const char* text)
+{
+ // Multiple processes may be writing to the same log file during
+ // an update. No attempt is made to synchronize access to the file.
+ //
+ // Under Unix, appends to a single file on a local FS by multiple writers should be atomic
+ // provided that the length of 'text' is less than PIPE_BUF
+ //
+ switch (type)
+ {
+ case Info:
+ stream << "INFO ";
+ break;
+ case Warn:
+ stream << "WARN ";
+ break;
+ case Error:
+ stream << "ERROR ";
+ break;
+ }
+ stream << '(' << intToStr(ProcessUtils::currentProcessId()) << ") " << text << std::endl;
+}
+
+void Log::write(Type type, const char* text)
+{
+ m_mutex.lock();
+ writeToStream(std::cerr,type,text);
+ if (m_output.is_open())
+ {
+ writeToStream(m_output,type,text);
+ }
+ m_mutex.unlock();
+}
+
diff --git a/mmc_updater/src/Log.h b/mmc_updater/src/Log.h
new file mode 100644
index 00000000..cf6be832
--- /dev/null
+++ b/mmc_updater/src/Log.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <string>
+#include <fstream>
+
+#include <thread>
+#include <mutex>
+
+class Log
+{
+ public:
+ enum Type
+ {
+ Info,
+ Warn,
+ Error
+ };
+
+ Log();
+ ~Log();
+
+ void open(const std::string& path);
+
+ /** Write @p text to the log. This method is thread-safe. */
+ void write(Type type, const std::string& text);
+ /** Write @p text to the log. This method is thread-safe. */
+ void write(Type type, const char* text);
+
+ static Log* instance();
+
+ private:
+ static void writeToStream(std::ostream& stream, Type type, const char* text);
+
+ std::mutex m_mutex;
+ std::ofstream m_output;
+};
+
+inline void Log::write(Type type, const std::string& text)
+{
+ write(type,text.c_str());
+}
+
+#define LOG(type,text) \
+ Log::instance()->write(Log::type,text)
+
+
diff --git a/mmc_updater/src/MacBundle.cpp b/mmc_updater/src/MacBundle.cpp
new file mode 100644
index 00000000..205869eb
--- /dev/null
+++ b/mmc_updater/src/MacBundle.cpp
@@ -0,0 +1,53 @@
+#include "MacBundle.h"
+
+#include "FileUtils.h"
+#include "Log.h"
+
+MacBundle::MacBundle(const std::string& path, const std::string& appName)
+: m_appName(appName)
+{
+ m_path = path + '/' + appName + ".app";
+}
+
+std::string MacBundle::bundlePath() const
+{
+ return m_path;
+}
+
+void MacBundle::create(const std::string& infoPlist,
+ const std::string& icon,
+ const std::string& exePath)
+{
+ try
+ {
+ // create the bundle directories
+ FileUtils::mkpath(m_path.c_str());
+
+ std::string contentDir = m_path + "/Contents";
+ std::string resourceDir = contentDir + "/Resources";
+ std::string binDir = contentDir + "/MacOS";
+
+ FileUtils::mkpath(resourceDir.c_str());
+ FileUtils::mkpath(binDir.c_str());
+
+ // create the Contents/Info.plist file
+ FileUtils::writeFile((contentDir + "/Info.plist").c_str(),infoPlist.c_str(),static_cast<int>(infoPlist.size()));
+
+ // save the icon to Contents/Resources/<appname>.icns
+ FileUtils::writeFile((resourceDir + '/' + m_appName + ".icns").c_str(),icon.c_str(),static_cast<int>(icon.size()));
+
+ // copy the app binary to Contents/MacOS/<appname>
+ m_exePath = binDir + '/' + m_appName;
+ FileUtils::copyFile(exePath.c_str(),m_exePath.c_str());
+ }
+ catch (const FileUtils::IOException& exception)
+ {
+ LOG(Error,"Unable to create app bundle. " + std::string(exception.what()));
+ }
+}
+
+std::string MacBundle::executablePath() const
+{
+ return m_exePath;
+}
+
diff --git a/mmc_updater/src/MacBundle.h b/mmc_updater/src/MacBundle.h
new file mode 100644
index 00000000..2b119d8f
--- /dev/null
+++ b/mmc_updater/src/MacBundle.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <string>
+
+/** Class for creating minimal Mac app bundles. */
+class MacBundle
+{
+ public:
+ /** Create a MacBundle instance representing the bundle
+ * in <path>/<appName>.app
+ */
+ MacBundle(const std::string& path, const std::string& appName);
+
+ /** Create a simple Mac bundle.
+ *
+ * @param infoPlist The content of the Info.plist file
+ * @param icon The content of the app icon
+ * @param exePath The path of the file to use for the main app in the bundle.
+ */
+ void create(const std::string& infoPlist,
+ const std::string& icon,
+ const std::string& exePath);
+
+ /** Returns the path of the main executable within the Mac bundle. */
+ std::string executablePath() const;
+
+ /** Returns the path of the bundle */
+ std::string bundlePath() const;
+
+ private:
+ std::string m_path;
+ std::string m_appName;
+ std::string m_exePath;
+};
+
diff --git a/mmc_updater/src/Platform.h b/mmc_updater/src/Platform.h
new file mode 100644
index 00000000..97867d6a
--- /dev/null
+++ b/mmc_updater/src/Platform.h
@@ -0,0 +1,34 @@
+#pragma once
+
+// basic platform defines
+#ifdef __linux__
+ #define PLATFORM_LINUX
+#endif
+
+#ifdef WIN32
+ #define PLATFORM_WINDOWS
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #include <shellapi.h>
+
+ // disable warnings about exception specifications,
+ // which are not implemented in Visual C++
+ #ifdef MSVC
+ #pragma warning(disable:4290)
+ #endif
+#endif
+
+#ifdef __APPLE__
+ #define PLATFORM_MAC
+#endif
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_MAC)
+ #define PLATFORM_UNIX
+#endif
+
+// platform-specific type aliases
+#if defined(PLATFORM_UNIX)
+ #define PLATFORM_PID pid_t
+#else
+ #define PLATFORM_PID DWORD
+#endif
diff --git a/mmc_updater/src/ProcessUtils.cpp b/mmc_updater/src/ProcessUtils.cpp
new file mode 100644
index 00000000..3b9ffac2
--- /dev/null
+++ b/mmc_updater/src/ProcessUtils.cpp
@@ -0,0 +1,536 @@
+#include "ProcessUtils.h"
+
+#include "FileUtils.h"
+#include "Platform.h"
+#include "StringUtils.h"
+#include "Log.h"
+
+#include <string.h>
+#include <vector>
+#include <iostream>
+
+#ifdef PLATFORM_WINDOWS
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <errno.h>
+#endif
+
+#ifdef PLATFORM_MAC
+#include <Security/Security.h>
+#include <mach-o/dyld.h>
+#endif
+
+PLATFORM_PID ProcessUtils::currentProcessId()
+{
+#ifdef PLATFORM_UNIX
+ return getpid();
+#else
+ return GetCurrentProcessId();
+#endif
+}
+
+int ProcessUtils::runSync(const std::string& executable,
+ const std::list<std::string>& args)
+{
+#ifdef PLATFORM_UNIX
+ return runSyncUnix(executable,args);
+#else
+ return runWindows(executable,args,RunSync);
+#endif
+}
+
+#ifdef PLATFORM_UNIX
+int ProcessUtils::runSyncUnix(const std::string& executable,
+ const std::list<std::string>& args)
+{
+ PLATFORM_PID pid = runAsyncUnix(executable,args);
+ int status = 0;
+ if (waitpid(pid,&status,0) != -1)
+ {
+ if (WIFEXITED(status))
+ {
+ return static_cast<char>(WEXITSTATUS(status));
+ }
+ else
+ {
+ LOG(Warn,"Child exited abnormally");
+ return -1;
+ }
+ }
+ else
+ {
+ LOG(Warn,"Failed to get exit status of child " + intToStr(pid));
+ return WaitFailed;
+ }
+}
+#endif
+
+void ProcessUtils::runAsync(const std::string& executable,
+ const std::list<std::string>& args)
+{
+#ifdef PLATFORM_WINDOWS
+ runWindows(executable,args,RunAsync);
+#elif defined(PLATFORM_UNIX)
+ runAsyncUnix(executable,args);
+#endif
+}
+
+int ProcessUtils::runElevated(const std::string& executable,
+ const std::list<std::string>& args,
+ const std::string& task)
+{
+#ifdef PLATFORM_WINDOWS
+ (void)task;
+ return runElevatedWindows(executable,args);
+#elif defined(PLATFORM_MAC)
+ (void)task;
+ return runElevatedMac(executable,args);
+#elif defined(PLATFORM_LINUX)
+ return runElevatedLinux(executable,args,task);
+#endif
+}
+
+bool ProcessUtils::waitForProcess(PLATFORM_PID pid)
+{
+#ifdef PLATFORM_UNIX
+ pid_t result = ::waitpid(pid, 0, 0);
+ if (result < 0)
+ {
+ LOG(Error,"waitpid() failed with error: " + std::string(strerror(errno)));
+ }
+ return result > 0;
+#elif defined(PLATFORM_WINDOWS)
+ HANDLE hProc;
+
+ if (!(hProc = OpenProcess(SYNCHRONIZE, FALSE, pid)))
+ {
+ LOG(Error,"Unable to get process handle for pid " + intToStr(pid) + " last error " + intToStr(GetLastError()));
+ return false;
+ }
+
+ DWORD dwRet = WaitForSingleObject(hProc, INFINITE);
+ CloseHandle(hProc);
+
+ if (dwRet == WAIT_FAILED)
+ {
+ LOG(Error,"WaitForSingleObject failed with error " + intToStr(GetLastError()));
+ }
+
+ return (dwRet == WAIT_OBJECT_0);
+#endif
+}
+
+#ifdef PLATFORM_LINUX
+int ProcessUtils::runElevatedLinux(const std::string& executable,
+ const std::list<std::string>& args,
+ const std::string& _task)
+{
+ std::string task(_task);
+ if (task.empty())
+ {
+ task = FileUtils::fileName(executable.c_str());
+ }
+
+ // try available graphical sudo instances until we find one that works.
+ // The different sudo front-ends have different behaviors with respect to error codes:
+ //
+ // - 'kdesudo': return 1 if the user enters the wrong password 3 times or if
+ // they cancel elevation
+ //
+ // - recent 'gksudo' versions: return 1 if the user enters the wrong password
+ // : return -1 if the user cancels elevation
+ //
+ // - older 'gksudo' versions : return 0 if the user cancels elevation
+
+ std::vector<std::string> sudos;
+
+ if (getenv("KDE_SESSION_VERSION"))
+ {
+ sudos.push_back("kdesudo");
+ }
+ sudos.push_back("gksudo");
+
+ for (unsigned int i=0; i < sudos.size(); i++)
+ {
+ const std::string& sudoBinary = sudos.at(i);
+
+ std::list<std::string> sudoArgs;
+ sudoArgs.push_back("-u");
+ sudoArgs.push_back("root");
+
+ if (sudoBinary == "kdesudo")
+ {
+ sudoArgs.push_back("-d");
+ sudoArgs.push_back("--comment");
+ std::string sudoMessage = task + " needs administrative privileges. Please enter your password.";
+ sudoArgs.push_back(sudoMessage);
+ }
+ else if (sudoBinary == "gksudo")
+ {
+ sudoArgs.push_back("--description");
+ sudoArgs.push_back(task);
+ }
+ else
+ {
+ sudoArgs.push_back(task);
+ }
+
+ sudoArgs.push_back("--");
+ sudoArgs.push_back(executable);
+ std::copy(args.begin(),args.end(),std::back_inserter(sudoArgs));
+
+ int result = ProcessUtils::runSync(sudoBinary,sudoArgs);
+
+ LOG(Info,"Tried to use sudo " + sudoBinary + " with response " + intToStr(result));
+
+ if (result != RunFailed)
+ {
+ return result;
+ break;
+ }
+ }
+ return RunElevatedFailed;
+}
+#endif
+
+#ifdef PLATFORM_MAC
+int ProcessUtils::runElevatedMac(const std::string& executable,
+ const std::list<std::string>& args)
+{
+ // request elevation using the Security Service.
+ //
+ // This only works when the application is being run directly
+ // from the Mac. Attempting to run the app via a remote SSH session
+ // (for example) will fail with an interaction-not-allowed error
+
+ OSStatus status;
+ AuthorizationRef authorizationRef;
+
+ status = AuthorizationCreate(
+ NULL,
+ kAuthorizationEmptyEnvironment,
+ kAuthorizationFlagDefaults,
+ &authorizationRef);
+
+ AuthorizationItem right = { kAuthorizationRightExecute, 0, NULL, 0 };
+ AuthorizationRights rights = { 1, &right };
+
+ AuthorizationFlags flags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagPreAuthorize |
+ kAuthorizationFlagExtendRights;
+
+ if (status == errAuthorizationSuccess)
+ {
+ status = AuthorizationCopyRights(authorizationRef, &rights, NULL,
+ flags, NULL);
+
+ if (status == errAuthorizationSuccess)
+ {
+ char** argv;
+ argv = (char**) malloc(sizeof(char*) * args.size() + 1);
+
+ unsigned int i = 0;
+ for (std::list<std::string>::const_iterator iter = args.begin(); iter != args.end(); iter++)
+ {
+ argv[i] = strdup(iter->c_str());
+ ++i;
+ }
+ argv[i] = NULL;
+
+ FILE* pipe = NULL;
+
+ char* tool = strdup(executable.c_str());
+
+ status = AuthorizationExecuteWithPrivileges(authorizationRef, tool,
+ kAuthorizationFlagDefaults, argv, &pipe);
+
+ if (status == errAuthorizationSuccess)
+ {
+ // AuthorizationExecuteWithPrivileges does not provide a way to get the process ID
+ // of the child process.
+ //
+ // Discussions on Apple development forums suggest two approaches for working around this,
+ //
+ // - Modify the child process to sent its process ID back to the parent via
+ // the pipe passed to AuthorizationExecuteWithPrivileges.
+ //
+ // - Use the generic Unix wait() call.
+ //
+ // This code uses wait(), which is simpler, but suffers from the problem that wait() waits
+ // for any child process, not necessarily the specific process launched
+ // by AuthorizationExecuteWithPrivileges.
+ //
+ // Apple's documentation (see 'Authorization Services Programming Guide') suggests
+ // installing files in an installer as a legitimate use for
+ // AuthorizationExecuteWithPrivileges but in general strongly recommends
+ // not using this call and discusses a number of other alternatives
+ // for performing privileged operations,
+ // which we could consider in future.
+
+ int childStatus;
+ pid_t childPid = wait(&childStatus);
+
+ if (childStatus != 0)
+ {
+ LOG(Error,"elevated process failed with status " + intToStr(childStatus) + " pid "
+ + intToStr(childPid));
+ }
+ else
+ {
+ LOG(Info,"elevated process succeded with pid " + intToStr(childPid));
+ }
+
+ return childStatus;
+ }
+ else
+ {
+ LOG(Error,"failed to launch elevated process " + intToStr(status));
+ return RunElevatedFailed;
+ }
+
+ // If we want to know more information about what has happened:
+ // http://developer.apple.com/mac/library/documentation/Security/Reference/authorization_ref/Reference/reference.html#//apple_ref/doc/uid/TP30000826-CH4g-CJBEABHG
+ free(tool);
+ for (i = 0; i < args.size(); i++)
+ {
+ free(argv[i]);
+ }
+ }
+ else
+ {
+ LOG(Error,"failed to get rights to launch elevated process. status: " + intToStr(status));
+ return RunElevatedFailed;
+ }
+ }
+ else
+ {
+ return RunElevatedFailed;
+ }
+}
+#endif
+
+// convert a list of arguments in a space-separated string.
+// Arguments containing spaces are enclosed in quotes
+std::string quoteArgs(const std::list<std::string>& arguments)
+{
+ std::string quotedArgs;
+ for (std::list<std::string>::const_iterator iter = arguments.begin();
+ iter != arguments.end();
+ iter++)
+ {
+ std::string arg = *iter;
+
+ bool isQuoted = !arg.empty() &&
+ arg.at(0) == '"' &&
+ arg.at(arg.size()-1) == '"';
+
+ if (!isQuoted && arg.find(' ') != std::string::npos)
+ {
+ arg.insert(0,"\"");
+ arg.append("\"");
+ }
+ quotedArgs += arg;
+ quotedArgs += " ";
+ }
+ return quotedArgs;
+}
+
+#ifdef PLATFORM_WINDOWS
+int ProcessUtils::runElevatedWindows(const std::string& executable,
+ const std::list<std::string>& arguments)
+{
+ std::string args = quoteArgs(arguments);
+
+ SHELLEXECUTEINFO executeInfo;
+ ZeroMemory(&executeInfo,sizeof(executeInfo));
+ executeInfo.cbSize = sizeof(SHELLEXECUTEINFO);
+ executeInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
+ // request UAC elevation
+ executeInfo.lpVerb = "runas";
+ executeInfo.lpFile = executable.c_str();
+ executeInfo.lpParameters = args.c_str();
+ executeInfo.nShow = SW_SHOWNORMAL;
+
+ LOG(Info,"Attempting to execute " + executable + " with administrator priviledges");
+ if (!ShellExecuteEx(&executeInfo))
+ {
+ LOG(Error,"Failed to start with admin priviledges using ShellExecuteEx()");
+ return RunElevatedFailed;
+ }
+
+ WaitForSingleObject(executeInfo.hProcess, INFINITE);
+
+ // this assumes the process succeeded - we need to check whether
+ // this is actually the case.
+ return 0;
+}
+#endif
+
+#ifdef PLATFORM_UNIX
+PLATFORM_PID ProcessUtils::runAsyncUnix(const std::string& executable,
+ const std::list<std::string>& args)
+{
+ pid_t child = fork();
+ if (child == 0)
+ {
+ // in child process
+ char** argBuffer = new char*[args.size() + 2];
+ argBuffer[0] = strdup(executable.c_str());
+ int i = 1;
+ for (std::list<std::string>::const_iterator iter = args.begin(); iter != args.end(); iter++)
+ {
+ argBuffer[i] = strdup(iter->c_str());
+ ++i;
+ }
+ argBuffer[i] = 0;
+
+ if (execvp(executable.c_str(),argBuffer) == -1)
+ {
+ LOG(Error,"error starting child: " + std::string(strerror(errno)));
+ exit(RunFailed);
+ }
+ }
+ else
+ {
+ LOG(Info,"Started child process " + intToStr(child));
+ }
+ return child;
+}
+#endif
+
+#ifdef PLATFORM_WINDOWS
+int ProcessUtils::runWindows(const std::string& _executable,
+ const std::list<std::string>& _args,
+ RunMode runMode)
+{
+ // most Windows API functions allow back and forward slashes to be
+ // used interchangeably. However, an application started with
+ // CreateProcess() may fail to find Side-by-Side library dependencies
+ // in the same directory as the executable if forward slashes are
+ // used as path separators, so convert the path to use back slashes here.
+ //
+ // This may be related to LoadLibrary() requiring backslashes instead
+ // of forward slashes.
+ std::string executable = FileUtils::toWindowsPathSeparators(_executable);
+
+ std::list<std::string> args(_args);
+ args.push_front(executable);
+ std::string commandLine = quoteArgs(args);
+
+ STARTUPINFO startupInfo;
+ ZeroMemory(&startupInfo,sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ PROCESS_INFORMATION processInfo;
+ ZeroMemory(&processInfo,sizeof(processInfo));
+
+ char* commandLineStr = strdup(commandLine.c_str());
+ bool result = CreateProcess(
+ executable.c_str(),
+ commandLineStr,
+ 0 /* process attributes */,
+ 0 /* thread attributes */,
+ false /* inherit handles */,
+ NORMAL_PRIORITY_CLASS /* creation flags */,
+ 0 /* environment */,
+ 0 /* current directory */,
+ &startupInfo /* startup info */,
+ &processInfo /* process information */
+ );
+
+ if (!result)
+ {
+ LOG(Error,"Failed to start child process. " + executable + " Last error: " + intToStr(GetLastError()));
+ return RunFailed;
+ }
+ else
+ {
+ if (runMode == RunSync)
+ {
+ if (WaitForSingleObject(processInfo.hProcess,INFINITE) == WAIT_OBJECT_0)
+ {
+ DWORD status = WaitFailed;
+ if (GetExitCodeProcess(processInfo.hProcess,&status) != 0)
+ {
+ LOG(Error,"Failed to get exit code for process");
+ }
+ return status;
+ }
+ else
+ {
+ LOG(Error,"Failed to wait for process to finish");
+ return WaitFailed;
+ }
+ }
+ else
+ {
+ // process is being run asynchronously - return zero as if it had
+ // succeeded
+ return 0;
+ }
+ }
+}
+#endif
+
+std::string ProcessUtils::currentProcessPath()
+{
+#ifdef PLATFORM_LINUX
+ std::string path = FileUtils::canonicalPath("/proc/self/exe");
+ LOG(Info,"Current process path " + path);
+ return path;
+#elif defined(PLATFORM_MAC)
+ uint32_t bufferSize = PATH_MAX;
+ char buffer[bufferSize];
+ _NSGetExecutablePath(buffer,&bufferSize);
+ return buffer;
+#else
+ char fileName[MAX_PATH];
+ GetModuleFileName(0 /* get path of current process */,fileName,MAX_PATH);
+ return fileName;
+#endif
+}
+
+#ifdef PLATFORM_WINDOWS
+void ProcessUtils::convertWindowsCommandLine(LPCWSTR commandLine, int& argc, char**& argv)
+{
+ argc = 0;
+ LPWSTR* argvUnicode = CommandLineToArgvW(commandLine,&argc);
+
+ argv = new char*[argc];
+ for (int i=0; i < argc; i++)
+ {
+ const int BUFFER_SIZE = 4096;
+ char buffer[BUFFER_SIZE];
+
+ int length = WideCharToMultiByte(CP_ACP,
+ 0 /* flags */,
+ argvUnicode[i],
+ -1, /* argvUnicode is null terminated */
+ buffer,
+ BUFFER_SIZE,
+ 0,
+ false);
+
+ // note: if WideCharToMultiByte() fails it will return zero,
+ // in which case we store a zero-length argument in argv
+ if (length == 0)
+ {
+ argv[i] = new char[1];
+ argv[i][0] = '\0';
+ }
+ else
+ {
+ // if the input string to WideCharToMultiByte is null-terminated,
+ // the output is also null-terminated
+ argv[i] = new char[length];
+ strncpy(argv[i],buffer,length);
+ }
+ }
+ LocalFree(argvUnicode);
+}
+#endif
+
diff --git a/mmc_updater/src/ProcessUtils.h b/mmc_updater/src/ProcessUtils.h
new file mode 100644
index 00000000..e0cc3dc0
--- /dev/null
+++ b/mmc_updater/src/ProcessUtils.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "Platform.h"
+
+#include <list>
+#include <string>
+
+#ifndef PLATFORM_WINDOWS
+#include <unistd.h>
+#endif
+
+/** A set of functions to get information about the current
+ * process and launch new processes.
+ */
+class ProcessUtils
+{
+ public:
+ enum Errors
+ {
+ /** Status code returned by runElevated() if launching
+ * the elevated process fails.
+ */
+ RunElevatedFailed = 255,
+ /** Status code returned by runSync() if the application
+ * cannot be started.
+ */
+ RunFailed = -8,
+ /** Status code returned by runSync() if waiting for
+ * the application to exit and reading its status code fails.
+ */
+ WaitFailed = -1
+ };
+
+ static PLATFORM_PID currentProcessId();
+
+ /** Returns the absolute path to the main binary for
+ * the current process.
+ */
+ static std::string currentProcessPath();
+
+ /** Start a process and wait for it to finish before
+ * returning its exit code.
+ *
+ * Returns -1 if the process cannot be started.
+ */
+ static int runSync(const std::string& executable,
+ const std::list<std::string>& args);
+
+ /** Start a process and return without waiting for
+ * it to finish.
+ */
+ static void runAsync(const std::string& executable,
+ const std::list<std::string>& args);
+
+ /** Run a process with administrative privileges and return the
+ * status code of the process, or 0 on Windows.
+ *
+ * Returns RunElevatedFailed if the elevated process could
+ * not be started.
+ */
+ static int runElevated(const std::string& executable,
+ const std::list<std::string>& args,
+ const std::string& task);
+
+ /** Wait for a process to exit.
+ * Returns true if the process was found and has exited or false
+ * otherwise.
+ */
+ static bool waitForProcess(PLATFORM_PID pid);
+
+#ifdef PLATFORM_WINDOWS
+ /** Convert a unicode command line returned by GetCommandLineW()
+ * to a standard (argc,argv) pair. The resulting argv array and each
+ * element of argv must be freed using free()
+ */
+ static void convertWindowsCommandLine(LPCWSTR commandLine, int& argc, char**& argv);
+#endif
+
+ private:
+ enum RunMode
+ {
+ RunSync,
+ RunAsync
+ };
+ static int runElevatedLinux(const std::string& executable,
+ const std::list<std::string>& args,
+ const std::string& task);
+ static int runElevatedMac(const std::string& executable,
+ const std::list<std::string>& args);
+ static int runElevatedWindows(const std::string& executable,
+ const std::list<std::string>& args);
+
+ static PLATFORM_PID runAsyncUnix(const std::string& executable,
+ const std::list<std::string>& args);
+ static int runWindows(const std::string& executable,
+ const std::list<std::string>& args,
+ RunMode runMode);
+ static int runSyncUnix(const std::string& executable,
+ const std::list<std::string>& args);
+};
+
diff --git a/mmc_updater/src/StandardDirs.cpp b/mmc_updater/src/StandardDirs.cpp
new file mode 100644
index 00000000..72743d5e
--- /dev/null
+++ b/mmc_updater/src/StandardDirs.cpp
@@ -0,0 +1,63 @@
+#include "StandardDirs.h"
+
+#include "FileUtils.h"
+#include "StringUtils.h"
+
+#ifdef PLATFORM_UNIX
+ #include <stdlib.h>
+ #include <pwd.h>
+ #include <unistd.h>
+#endif
+
+#ifdef PLATFORM_WINDOWS
+#include <shlobj.h>
+#endif
+
+#ifdef PLATFORM_UNIX
+std::string StandardDirs::homeDir()
+{
+ std::string dir = notNullString(getenv("HOME"));
+ if (!dir.empty())
+ {
+ return dir;
+ }
+ else
+ {
+ // note: if this process has been elevated with sudo,
+ // this will return the home directory of the root user
+ struct passwd* userData = getpwuid(getuid());
+ return notNullString(userData->pw_dir);
+ }
+}
+#endif
+
+std::string StandardDirs::appDataPath(const std::string& organizationName,
+ const std::string& appName)
+{
+#ifdef PLATFORM_LINUX
+ std::string xdgDataHome = notNullString(getenv("XDG_DATA_HOME"));
+ if (xdgDataHome.empty())
+ {
+ xdgDataHome = homeDir() + "/.local/share";
+ }
+ xdgDataHome += "/data/" + organizationName + '/' + appName;
+ return xdgDataHome;
+
+#elif defined(PLATFORM_MAC)
+ std::string path = applicationSupportFolderPath();
+ path += '/' + appName;
+ return path;
+#elif defined(PLATFORM_WINDOWS)
+ char buffer[MAX_PATH + 1];
+ if (SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0 /* hToken */, SHGFP_TYPE_CURRENT, buffer) == S_OK)
+ {
+ std::string path = FileUtils::toUnixPathSeparators(notNullString(buffer));
+ path += '/' + organizationName + '/' + appName;
+ return path;
+ }
+ else
+ {
+ return std::string();
+ }
+#endif
+}
diff --git a/mmc_updater/src/StandardDirs.h b/mmc_updater/src/StandardDirs.h
new file mode 100644
index 00000000..18526173
--- /dev/null
+++ b/mmc_updater/src/StandardDirs.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Platform.h"
+
+#include <string>
+
+class StandardDirs
+{
+ public:
+ static std::string appDataPath(const std::string& organizationName,
+ const std::string& appName);
+
+ private:
+#ifdef PLATFORM_UNIX
+ static std::string homeDir();
+#endif
+
+#ifdef PLATFORM_MAC
+ static std::string applicationSupportFolderPath();
+#endif
+};
+
diff --git a/mmc_updater/src/StandardDirs.mm b/mmc_updater/src/StandardDirs.mm
new file mode 100644
index 00000000..53eecd47
--- /dev/null
+++ b/mmc_updater/src/StandardDirs.mm
@@ -0,0 +1,18 @@
+#include <Foundation/Foundation.h>
+
+#include "StandardDirs.h"
+
+std::string StandardDirs::applicationSupportFolderPath()
+{
+ NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
+ NSUserDomainMask,
+ true /* expand tildes */);
+
+ for (unsigned int i=0; i < [paths count]; i++)
+ {
+ NSString* path = [paths objectAtIndex:i];
+ return std::string([path UTF8String]);
+ }
+ return std::string();
+}
+
diff --git a/mmc_updater/src/StlSymbolsLeopard.cpp b/mmc_updater/src/StlSymbolsLeopard.cpp
new file mode 100644
index 00000000..932c78f0
--- /dev/null
+++ b/mmc_updater/src/StlSymbolsLeopard.cpp
@@ -0,0 +1,81 @@
+// Workarounds for iostream symbols that are referenced when building on OS X 10.7 but missing from
+// OS X 10.5's stdlibc++.dylib.
+//
+// In the <iostream> headers these are declared as extern templates but the symbols are not present under 10.5.
+// This file forces the compiler to instantiate the templates.
+//
+// see http://stackoverflow.com/questions/3484043/os-x-program-runs-on-dev-machine-crashing-horribly-on-others
+
+// Only do this on GCC. It fails builds on clang.
+#if defined(__GCC__)
+
+#include <iostream>
+
+_GLIBCXX_BEGIN_NAMESPACE(std)
+// From ostream_insert.h
+template ostream& __ostream_insert(ostream&, const char*, streamsize);
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+template wostream& __ostream_insert(wostream&, const wchar_t*, streamsize);
+#endif
+
+// From ostream.tcc
+template ostream& ostream::_M_insert(long);
+template ostream& ostream::_M_insert(unsigned long);
+template ostream& ostream::_M_insert(bool);
+#ifdef _GLIBCXX_USE_LONG_LONG
+template ostream& ostream::_M_insert(long long);
+template ostream& ostream::_M_insert(unsigned long long);
+#endif
+template ostream& ostream::_M_insert(double);
+template ostream& ostream::_M_insert(long double);
+template ostream& ostream::_M_insert(const void*);
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+template wostream& wostream::_M_insert(long);
+template wostream& wostream::_M_insert(unsigned long);
+template wostream& wostream::_M_insert(bool);
+#ifdef _GLIBCXX_USE_LONG_LONG
+template wostream& wostream::_M_insert(long long);
+template wostream& wostream::_M_insert(unsigned long long);
+#endif
+template wostream& wostream::_M_insert(double);
+template wostream& wostream::_M_insert(long double);
+template wostream& wostream::_M_insert(const void*);
+#endif
+
+// From istream.tcc
+template istream& istream::_M_extract(unsigned short&);
+template istream& istream::_M_extract(unsigned int&);
+template istream& istream::_M_extract(long&);
+template istream& istream::_M_extract(unsigned long&);
+template istream& istream::_M_extract(bool&);
+#ifdef _GLIBCXX_USE_LONG_LONG
+template istream& istream::_M_extract(long long&);
+template istream& istream::_M_extract(unsigned long long&);
+#endif
+template istream& istream::_M_extract(float&);
+template istream& istream::_M_extract(double&);
+template istream& istream::_M_extract(long double&);
+template istream& istream::_M_extract(void*&);
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+template wistream& wistream::_M_extract(unsigned short&);
+template wistream& wistream::_M_extract(unsigned int&);
+template wistream& wistream::_M_extract(long&);
+template wistream& wistream::_M_extract(unsigned long&);
+template wistream& wistream::_M_extract(bool&);
+#ifdef _GLIBCXX_USE_LONG_LONG
+template wistream& wistream::_M_extract(long long&);
+template wistream& wistream::_M_extract(unsigned long long&);
+#endif
+template wistream& wistream::_M_extract(float&);
+template wistream& wistream::_M_extract(double&);
+template wistream& wistream::_M_extract(long double&);
+template wistream& wistream::_M_extract(void*&);
+#endif
+
+_GLIBCXX_END_NAMESPACE
+
+#endif
+
diff --git a/mmc_updater/src/StringUtils.h b/mmc_updater/src/StringUtils.h
new file mode 100644
index 00000000..745b71c9
--- /dev/null
+++ b/mmc_updater/src/StringUtils.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <string.h>
+#include <string>
+#include <sstream>
+#include <stdlib.h>
+
+template <class T>
+inline std::string intToStr(T i)
+{
+ std::stringstream stream;
+ stream << i;
+ return stream.str();
+}
+
+inline bool strToBool(const std::string& str)
+{
+ return str == "true" || atoi(str.c_str()) != 0;
+}
+
+/** Returns @p text if non-null or a pointer
+ * to an empty null-terminated string otherwise.
+ */
+inline const char* notNullString(const char* text)
+{
+ if (text)
+ {
+ return text;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+inline bool endsWith(const std::string& str, const char* text)
+{
+ size_t length = strlen(text);
+ return str.find(text,str.size() - length) != std::string::npos;
+}
+
+inline bool startsWith(const std::string& str, const char* text)
+{
+ return str.find(text,0) == 0;
+}
+
diff --git a/mmc_updater/src/UpdateDialog.cpp b/mmc_updater/src/UpdateDialog.cpp
new file mode 100644
index 00000000..5f181a1c
--- /dev/null
+++ b/mmc_updater/src/UpdateDialog.cpp
@@ -0,0 +1,25 @@
+#include "UpdateDialog.h"
+
+UpdateDialog::UpdateDialog()
+: m_autoClose(false)
+{
+}
+
+void UpdateDialog::setAutoClose(bool autoClose)
+{
+ m_autoClose = autoClose;
+}
+
+bool UpdateDialog::autoClose() const
+{
+ return m_autoClose;
+}
+
+void UpdateDialog::updateFinished()
+{
+ if (m_autoClose)
+ {
+ quit();
+ }
+}
+
diff --git a/mmc_updater/src/UpdateDialog.h b/mmc_updater/src/UpdateDialog.h
new file mode 100644
index 00000000..d0782f8f
--- /dev/null
+++ b/mmc_updater/src/UpdateDialog.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "UpdateObserver.h"
+
+/** Base class for the updater's UI, sub-classed
+ * by the different platform implementations.
+ */
+class UpdateDialog : public UpdateObserver
+{
+ public:
+ UpdateDialog();
+ virtual ~UpdateDialog() {};
+
+ /** Sets whether the updater should automatically
+ * exit once the update has been installed.
+ */
+ void setAutoClose(bool autoClose);
+ bool autoClose() const;
+
+ virtual void init(int argc, char** argv) = 0;
+ virtual void exec() = 0;
+ virtual void quit() = 0;
+
+ virtual void updateFinished();
+
+ private:
+ bool m_autoClose;
+};
+
diff --git a/mmc_updater/src/UpdateDialogAscii.cpp b/mmc_updater/src/UpdateDialogAscii.cpp
new file mode 100644
index 00000000..78eb7433
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogAscii.cpp
@@ -0,0 +1,70 @@
+#include "UpdateDialogAscii.h"
+
+#include "AppInfo.h"
+#include "ProcessUtils.h"
+#include "StringUtils.h"
+
+const char* introMessage =
+ "%s (ASCII-art edition)\n"
+ "====================================\n"
+ "\n"
+ "We have a nice graphical interface for the %s, but unfortunately\n"
+ "we can't show it to you :(\n"
+ "\n"
+ "You can fix this by installing the GTK 2 libraries.\n\n"
+ "Installing Updates...\n";
+
+void UpdateDialogAscii::init(int /* argc */, char** /* argv */)
+{
+ const char* path = "/tmp/update-progress";
+ m_output.open(path);
+
+ char message[4096];
+ sprintf(message,introMessage,AppInfo::name().c_str());
+ m_output << message;
+
+ std::string command = "xterm";
+ std::list<std::string> args;
+ args.push_back("-hold");
+ args.push_back("-T");
+ args.push_back(AppInfo::name());
+ args.push_back("-e");
+ args.push_back("tail");
+ args.push_back("-n+1");
+ args.push_back("-f");
+ args.push_back(path);
+
+ ProcessUtils::runAsync(command,args);
+}
+
+void UpdateDialogAscii::updateError(const std::string& errorMessage)
+{
+ m_mutex.lock();
+ m_output << "\nThere was a problem installing the update: " << errorMessage << std::endl;
+ m_mutex.unlock();
+}
+
+void UpdateDialogAscii::updateProgress(int percentage)
+{
+ m_mutex.lock();
+ m_output << "Update Progress: " << intToStr(percentage) << '%' << std::endl;
+ m_mutex.unlock();
+}
+
+void UpdateDialogAscii::updateFinished()
+{
+ m_mutex.lock();
+ m_output << "\nUpdate Finished. You can now restart " << AppInfo::appName() << "." << std::endl;
+ m_mutex.unlock();
+
+ UpdateDialog::updateFinished();
+}
+
+void UpdateDialogAscii::quit()
+{
+}
+
+void UpdateDialogAscii::exec()
+{
+}
+
diff --git a/mmc_updater/src/UpdateDialogAscii.h b/mmc_updater/src/UpdateDialogAscii.h
new file mode 100644
index 00000000..138194c5
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogAscii.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "UpdateDialog.h"
+
+#include <fstream>
+#include <thread>
+#include <mutex>
+
+/** A fallback auto-update progress 'dialog' for use on
+ * Linux when the GTK UI cannot be loaded.
+ *
+ * The 'dialog' consists of an xterm tailing the contents
+ * of a file, into which progress messages are written.
+ */
+class UpdateDialogAscii : public UpdateDialog
+{
+ public:
+ // implements UpdateDialog
+ virtual void init(int argc, char** argv);
+ virtual void exec();
+ virtual void quit();
+
+ // implements UpdateObserver
+ virtual void updateError(const std::string& errorMessage);
+ virtual void updateProgress(int percentage);
+ virtual void updateFinished();
+
+ private:
+ std::mutex m_mutex;
+ std::ofstream m_output;
+};
+
diff --git a/mmc_updater/src/UpdateDialogCocoa.h b/mmc_updater/src/UpdateDialogCocoa.h
new file mode 100644
index 00000000..586fdc8e
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogCocoa.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "UpdateDialog.h"
+#include "UpdateObserver.h"
+
+class UpdateDialogPrivate;
+
+class UpdateDialogCocoa : public UpdateDialog
+{
+ public:
+ UpdateDialogCocoa();
+ ~UpdateDialogCocoa();
+
+ // implements UpdateDialog
+ virtual void init(int argc, char** argv);
+ virtual void exec();
+ virtual void quit();
+
+ // implements UpdateObserver
+ virtual void updateError(const std::string& errorMessage);
+ virtual void updateProgress(int percentage);
+ virtual void updateFinished();
+
+ static void* createAutoreleasePool();
+ static void releaseAutoreleasePool(void* data);
+
+ private:
+ void enableDockIcon();
+
+ UpdateDialogPrivate* d;
+};
+
diff --git a/mmc_updater/src/UpdateDialogCocoa.mm b/mmc_updater/src/UpdateDialogCocoa.mm
new file mode 100644
index 00000000..f24f3c4d
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogCocoa.mm
@@ -0,0 +1,194 @@
+#include "UpdateDialogCocoa.h"
+
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
+
+#include "AppInfo.h"
+#include "Log.h"
+#include "StringUtils.h"
+
+@interface UpdateDialogDelegate : NSObject
+{
+ @public UpdateDialogPrivate* dialog;
+}
+- (void) finishClicked;
+- (void) reportUpdateError:(id)arg;
+- (void) reportUpdateProgress:(id)arg;
+- (void) reportUpdateFinished:(id)arg;
+@end
+
+class UpdateDialogPrivate
+{
+ public:
+ UpdateDialogPrivate()
+ : hadError(false)
+ {
+ }
+
+ UpdateDialogDelegate* delegate;
+ NSAutoreleasePool* pool;
+ NSWindow* window;
+ NSButton* finishButton;
+ NSTextField* progressLabel;
+ NSProgressIndicator* progressBar;
+ bool hadError;
+};
+
+@implementation UpdateDialogDelegate
+- (void) finishClicked
+{
+ [NSApp stop:self];
+}
+- (void) reportUpdateError: (id)arg
+{
+ dialog->hadError = true;
+
+ NSAlert* alert = [NSAlert
+ alertWithMessageText: @"Update Problem"
+ defaultButton: nil
+ alternateButton: nil
+ otherButton: nil
+ informativeTextWithFormat: @"There was a problem installing the update:\n\n%@", arg];
+ [alert runModal];
+}
+- (void) reportUpdateProgress: (id)arg
+{
+ int percentage = [arg intValue];
+ [dialog->progressBar setDoubleValue:(percentage/100.0)];
+}
+- (void) reportUpdateFinished: (id)arg
+{
+ NSMutableString* message = [[NSMutableString alloc] init];
+ if (!dialog->hadError)
+ {
+ [message appendString:@"Updates installed."];
+ }
+ else
+ {
+ [message appendString:@"Update failed."];
+ }
+
+ [message appendString:@" Click 'Finish' to restart the application."];
+ [dialog->progressLabel setTitleWithMnemonic:message];
+ [message release];
+}
+@end
+
+UpdateDialogCocoa::UpdateDialogCocoa()
+: d(new UpdateDialogPrivate)
+{
+ [NSApplication sharedApplication];
+ d->pool = [[NSAutoreleasePool alloc] init];
+}
+
+UpdateDialogCocoa::~UpdateDialogCocoa()
+{
+ [d->pool release];
+}
+
+void UpdateDialogCocoa::enableDockIcon()
+{
+ // convert the application to a foreground application and in
+ // the process, enable the dock icon
+
+ // the reverse transformation is not possible, according to
+ // http://stackoverflow.com/questions/2832961/is-it-possible-to-hide-the-dock-icon-programmatically
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ TransformProcessType(&psn,kProcessTransformToForegroundApplication);
+}
+
+void UpdateDialogCocoa::init(int /* argc */, char** /* argv */)
+{
+ enableDockIcon();
+
+ // make the updater the active application. This does not
+ // happen automatically because the updater starts as a
+ // background application
+ [NSApp activateIgnoringOtherApps:YES];
+
+ d->delegate = [[UpdateDialogDelegate alloc] init];
+ d->delegate->dialog = d;
+
+ int width = 370;
+ int height = 100;
+
+ d->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, width, height)
+ styleMask:NSTitledWindowMask | NSMiniaturizableWindowMask
+ backing:NSBackingStoreBuffered defer:NO];
+ [d->window setTitle:[NSString stringWithUTF8String:AppInfo::name().c_str()]];
+
+ d->finishButton = [[NSButton alloc] init];
+ [d->finishButton setTitle:@"Finish"];
+ [d->finishButton setButtonType:NSMomentaryLightButton];
+ [d->finishButton setBezelStyle:NSRoundedBezelStyle];
+ [d->finishButton setTarget:d->delegate];
+ [d->finishButton setAction:@selector(finishClicked)];
+
+ d->progressBar = [[NSProgressIndicator alloc] init];
+ [d->progressBar setIndeterminate:false];
+ [d->progressBar setMinValue:0.0];
+ [d->progressBar setMaxValue:1.0];
+
+ d->progressLabel = [[NSTextField alloc] init];
+ [d->progressLabel setEditable:false];
+ [d->progressLabel setSelectable:false];
+ [d->progressLabel setTitleWithMnemonic:@"Installing Updates"];
+ [d->progressLabel setBezeled:false];
+ [d->progressLabel setDrawsBackground:false];
+
+ NSView* windowContent = [d->window contentView];
+ [windowContent addSubview:d->progressLabel];
+ [windowContent addSubview:d->progressBar];
+ [windowContent addSubview:d->finishButton];
+
+ [d->progressLabel setFrame:NSMakeRect(10,70,width - 10,20)];
+ [d->progressBar setFrame:NSMakeRect(10,40,width - 20,20)];
+ [d->finishButton setFrame:NSMakeRect(width - 85,5,80,30)];
+}
+
+void UpdateDialogCocoa::exec()
+{
+ [d->window makeKeyAndOrderFront:d->window];
+ [d->window center];
+ [NSApp run];
+}
+
+void UpdateDialogCocoa::updateError(const std::string& errorMessage)
+{
+ [d->delegate performSelectorOnMainThread:@selector(reportUpdateError:)
+ withObject:[NSString stringWithUTF8String:errorMessage.c_str()]
+ waitUntilDone:false];
+}
+
+void UpdateDialogCocoa::updateProgress(int percentage)
+{
+ [d->delegate performSelectorOnMainThread:@selector(reportUpdateProgress:)
+ withObject:[NSNumber numberWithInt:percentage]
+ waitUntilDone:false];
+}
+
+void UpdateDialogCocoa::updateFinished()
+{
+ [d->delegate performSelectorOnMainThread:@selector(reportUpdateFinished:)
+ withObject:nil
+ waitUntilDone:false];
+ UpdateDialog::updateFinished();
+}
+
+void* UpdateDialogCocoa::createAutoreleasePool()
+{
+ return [[NSAutoreleasePool alloc] init];
+}
+
+void UpdateDialogCocoa::releaseAutoreleasePool(void* arg)
+{
+ [(id)arg release];
+}
+
+void UpdateDialogCocoa::quit()
+{
+ [NSApp performSelectorOnMainThread:@selector(stop:) withObject:d->delegate waitUntilDone:false];
+}
+
+
diff --git a/mmc_updater/src/UpdateDialogGtk.cpp b/mmc_updater/src/UpdateDialogGtk.cpp
new file mode 100644
index 00000000..d91144f5
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogGtk.cpp
@@ -0,0 +1,155 @@
+#include "UpdateDialogGtk.h"
+
+#include "AppInfo.h"
+#include "StringUtils.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+UpdateDialogGtk* update_dialog_gtk_new()
+{
+ return new UpdateDialogGtk();
+}
+
+UpdateDialogGtk::UpdateDialogGtk()
+: m_hadError(false)
+{
+}
+
+void UpdateDialogGtk::init(int argc, char** argv)
+{
+ gtk_init(&argc,&argv);
+
+ m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(m_window),AppInfo::name().c_str());
+
+ m_progressLabel = gtk_label_new("Installing Updates");
+
+ GtkWidget* windowLayout = gtk_vbox_new(FALSE,3);
+ GtkWidget* buttonLayout = gtk_hbox_new(FALSE,3);
+ GtkWidget* labelLayout = gtk_hbox_new(FALSE,3);
+
+ m_finishButton = gtk_button_new_with_label("Finish");
+ gtk_widget_set_sensitive(m_finishButton,false);
+
+ m_progressBar = gtk_progress_bar_new();
+
+ // give the dialog a sensible default size by setting a minimum
+ // width on the progress bar. This is used instead of setting
+ // a default size for the dialog since gtk_window_set_default_size()
+ // is ignored when a dialog is marked as non-resizable
+ gtk_widget_set_usize(m_progressBar,350,-1);
+
+ gtk_signal_connect(GTK_OBJECT(m_finishButton),"clicked",
+ GTK_SIGNAL_FUNC(UpdateDialogGtk::finish),this);
+
+ gtk_container_add(GTK_CONTAINER(m_window),windowLayout);
+ gtk_container_set_border_width(GTK_CONTAINER(m_window),12);
+
+ gtk_box_pack_start(GTK_BOX(labelLayout),m_progressLabel,false,false,0);
+ gtk_box_pack_end(GTK_BOX(buttonLayout),m_finishButton,false,false,0);
+
+ gtk_box_pack_start(GTK_BOX(windowLayout),labelLayout,false,false,0);
+ gtk_box_pack_start(GTK_BOX(windowLayout),m_progressBar,false,false,0);
+ gtk_box_pack_start(GTK_BOX(windowLayout),buttonLayout,false,false,0);
+
+
+ gtk_widget_show(m_progressLabel);
+ gtk_widget_show(labelLayout);
+ gtk_widget_show(windowLayout);
+ gtk_widget_show(buttonLayout);
+ gtk_widget_show(m_finishButton);
+ gtk_widget_show(m_progressBar);
+
+ gtk_window_set_resizable(GTK_WINDOW(m_window),false);
+ gtk_window_set_position(GTK_WINDOW(m_window),GTK_WIN_POS_CENTER);
+
+ gtk_widget_show(m_window);
+}
+
+void UpdateDialogGtk::exec()
+{
+ gtk_main();
+}
+
+void UpdateDialogGtk::finish(GtkWidget* widget, gpointer _dialog)
+{
+ UpdateDialogGtk* dialog = static_cast<UpdateDialogGtk*>(_dialog);
+ dialog->quit();
+}
+
+void UpdateDialogGtk::quit()
+{
+ gtk_main_quit();
+}
+
+gboolean UpdateDialogGtk::notify(void* _message)
+{
+ UpdateMessage* message = static_cast<UpdateMessage*>(_message);
+ UpdateDialogGtk* dialog = static_cast<UpdateDialogGtk*>(message->receiver);
+ switch (message->type)
+ {
+ case UpdateMessage::UpdateProgress:
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->m_progressBar),message->progress/100.0);
+ break;
+ case UpdateMessage::UpdateFailed:
+ {
+ dialog->m_hadError = true;
+ std::string errorMessage = AppInfo::updateErrorMessage(message->message);
+ GtkWidget* errorDialog = gtk_message_dialog_new (GTK_WINDOW(dialog->m_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s",
+ errorMessage.c_str());
+ gtk_dialog_run (GTK_DIALOG (errorDialog));
+ gtk_widget_destroy (errorDialog);
+ gtk_widget_set_sensitive(dialog->m_finishButton,true);
+ }
+ break;
+ case UpdateMessage::UpdateFinished:
+ {
+ std::string message;
+ if (dialog->m_hadError)
+ {
+ message = "Update failed.";
+ }
+ else
+ {
+ message = "Update installed.";
+ }
+ message += " Click 'Finish' to restart the application.";
+ gtk_label_set_text(GTK_LABEL(dialog->m_progressLabel),message.c_str());
+ gtk_widget_set_sensitive(dialog->m_finishButton,true);
+ }
+ break;
+ }
+ delete message;
+
+ // do not invoke this function again
+ return false;
+}
+
+// callbacks during update installation
+void UpdateDialogGtk::updateError(const std::string& errorMessage)
+{
+ UpdateMessage* message = new UpdateMessage(this,UpdateMessage::UpdateFailed);
+ message->message = errorMessage;
+ g_idle_add(&UpdateDialogGtk::notify,message);
+}
+
+void UpdateDialogGtk::updateProgress(int percentage)
+{
+ UpdateMessage* message = new UpdateMessage(this,UpdateMessage::UpdateProgress);
+ message->progress = percentage;
+ g_idle_add(&UpdateDialogGtk::notify,message);
+}
+
+void UpdateDialogGtk::updateFinished()
+{
+ UpdateMessage* message = new UpdateMessage(this,UpdateMessage::UpdateFinished);
+ g_idle_add(&UpdateDialogGtk::notify,message);
+ UpdateDialog::updateFinished();
+}
+
+
diff --git a/mmc_updater/src/UpdateDialogGtk.h b/mmc_updater/src/UpdateDialogGtk.h
new file mode 100644
index 00000000..70e29c78
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogGtk.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "UpdateDialog.h"
+#include "UpdateMessage.h"
+#include "UpdateObserver.h"
+
+#include <gtk/gtk.h>
+
+class UpdateDialogGtk : public UpdateDialog
+{
+ public:
+ UpdateDialogGtk();
+
+ // implements UpdateDialog
+ virtual void init(int argc, char** argv);
+ virtual void exec();
+ virtual void quit();
+
+ // observer callbacks - these may be called
+ // from a background thread
+ virtual void updateError(const std::string& errorMessage);
+ virtual void updateProgress(int percentage);
+ virtual void updateFinished();
+
+ private:
+ static void finish(GtkWidget* widget, gpointer dialog);
+ static gboolean notify(void* message);
+
+ GtkWidget* m_window;
+ GtkWidget* m_progressLabel;
+ GtkWidget* m_finishButton;
+ GtkWidget* m_progressBar;
+ bool m_hadError;
+};
+
+// helper functions which allow the GTK dialog to be loaded dynamically
+// at runtime and used only if the GTK libraries are actually present
+extern "C" {
+ UpdateDialogGtk* update_dialog_gtk_new();
+}
+
+
diff --git a/mmc_updater/src/UpdateDialogGtkFactory.cpp b/mmc_updater/src/UpdateDialogGtkFactory.cpp
new file mode 100644
index 00000000..313da31a
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogGtkFactory.cpp
@@ -0,0 +1,59 @@
+#include "UpdateDialogGtkFactory.h"
+
+#include "Log.h"
+#include "UpdateDialog.h"
+#include "StringUtils.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+class UpdateDialogGtk;
+
+// GTK updater UI library embedded into
+// the updater binary
+extern unsigned char libupdatergtk_so[];
+extern unsigned int libupdatergtk_so_len;
+
+// pointers to helper functions in the GTK updater UI library
+UpdateDialogGtk* (*update_dialog_gtk_new)() = 0;
+
+bool extractFileFromBinary(const char* path, const void* buffer, size_t length)
+{
+ int fd = open(path,O_CREAT | O_WRONLY | O_TRUNC,0755);
+ size_t count = write(fd,buffer,length);
+ if (fd < 0 || count < length)
+ {
+ if (fd >= 0)
+ {
+ close(fd);
+ }
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+UpdateDialog* UpdateDialogGtkFactory::createDialog()
+{
+ const char* libPath = "/tmp/libupdatergtk.so";
+
+ if (!extractFileFromBinary(libPath,libupdatergtk_so,libupdatergtk_so_len))
+ {
+ LOG(Warn,"Failed to load the GTK UI library - " + std::string(strerror(errno)));
+ return 0;
+ }
+
+ void* gtkLib = dlopen(libPath,RTLD_LAZY);
+ if (!gtkLib)
+ {
+ LOG(Warn,"Failed to load the GTK UI - " + std::string(dlerror()));
+ return 0;
+ }
+ update_dialog_gtk_new = (UpdateDialogGtk* (*)()) dlsym(gtkLib,"update_dialog_gtk_new");
+ return reinterpret_cast<UpdateDialog*>(update_dialog_gtk_new());
+}
+
diff --git a/mmc_updater/src/UpdateDialogGtkFactory.h b/mmc_updater/src/UpdateDialogGtkFactory.h
new file mode 100644
index 00000000..1806c252
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogGtkFactory.h
@@ -0,0 +1,13 @@
+#pragma once
+
+class UpdateDialog;
+
+/** Factory for loading the GTK version of the update dialog
+ * dynamically at runtime if the GTK libraries are available.
+ */
+class UpdateDialogGtkFactory
+{
+ public:
+ static UpdateDialog* createDialog();
+};
+
diff --git a/mmc_updater/src/UpdateDialogWin32.cpp b/mmc_updater/src/UpdateDialogWin32.cpp
new file mode 100644
index 00000000..8b38bed2
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogWin32.cpp
@@ -0,0 +1,215 @@
+#include "UpdateDialogWin32.h"
+
+#include "AppInfo.h"
+#include "Log.h"
+
+// enable themed controls
+// see http://msdn.microsoft.com/en-us/library/bb773175%28v=vs.85%29.aspx
+// for details
+#pragma comment(linker,"\"/manifestdependency:type='win32' \
+name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
+processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+
+static const char* updateDialogClassName = "UPDATEDIALOG";
+
+static std::map<HWND,UpdateDialogWin32*> windowDialogMap;
+
+// enable the standard Windows font for a widget
+// (typically Tahoma or Segoe UI)
+void setDefaultFont(HWND window)
+{
+ SendMessage(window, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0));
+}
+
+LRESULT WINAPI updateDialogWindowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ std::map<HWND,UpdateDialogWin32*>::const_iterator iter = windowDialogMap.find(window);
+ if (iter != windowDialogMap.end())
+ {
+ return iter->second->windowProc(window,message,wParam,lParam);
+ }
+ else
+ {
+ return DefWindowProc(window,message,wParam,lParam);
+ }
+};
+
+void registerWindowClass()
+{
+ WNDCLASSEX wcex;
+ ZeroMemory(&wcex,sizeof(WNDCLASSEX));
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ HBRUSH background = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = updateDialogWindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hIcon = LoadIcon(GetModuleHandle(0),"IDI_APPICON");
+ wcex.hCursor = LoadCursor(0,IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)background;
+ wcex.lpszMenuName = (LPCTSTR)0;
+ wcex.lpszClassName = updateDialogClassName;
+ wcex.hIconSm = 0;
+ wcex.hInstance = GetModuleHandle(0);
+
+ RegisterClassEx(&wcex);
+}
+
+UpdateDialogWin32::UpdateDialogWin32()
+: m_hadError(false)
+{
+ registerWindowClass();
+}
+
+UpdateDialogWin32::~UpdateDialogWin32()
+{
+ for (std::map<HWND,UpdateDialogWin32*>::iterator iter = windowDialogMap.begin();
+ iter != windowDialogMap.end();
+ iter++)
+ {
+ if (iter->second == this)
+ {
+ std::map<HWND,UpdateDialogWin32*>::iterator oldIter = iter;
+ ++iter;
+ windowDialogMap.erase(oldIter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+}
+
+void UpdateDialogWin32::init(int /* argc */, char** /* argv */)
+{
+ int width = 400;
+ int height = 150;
+
+ DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+ m_window.CreateEx(0 /* dwExStyle */,
+ updateDialogClassName /* class name */,
+ AppInfo::name().c_str(),
+ style,
+ 0, 0, width, height,
+ 0 /* parent */, 0 /* menu */, 0 /* reserved */);
+ m_progressBar.Create(&m_window);
+ m_finishButton.Create(&m_window);
+ m_progressLabel.Create(&m_window);
+
+ installWindowProc(&m_window);
+ installWindowProc(&m_finishButton);
+
+ setDefaultFont(m_progressLabel);
+ setDefaultFont(m_finishButton);
+
+ m_progressBar.SetRange(0,100);
+ m_finishButton.SetWindowText("Finish");
+ m_finishButton.EnableWindow(false);
+ m_progressLabel.SetWindowText("Installing Updates");
+
+ m_window.SetWindowPos(0,0,0,width,height,0);
+ m_progressBar.SetWindowPos(0,10,40,width - 30,20,0);
+ m_progressLabel.SetWindowPos(0,10,15,width - 30,20,0);
+ m_finishButton.SetWindowPos(0,width-100,70,80,25,0);
+ m_window.CenterWindow();
+ m_window.ShowWindow();
+}
+
+void UpdateDialogWin32::exec()
+{
+ m_app.Run();
+}
+
+void UpdateDialogWin32::updateError(const std::string& errorMessage)
+{
+ UpdateMessage* message = new UpdateMessage(UpdateMessage::UpdateFailed);
+ message->message = errorMessage;
+ SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast<WPARAM>(message),0);
+}
+
+void UpdateDialogWin32::updateProgress(int percentage)
+{
+ UpdateMessage* message = new UpdateMessage(UpdateMessage::UpdateProgress);
+ message->progress = percentage;
+ SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast<WPARAM>(message),0);
+}
+
+void UpdateDialogWin32::updateFinished()
+{
+ UpdateMessage* message = new UpdateMessage(UpdateMessage::UpdateFinished);
+ SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast<WPARAM>(message),0);
+ UpdateDialog::updateFinished();
+}
+
+void UpdateDialogWin32::quit()
+{
+ PostThreadMessage(GetWindowThreadProcessId(m_window.GetHwnd(), 0 /* process ID */), WM_QUIT, 0, 0);
+}
+
+LRESULT WINAPI UpdateDialogWin32::windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CLOSE:
+ if (window == m_window.GetHwnd())
+ {
+ return 0;
+ }
+ break;
+ case WM_COMMAND:
+ {
+ if (reinterpret_cast<HWND>(lParam) == m_finishButton.GetHwnd())
+ {
+ quit();
+ }
+ }
+ break;
+ case WM_USER:
+ {
+ if (window == m_window.GetHwnd())
+ {
+ UpdateMessage* message = reinterpret_cast<UpdateMessage*>(wParam);
+ switch (message->type)
+ {
+ case UpdateMessage::UpdateFailed:
+ {
+ m_hadError = true;
+ std::string text = AppInfo::updateErrorMessage(message->message);
+ MessageBox(m_window.GetHwnd(),text.c_str(),"Update Problem",MB_OK);
+ }
+ break;
+ case UpdateMessage::UpdateProgress:
+ m_progressBar.SetPos(message->progress);
+ break;
+ case UpdateMessage::UpdateFinished:
+ {
+ std::string message;
+ m_finishButton.EnableWindow(true);
+ if (m_hadError)
+ {
+ message = "Update failed.";
+ }
+ else
+ {
+ message = "Updates installed.";
+ }
+ message += " Click 'Finish' to restart the application.";
+ m_progressLabel.SetWindowText(message.c_str());
+ }
+ break;
+ }
+ delete message;
+ }
+ }
+ break;
+ }
+ return DefWindowProc(window,message,wParam,lParam);
+}
+
+void UpdateDialogWin32::installWindowProc(CWnd* window)
+{
+ windowDialogMap[window->GetHwnd()] = this;
+}
diff --git a/mmc_updater/src/UpdateDialogWin32.h b/mmc_updater/src/UpdateDialogWin32.h
new file mode 100644
index 00000000..fe4208c8
--- /dev/null
+++ b/mmc_updater/src/UpdateDialogWin32.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "Platform.h"
+#include "UpdateDialog.h"
+#include "UpdateMessage.h"
+
+#include "wincore.h"
+#include "controls.h"
+#include "stdcontrols.h"
+
+class UpdateDialogWin32 : public UpdateDialog
+{
+ public:
+ UpdateDialogWin32();
+ ~UpdateDialogWin32();
+
+ // implements UpdateDialog
+ virtual void init(int argc, char** argv);
+ virtual void exec();
+ virtual void quit();
+
+ // implements UpdateObserver
+ virtual void updateError(const std::string& errorMessage);
+ virtual void updateProgress(int percentage);
+ virtual void updateFinished();
+
+ LRESULT WINAPI windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
+
+ private:
+ void installWindowProc(CWnd* window);
+
+ CWinApp m_app;
+ CWnd m_window;
+ CStatic m_progressLabel;
+ CProgressBar m_progressBar;
+ CButton m_finishButton;
+ bool m_hadError;
+};
+
diff --git a/mmc_updater/src/UpdateInstaller.cpp b/mmc_updater/src/UpdateInstaller.cpp
new file mode 100644
index 00000000..b29c5316
--- /dev/null
+++ b/mmc_updater/src/UpdateInstaller.cpp
@@ -0,0 +1,479 @@
+#include "UpdateInstaller.h"
+
+#include "AppInfo.h"
+#include "FileUtils.h"
+#include "Log.h"
+#include "ProcessUtils.h"
+#include "UpdateObserver.h"
+
+void UpdateInstaller::setWaitPid(PLATFORM_PID pid)
+{
+ m_waitPid = pid;
+}
+
+void UpdateInstaller::setInstallDir(const std::string& path)
+{
+ m_installDir = path;
+}
+
+void UpdateInstaller::setPackageDir(const std::string& path)
+{
+ m_packageDir = path;
+}
+
+void UpdateInstaller::setBackupDir(const std::string& path)
+{
+ m_backupDir = path;
+}
+
+void UpdateInstaller::setMode(Mode mode)
+{
+ m_mode = mode;
+}
+
+void UpdateInstaller::setScript(UpdateScript* script)
+{
+ m_script = script;
+}
+
+void UpdateInstaller::setForceElevated(bool elevated)
+{
+ m_forceElevated = elevated;
+}
+
+void UpdateInstaller::setFinishCmd(const std::string& cmd)
+{
+ m_finishCmd = cmd;
+}
+
+void UpdateInstaller::setFinishDir(const std::string &dir)
+{
+ m_finishDir = dir;
+}
+
+std::list<std::string> UpdateInstaller::updaterArgs() const
+{
+ std::list<std::string> args;
+ args.push_back("--install-dir");
+ args.push_back(m_installDir);
+ args.push_back("--package-dir");
+ args.push_back(m_packageDir);
+ args.push_back("--script");
+ args.push_back(m_script->path());
+ if (m_autoClose)
+ {
+ args.push_back("--auto-close");
+ }
+ if (m_dryRun)
+ {
+ args.push_back("--dry-run");
+ }
+ if (m_finishDir.size())
+ {
+ args.push_back("--dir");
+ args.push_back(m_finishDir);
+ }
+ return args;
+}
+
+void UpdateInstaller::reportError(const std::string& error)
+{
+ if (m_observer)
+ {
+ m_observer->updateError(error);
+ m_observer->updateFinished();
+ }
+}
+
+std::string UpdateInstaller::friendlyErrorForError(const FileUtils::IOException& exception) const
+{
+ std::string friendlyError;
+
+ switch (exception.type())
+ {
+ case FileUtils::IOException::ReadOnlyFileSystem:
+#ifdef PLATFORM_MAC
+ friendlyError = AppInfo::appName() + " was started from a read-only location. "
+ "Copy it to the Applications folder on your Mac and run "
+ "it from there.";
+#else
+ friendlyError = AppInfo::appName() + " was started from a read-only location. "
+ "Re-install it to a location that can be updated and run it from there.";
+#endif
+ break;
+ case FileUtils::IOException::DiskFull:
+ friendlyError = "The disk is full. Please free up some space and try again.";
+ break;
+ default:
+ break;
+ }
+
+ return friendlyError;
+}
+
+void UpdateInstaller::run() throw ()
+{
+ if (!m_script || !m_script->isValid())
+ {
+ reportError("Unable to read update script");
+ return;
+ }
+ if (m_installDir.empty())
+ {
+ reportError("No installation directory specified");
+ return;
+ }
+
+ std::string updaterPath;
+ try
+ {
+ updaterPath = ProcessUtils::currentProcessPath();
+ }
+ catch (const FileUtils::IOException&)
+ {
+ LOG(Error,"error reading process path with mode " + intToStr(m_mode));
+ reportError("Unable to determine path of updater");
+ return;
+ }
+
+ if (m_mode == Setup)
+ {
+ if (m_waitPid != 0)
+ {
+ LOG(Info,"Waiting for main app process to finish");
+ ProcessUtils::waitForProcess(m_waitPid);
+ }
+
+ std::list<std::string> args = updaterArgs();
+ args.push_back("--mode");
+ args.push_back("main");
+ args.push_back("--wait");
+ args.push_back(intToStr(ProcessUtils::currentProcessId()));
+
+ int installStatus = 0;
+ if (m_forceElevated || !checkAccess())
+ {
+ LOG(Info,"Insufficient rights to install app to " + m_installDir + " requesting elevation");
+
+ // start a copy of the updater with admin rights
+ installStatus = ProcessUtils::runElevated(updaterPath,args,AppInfo::name());
+ }
+ else
+ {
+ LOG(Info,"Sufficient rights to install app - restarting with same permissions");
+ installStatus = ProcessUtils::runSync(updaterPath,args);
+ }
+
+ if (installStatus == 0)
+ {
+ LOG(Info,"Update install completed");
+ }
+ else
+ {
+ LOG(Error,"Update install failed with status " + intToStr(installStatus));
+ }
+
+ // restart the main application - this is currently done
+ // regardless of whether the installation succeeds or not
+ restartMainApp();
+
+ // clean up files created by the updater
+ cleanup();
+ }
+ else if (m_mode == Main)
+ {
+ LOG(Info,"Starting update installation");
+
+ // the detailed error string returned by the OS
+ std::string error;
+ // the message to present to the user. This may be the same
+ // as 'error' or may be different if a more helpful suggestion
+ // can be made for a particular problem
+ std::string friendlyError;
+
+ try
+ {
+ LOG(Info,"Installing new and updated files");
+ installFiles();
+
+ LOG(Info,"Uninstalling removed files");
+ uninstallFiles();
+
+ LOG(Info,"Removing backups");
+ removeBackups();
+
+ postInstallUpdate();
+ }
+ catch (const FileUtils::IOException& exception)
+ {
+ error = exception.what();
+ friendlyError = friendlyErrorForError(exception);
+ }
+ catch (const std::string& genericError)
+ {
+ error = genericError;
+ }
+
+ if (!error.empty())
+ {
+ LOG(Error,std::string("Error installing update ") + error);
+
+ try
+ {
+ revert();
+ }
+ catch (const FileUtils::IOException& exception)
+ {
+ LOG(Error,"Error reverting partial update " + std::string(exception.what()));
+ }
+
+ if (m_observer)
+ {
+ if (friendlyError.empty())
+ {
+ friendlyError = error;
+ }
+ m_observer->updateError(friendlyError);
+ }
+ }
+
+ if (m_observer)
+ {
+ m_observer->updateFinished();
+ }
+ }
+}
+
+void UpdateInstaller::cleanup()
+{
+ try
+ {
+ FileUtils::rmdirRecursive(m_packageDir.c_str());
+ }
+ catch (const FileUtils::IOException& ex)
+ {
+ LOG(Error,"Error cleaning up updater " + std::string(ex.what()));
+ }
+ LOG(Info,"Updater files removed");
+}
+
+void UpdateInstaller::revert()
+{
+ LOG(Info,"Reverting installation!");
+ std::map<std::string,std::string>::const_iterator iter = m_backups.begin();
+ for (;iter != m_backups.end();iter++)
+ {
+ const std::string& installedFile = iter->first;
+ const std::string& backupFile = iter->second;
+ LOG(Info,"Restoring " + installedFile);
+ if(!m_dryRun)
+ {
+ if (FileUtils::fileExists(installedFile.c_str()))
+ {
+ FileUtils::removeFile(installedFile.c_str());
+ }
+ FileUtils::moveFile(backupFile.c_str(),installedFile.c_str());
+ }
+ }
+}
+
+void UpdateInstaller::installFile(const UpdateScriptFile& file)
+{
+ std::string sourceFile = file.source;
+ std::string destPath = file.dest;
+ std::string absDestPath = FileUtils::makeAbsolute(destPath.c_str(), m_installDir.c_str());
+
+ LOG(Info,"Installing file " + sourceFile + " to " + absDestPath);
+
+ // backup the existing file if any
+ backupFile(absDestPath);
+
+ // create the target directory if it does not exist
+ std::string destDir = FileUtils::dirname(absDestPath.c_str());
+ if (!FileUtils::fileExists(destDir.c_str()))
+ {
+ LOG(Info,"Destination path missing. Creating " + destDir);
+ if(!m_dryRun)
+ {
+ FileUtils::mkpath(destDir.c_str());
+ }
+ }
+
+ if (!FileUtils::fileExists(sourceFile.c_str()))
+ {
+ throw "Source file does not exist: " + sourceFile;
+ }
+ if(!m_dryRun)
+ {
+ FileUtils::copyFile(sourceFile.c_str(),absDestPath.c_str());
+
+ // set the permissions on the newly extracted file
+ FileUtils::chmod(absDestPath.c_str(),file.permissions);
+ }
+}
+
+void UpdateInstaller::installFiles()
+{
+ LOG(Info,"Installing files.");
+ std::vector<UpdateScriptFile>::const_iterator iter = m_script->filesToInstall().begin();
+ int filesInstalled = 0;
+ for (;iter != m_script->filesToInstall().end();iter++)
+ {
+ installFile(*iter);
+ ++filesInstalled;
+ if (m_observer)
+ {
+ int toInstallCount = static_cast<int>(m_script->filesToInstall().size());
+ double percentage = ((1.0 * filesInstalled) / toInstallCount) * 100.0;
+ m_observer->updateProgress(static_cast<int>(percentage));
+ }
+ }
+}
+
+void UpdateInstaller::uninstallFiles()
+{
+ LOG(Info,"Uninstalling files.");
+ std::vector<std::string>::const_iterator iter = m_script->filesToUninstall().begin();
+ for (;iter != m_script->filesToUninstall().end();iter++)
+ {
+ std::string path = FileUtils::makeAbsolute(iter->c_str(), m_installDir.c_str());
+ if (FileUtils::fileExists(path.c_str()))
+ {
+ LOG(Info,"Uninstalling " + path);
+ if(!m_dryRun)
+ {
+ FileUtils::removeFile(path.c_str());
+ }
+ }
+ else
+ {
+ LOG(Warn,"Unable to uninstall file " + path + " because it does not exist.");
+ }
+ }
+}
+
+void UpdateInstaller::backupFile(const std::string& path)
+{
+ if (!FileUtils::fileExists(path.c_str()))
+ {
+ // no existing file to backup
+ return;
+ }
+ std::string backupPath = path + ".bak";
+ LOG(Info,"Backing up file: " + path + " as " + backupPath);
+ if(!m_dryRun)
+ {
+ FileUtils::removeFile(backupPath.c_str());
+ FileUtils::moveFile(path.c_str(), backupPath.c_str());
+ }
+ m_backups[path] = backupPath;
+}
+
+void UpdateInstaller::removeBackups()
+{
+ LOG(Info,"Removing backups.");
+ std::map<std::string,std::string>::const_iterator iter = m_backups.begin();
+ for (;iter != m_backups.end();iter++)
+ {
+ const std::string& backupFile = iter->second;
+ LOG(Info,"Removing " + backupFile);
+ if(!m_dryRun)
+ {
+ FileUtils::removeFile(backupFile.c_str());
+ }
+ }
+}
+
+bool UpdateInstaller::checkAccess()
+{
+ std::string testFile = m_installDir + "/update-installer-test-file";
+ LOG(Info,"Checking for access: " + testFile);
+ try
+ {
+ if(!m_dryRun)
+ {
+ FileUtils::removeFile(testFile.c_str());
+ }
+ }
+ catch (const FileUtils::IOException& error)
+ {
+ LOG(Info,"Removing existing access check file failed " + std::string(error.what()));
+ }
+
+ try
+ {
+ if(!m_dryRun)
+ {
+ FileUtils::touch(testFile.c_str());
+ FileUtils::removeFile(testFile.c_str());
+ }
+ return true;
+ }
+ catch (const FileUtils::IOException& error)
+ {
+ LOG(Info,"checkAccess() failed " + std::string(error.what()));
+ return false;
+ }
+}
+
+void UpdateInstaller::setObserver(UpdateObserver* observer)
+{
+ m_observer = observer;
+}
+
+void UpdateInstaller::restartMainApp()
+{
+ try
+ {
+ std::string command = m_finishCmd;
+ std::list<std::string> args;
+
+ if (!command.empty())
+ {
+ if(!m_finishDir.empty())
+ {
+ args.push_back("--dir");
+ args.push_back(m_finishDir);
+ }
+ LOG(Info,"Starting main application " + command);
+ if(!m_dryRun)
+ {
+ ProcessUtils::runAsync(command,args);
+ }
+ }
+ else
+ {
+ LOG(Error,"No main binary specified");
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ LOG(Error,"Unable to restart main app " + std::string(ex.what()));
+ }
+}
+
+void UpdateInstaller::postInstallUpdate()
+{
+ // perform post-install actions
+
+#ifdef PLATFORM_MAC
+ // touch the application's bundle directory so that
+ // OS X' Launch Services notices any changes in the application's
+ // Info.plist file.
+ LOG(Info,"Touching " + m_installDir + " to notify OSX of metadata changes.");
+ if(!m_dryRun)
+ {
+ FileUtils::touch(m_installDir.c_str());
+ }
+#endif
+}
+
+void UpdateInstaller::setAutoClose(bool autoClose)
+{
+ m_autoClose = autoClose;
+}
+
+void UpdateInstaller::setDryRun(bool dryRun)
+{
+ m_dryRun = dryRun;
+}
diff --git a/mmc_updater/src/UpdateInstaller.h b/mmc_updater/src/UpdateInstaller.h
new file mode 100644
index 00000000..5920deec
--- /dev/null
+++ b/mmc_updater/src/UpdateInstaller.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "Platform.h"
+#include "FileUtils.h"
+#include "UpdateScript.h"
+
+#include <list>
+#include <string>
+#include <map>
+
+class UpdateObserver;
+
+/** Central class responsible for installing updates,
+ * launching an elevated copy of the updater if required
+ * and restarting the main application once the update
+ * is installed.
+ */
+class UpdateInstaller
+{
+ public:
+ enum Mode
+ {
+ Setup,
+ Main
+ };
+
+ void setInstallDir(const std::string& path);
+ void setPackageDir(const std::string& path);
+ void setBackupDir(const std::string& path);
+ void setMode(Mode mode);
+ void setScript(UpdateScript* script);
+ void setWaitPid(PLATFORM_PID pid);
+ void setForceElevated(bool elevated);
+ void setAutoClose(bool autoClose);
+ void setDryRun(bool dryRun);
+ void setFinishCmd(const std::string& cmd);
+ void setFinishDir(const std::string& dir);
+
+ void setObserver(UpdateObserver* observer);
+
+ void run() throw ();
+
+ void restartMainApp();
+
+ private:
+ void cleanup();
+ void revert();
+ void removeBackups();
+ bool checkAccess();
+
+ void installFiles();
+ void uninstallFiles();
+ void installFile(const UpdateScriptFile& file);
+ void backupFile(const std::string& path);
+ void reportError(const std::string& error);
+ void postInstallUpdate();
+
+ std::list<std::string> updaterArgs() const;
+ std::string friendlyErrorForError(const FileUtils::IOException& ex) const;
+
+ Mode m_mode = Setup;
+ std::string m_installDir;
+ std::string m_packageDir;
+ std::string m_backupDir;
+ std::string m_finishCmd;
+ std::string m_finishDir;
+ PLATFORM_PID m_waitPid = 0;
+ UpdateScript* m_script = nullptr;
+ UpdateObserver* m_observer = nullptr;
+ std::map<std::string,std::string> m_backups;
+ bool m_forceElevated = false;
+ bool m_autoClose = false;
+ bool m_dryRun = false;
+};
diff --git a/mmc_updater/src/UpdateMessage.h b/mmc_updater/src/UpdateMessage.h
new file mode 100644
index 00000000..fee51ab8
--- /dev/null
+++ b/mmc_updater/src/UpdateMessage.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <string>
+
+/** UpdateMessage stores information for a message
+ * about the status of update installation sent
+ * between threads.
+ */
+class UpdateMessage
+{
+ public:
+ enum Type
+ {
+ UpdateFailed,
+ UpdateProgress,
+ UpdateFinished
+ };
+
+ UpdateMessage(void* receiver, Type type)
+ {
+ init(receiver,type);
+ }
+
+ UpdateMessage(Type type)
+ {
+ init(0,type);
+ }
+
+ void* receiver;
+ Type type;
+ std::string message;
+ int progress;
+
+ private:
+ void init(void* receiver, Type type)
+ {
+ this->progress = 0;
+ this->receiver = receiver;
+ this->type = type;
+ }
+};
+
diff --git a/mmc_updater/src/UpdateObserver.h b/mmc_updater/src/UpdateObserver.h
new file mode 100644
index 00000000..84d47325
--- /dev/null
+++ b/mmc_updater/src/UpdateObserver.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <string>
+
+/** Base class for observers of update installation status.
+ * See UpdateInstaller::setObserver()
+ */
+class UpdateObserver
+{
+ public:
+ virtual void updateError(const std::string& errorMessage) = 0;
+ virtual void updateProgress(int percentage) = 0;
+ virtual void updateFinished() = 0;
+};
+
diff --git a/mmc_updater/src/UpdateScript.cpp b/mmc_updater/src/UpdateScript.cpp
new file mode 100644
index 00000000..849a2217
--- /dev/null
+++ b/mmc_updater/src/UpdateScript.cpp
@@ -0,0 +1,99 @@
+#include "UpdateScript.h"
+
+#include "Log.h"
+#include "StringUtils.h"
+
+#include "tinyxml/tinyxml.h"
+
+std::string elementText(const TiXmlElement* element)
+{
+ if (!element)
+ {
+ return std::string();
+ }
+ return element->GetText();
+}
+
+UpdateScript::UpdateScript()
+{
+}
+
+void UpdateScript::parse(const std::string& path)
+{
+ m_path.clear();
+
+ TiXmlDocument document(path);
+ if (document.LoadFile())
+ {
+ m_path = path;
+
+ LOG(Info,"Loaded script from " + path);
+
+ const TiXmlElement* updateNode = document.RootElement();
+ parseUpdate(updateNode);
+ }
+ else
+ {
+ LOG(Error,"Unable to load script " + path);
+ }
+}
+
+bool UpdateScript::isValid() const
+{
+ return !m_path.empty();
+}
+
+void UpdateScript::parseUpdate(const TiXmlElement* updateNode)
+{
+ const TiXmlElement* installNode = updateNode->FirstChildElement("install");
+ if (installNode)
+ {
+ const TiXmlElement* installFileNode = installNode->FirstChildElement("file");
+ while (installFileNode)
+ {
+ m_filesToInstall.push_back(parseFile(installFileNode));
+ installFileNode = installFileNode->NextSiblingElement("file");
+ }
+ }
+
+ const TiXmlElement* uninstallNode = updateNode->FirstChildElement("uninstall");
+ if (uninstallNode)
+ {
+ const TiXmlElement* uninstallFileNode = uninstallNode->FirstChildElement("file");
+ while (uninstallFileNode)
+ {
+ m_filesToUninstall.push_back(uninstallFileNode->GetText());
+ uninstallFileNode = uninstallFileNode->NextSiblingElement("file");
+ }
+ }
+}
+
+UpdateScriptFile UpdateScript::parseFile(const TiXmlElement* element)
+{
+ UpdateScriptFile file;
+ // The name within the update files folder.
+ file.source = elementText(element->FirstChildElement("source"));
+ // The path to install to.
+ file.dest = elementText(element->FirstChildElement("dest"));
+
+ std::string modeString = elementText(element->FirstChildElement("mode"));
+ sscanf(modeString.c_str(),"%i",&file.permissions);
+
+ return file;
+}
+
+const std::vector<UpdateScriptFile>& UpdateScript::filesToInstall() const
+{
+ return m_filesToInstall;
+}
+
+const std::vector<std::string>& UpdateScript::filesToUninstall() const
+{
+ return m_filesToUninstall;
+}
+
+const std::string UpdateScript::path() const
+{
+ return m_path;
+}
+
diff --git a/mmc_updater/src/UpdateScript.h b/mmc_updater/src/UpdateScript.h
new file mode 100644
index 00000000..f55c7236
--- /dev/null
+++ b/mmc_updater/src/UpdateScript.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+class TiXmlElement;
+
+/** Represents a package containing one or more
+ * files for an update.
+ */
+class UpdateScriptPackage
+{
+ public:
+ UpdateScriptPackage()
+ : size(0)
+ {}
+
+ std::string name;
+ std::string sha1;
+ std::string source;
+ int size;
+
+ bool operator==(const UpdateScriptPackage& other) const
+ {
+ return name == other.name &&
+ sha1 == other.sha1 &&
+ source == other.source &&
+ size == other.size;
+ }
+};
+
+/** Represents a file to be installed as part of an update. */
+class UpdateScriptFile
+{
+ public:
+ UpdateScriptFile()
+ : permissions(0)
+ {}
+
+ /// Path to copy from.
+ std::string source;
+ /// The path to copy to.
+ std::string dest;
+
+ /** The permissions for this file, specified
+ * using the standard Unix mode_t values.
+ */
+ int permissions;
+
+ bool operator==(const UpdateScriptFile& other) const
+ {
+ return source == other.source &&
+ dest == other.dest &&
+ permissions == other.permissions;
+ }
+};
+
+/** Stores information about the files included in an update, parsed from an XML file. */
+class UpdateScript
+{
+ public:
+ UpdateScript();
+
+ /** Initialize this UpdateScript with the script stored
+ * in the XML file at @p path.
+ */
+ void parse(const std::string& path);
+
+ bool isValid() const;
+ const std::string path() const;
+ const std::vector<UpdateScriptFile>& filesToInstall() const;
+ const std::vector<std::string>& filesToUninstall() const;
+
+ private:
+ void parseUpdate(const TiXmlElement* element);
+ UpdateScriptFile parseFile(const TiXmlElement* element);
+
+ std::string m_path;
+ std::vector<UpdateScriptFile> m_filesToInstall;
+ std::vector<std::string> m_filesToUninstall;
+};
+
diff --git a/mmc_updater/src/UpdaterOptions.cpp b/mmc_updater/src/UpdaterOptions.cpp
new file mode 100644
index 00000000..abc7c6d7
--- /dev/null
+++ b/mmc_updater/src/UpdaterOptions.cpp
@@ -0,0 +1,87 @@
+#include "UpdaterOptions.h"
+
+#include "Log.h"
+#include "AnyOption/anyoption.h"
+#include "FileUtils.h"
+#include "Platform.h"
+#include "StringUtils.h"
+
+#include <cstdlib>
+#include <iostream>
+
+UpdaterOptions::UpdaterOptions()
+: mode(UpdateInstaller::Setup)
+, waitPid(0)
+, showVersion(false)
+, forceElevated(false)
+, autoClose(false)
+{
+}
+
+UpdateInstaller::Mode stringToMode(const std::string& modeStr)
+{
+ if (modeStr == "main")
+ {
+ return UpdateInstaller::Main;
+ }
+ else
+ {
+ if (!modeStr.empty())
+ {
+ LOG(Error,"Unknown mode " + modeStr);
+ }
+ return UpdateInstaller::Setup;
+ }
+}
+
+void UpdaterOptions::parse(int argc, char** argv)
+{
+ AnyOption parser;
+ parser.setOption("install-dir");
+ parser.setOption("package-dir");
+ parser.setOption("finish-cmd");
+ parser.setOption("finish-dir");
+ parser.setOption("script");
+ parser.setOption("wait");
+ parser.setOption("mode");
+ parser.setFlag("version");
+ parser.setFlag("force-elevated");
+ parser.setFlag("dry-run");
+ parser.setFlag("auto-close");
+
+ parser.processCommandArgs(argc,argv);
+
+ if (parser.getValue("mode"))
+ {
+ mode = stringToMode(parser.getValue("mode"));
+ }
+ if (parser.getValue("install-dir"))
+ {
+ installDir = parser.getValue("install-dir");
+ }
+ if (parser.getValue("package-dir"))
+ {
+ packageDir = parser.getValue("package-dir");
+ }
+ if (parser.getValue("script"))
+ {
+ scriptPath = parser.getValue("script");
+ }
+ if (parser.getValue("wait"))
+ {
+ waitPid = static_cast<PLATFORM_PID>(atoll(parser.getValue("wait")));
+ }
+ if (parser.getValue("finish-cmd"))
+ {
+ finishCmd = parser.getValue("finish-cmd");
+ }
+ if (parser.getValue("finish-dir"))
+ {
+ finishDir = parser.getValue("finish-dir");
+ }
+
+ showVersion = parser.getFlag("version");
+ forceElevated = parser.getFlag("force-elevated");
+ dryRun = parser.getFlag("dry-run");
+ autoClose = parser.getFlag("auto-close");
+}
diff --git a/mmc_updater/src/UpdaterOptions.h b/mmc_updater/src/UpdaterOptions.h
new file mode 100644
index 00000000..d4345490
--- /dev/null
+++ b/mmc_updater/src/UpdaterOptions.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "UpdateInstaller.h"
+
+/** Parses the command-line options to the updater binary. */
+class UpdaterOptions
+{
+ public:
+ UpdaterOptions();
+
+ void parse(int argc, char** argv);
+
+ UpdateInstaller::Mode mode;
+ std::string installDir;
+ std::string packageDir;
+ std::string scriptPath;
+ std::string finishCmd;
+ std::string finishDir;
+ PLATFORM_PID waitPid;
+ std::string logFile;
+ bool showVersion;
+ bool dryRun;
+ bool forceElevated;
+ bool autoClose;
+};
+
diff --git a/mmc_updater/src/main.cpp b/mmc_updater/src/main.cpp
new file mode 100644
index 00000000..602c30a6
--- /dev/null
+++ b/mmc_updater/src/main.cpp
@@ -0,0 +1,206 @@
+#include "AppInfo.h"
+#include "FileUtils.h"
+#include "Log.h"
+#include "Platform.h"
+#include "ProcessUtils.h"
+#include "StringUtils.h"
+#include "UpdateScript.h"
+#include "UpdaterOptions.h"
+
+#include <thread>
+
+#if defined(PLATFORM_LINUX)
+ #include "UpdateDialogGtkFactory.h"
+ #include "UpdateDialogAscii.h"
+#endif
+
+#if defined(PLATFORM_MAC)
+ #include "MacBundle.h"
+ #include "UpdateDialogCocoa.h"
+#endif
+
+#if defined(PLATFORM_WINDOWS)
+ #include "UpdateDialogWin32.h"
+#endif
+
+#include <iostream>
+#include <memory>
+
+#define UPDATER_VERSION "0.16"
+
+UpdateDialog* createUpdateDialog();
+
+void runUpdaterThread(void* arg)
+{
+#ifdef PLATFORM_MAC
+ // create an autorelease pool to free any temporary objects
+ // created by Cocoa whilst handling notifications from the UpdateInstaller
+ void* pool = UpdateDialogCocoa::createAutoreleasePool();
+#endif
+
+ try
+ {
+ UpdateInstaller* installer = static_cast<UpdateInstaller*>(arg);
+ installer->run();
+ }
+ catch (const std::exception& ex)
+ {
+ LOG(Error,"Unexpected exception " + std::string(ex.what()));
+ }
+
+#ifdef PLATFORM_MAC
+ UpdateDialogCocoa::releaseAutoreleasePool(pool);
+#endif
+}
+
+#ifdef PLATFORM_MAC
+extern unsigned char Info_plist[];
+extern unsigned int Info_plist_len;
+
+extern unsigned char mac_icns[];
+extern unsigned int mac_icns_len;
+
+bool unpackBundle(int argc, char** argv)
+{
+ MacBundle bundle(FileUtils::tempPath(),AppInfo::name());
+ std::string currentExePath = ProcessUtils::currentProcessPath();
+
+ if (currentExePath.find(bundle.bundlePath()) != std::string::npos)
+ {
+ // already running from a bundle
+ return false;
+ }
+ LOG(Info,"Creating bundle " + bundle.bundlePath());
+
+ // create a Mac app bundle
+ std::string plistContent(reinterpret_cast<const char*>(Info_plist),Info_plist_len);
+ std::string iconContent(reinterpret_cast<const char*>(mac_icns),mac_icns_len);
+ bundle.create(plistContent,iconContent,ProcessUtils::currentProcessPath());
+
+ std::list<std::string> args;
+ for (int i = 1; i < argc; i++)
+ {
+ args.push_back(argv[i]);
+ }
+ ProcessUtils::runSync(bundle.executablePath(),args);
+ return true;
+}
+#endif
+
+void setupConsole()
+{
+#ifdef PLATFORM_WINDOWS
+ // see http://stackoverflow.com/questions/587767/how-to-output-to-console-in-c-windows
+ // and http://www.libsdl.org/cgi/docwiki.cgi/FAQ_Console
+ AttachConsole(ATTACH_PARENT_PROCESS);
+ freopen( "CON", "w", stdout );
+ freopen( "CON", "w", stderr );
+#endif
+}
+
+int main(int argc, char** argv)
+{
+#ifdef PLATFORM_MAC
+ void* pool = UpdateDialogCocoa::createAutoreleasePool();
+#endif
+
+ Log::instance()->open(AppInfo::logFilePath());
+
+#ifdef PLATFORM_MAC
+ // when the updater is run for the first time, create a Mac app bundle
+ // and re-launch the application from the bundle. This permits
+ // setting up bundle properties (such as application icon)
+ if (unpackBundle(argc,argv))
+ {
+ return 0;
+ }
+#endif
+
+ UpdaterOptions options;
+ options.parse(argc,argv);
+ if (options.showVersion)
+ {
+ setupConsole();
+ std::cout << "Update installer version " << UPDATER_VERSION << std::endl;
+ return 0;
+ }
+
+ UpdateInstaller installer;
+ UpdateScript script;
+
+ if (!options.scriptPath.empty())
+ {
+ script.parse(FileUtils::makeAbsolute(options.scriptPath.c_str(),options.packageDir.c_str()));
+ }
+
+ LOG(Info,"started updater. install-dir: " + options.installDir
+ + ", package-dir: " + options.packageDir
+ + ", wait-pid: " + intToStr(options.waitPid)
+ + ", script-path: " + options.scriptPath
+ + ", mode: " + intToStr(options.mode)
+ + ", finish-cmd: " + options.finishCmd
+ + ", finish-dir: " + options.finishDir);
+
+ installer.setMode(options.mode);
+ installer.setInstallDir(options.installDir);
+ installer.setPackageDir(options.packageDir);
+ installer.setScript(&script);
+ installer.setWaitPid(options.waitPid);
+ installer.setForceElevated(options.forceElevated);
+ installer.setAutoClose(options.autoClose);
+ installer.setFinishCmd(options.finishCmd);
+ installer.setFinishDir(options.finishDir);
+ installer.setDryRun(options.dryRun);
+
+ if (options.mode == UpdateInstaller::Main)
+ {
+ LOG(Info, "Showing updater UI - auto close? " + intToStr(options.autoClose));
+ std::auto_ptr<UpdateDialog> dialog(createUpdateDialog());
+ dialog->setAutoClose(options.autoClose);
+ dialog->init(argc, argv);
+ installer.setObserver(dialog.get());
+ std::thread updaterThread(runUpdaterThread, &installer);
+ dialog->exec();
+ updaterThread.join();
+ }
+ else
+ {
+ installer.run();
+ }
+
+#ifdef PLATFORM_MAC
+ UpdateDialogCocoa::releaseAutoreleasePool(pool);
+#endif
+
+ return 0;
+}
+
+UpdateDialog* createUpdateDialog()
+{
+#if defined(PLATFORM_WINDOWS)
+ return new UpdateDialogWin32();
+#elif defined(PLATFORM_MAC)
+ return new UpdateDialogCocoa();
+#elif defined(PLATFORM_LINUX)
+ UpdateDialog* dialog = UpdateDialogGtkFactory::createDialog();
+ if (!dialog)
+ {
+ dialog = new UpdateDialogAscii();
+ }
+ return dialog;
+#endif
+}
+
+#ifdef PLATFORM_WINDOWS
+// application entry point under Windows
+int CALLBACK WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow)
+{
+ int argc = 0;
+ char** argv;
+ ProcessUtils::convertWindowsCommandLine(GetCommandLineW(),argc,argv);
+ return main(argc,argv);
+}
+#endif
diff --git a/mmc_updater/src/resources/Info.plist b/mmc_updater/src/resources/Info.plist
new file mode 100644
index 00000000..93e97ccd
--- /dev/null
+++ b/mmc_updater/src/resources/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <!-- Note - The name of the application specified here must match the value
+ returned by AppInfo::name()
+ !-->
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>MultiMC Updater</string>
+ <key>CFBundleIconFile</key>
+ <string>MultiMC Updater.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.multimc.MultiMCUpdater</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.5</string>
+ <key>LSMinimumSystemVersionByArchitecture</key>
+ <dict>
+ <key>i386</key>
+ <string>10.5.0</string>
+ <key>x86_64</key>
+ <string>10.5.0</string>
+ </dict>
+</dict>
+</plist>
diff --git a/mmc_updater/src/resources/icon128.png b/mmc_updater/src/resources/icon128.png
new file mode 100644
index 00000000..324452aa
--- /dev/null
+++ b/mmc_updater/src/resources/icon128.png
Binary files differ
diff --git a/mmc_updater/src/resources/icon64.png b/mmc_updater/src/resources/icon64.png
new file mode 100644
index 00000000..5e3373e2
--- /dev/null
+++ b/mmc_updater/src/resources/icon64.png
Binary files differ
diff --git a/mmc_updater/src/resources/mac.icns b/mmc_updater/src/resources/mac.icns
new file mode 100644
index 00000000..7c8fa2ef
--- /dev/null
+++ b/mmc_updater/src/resources/mac.icns
Binary files differ
diff --git a/mmc_updater/src/resources/updater.ico b/mmc_updater/src/resources/updater.ico
new file mode 100644
index 00000000..b011bac9
--- /dev/null
+++ b/mmc_updater/src/resources/updater.ico
Binary files differ
diff --git a/mmc_updater/src/resources/updater.manifest b/mmc_updater/src/resources/updater.manifest
new file mode 100644
index 00000000..cafc47d3
--- /dev/null
+++ b/mmc_updater/src/resources/updater.manifest
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity name="MultiMC.Updater.1" type="win32" version="1.0.0.0" />
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
+ </dependentAssembly>
+ </dependency>
+ <description>Software updater for MultiMC.</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!--The ID below indicates app support for Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!--The ID below indicates app support for Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly> \ No newline at end of file
diff --git a/mmc_updater/src/resources/updater.rc b/mmc_updater/src/resources/updater.rc
new file mode 100644
index 00000000..9c7c5711
--- /dev/null
+++ b/mmc_updater/src/resources/updater.rc
@@ -0,0 +1,30 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+IDI_APPICON ICON DISCARDABLE "updater.ico"
+
+1 RT_MANIFEST "updater.manifest"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 1,0,0,0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "FileDescription", "Software Update Tool"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "ProductName", "MultiMC Software Updater"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04b0 // Unicode
+ END
+END \ No newline at end of file
diff --git a/mmc_updater/src/tests/CMakeLists.txt b/mmc_updater/src/tests/CMakeLists.txt
new file mode 100644
index 00000000..08501a98
--- /dev/null
+++ b/mmc_updater/src/tests/CMakeLists.txt
@@ -0,0 +1,47 @@
+
+include_directories("${CMAKE_CURRENT_SOURCE_DIR}/..")
+
+if (APPLE)
+ set(HELPER_SHARED_SOURCES ../StlSymbolsLeopard.cpp)
+endif()
+
+# # Create helper binaries for unit tests
+# add_executable(oldapp
+# old_app.cpp
+# ${HELPER_SHARED_SOURCES}
+# )
+# add_executable(newapp
+# new_app.cpp
+# ${HELPER_SHARED_SOURCES}
+# )
+
+# Install data files required by unit tests
+set(TEST_FILES
+ file_list.xml
+)
+
+foreach(TEST_FILE ${TEST_FILES})
+ execute_process(
+ COMMAND
+ "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}"
+ )
+endforeach()
+
+# Add unit test binaries
+macro(ADD_UPDATER_TEST CLASS)
+ set(TEST_TARGET updater_${CLASS})
+ unset(srcs)
+ list(APPEND srcs ${CLASS}.cpp)
+ if (WIN32)
+ list(APPEND srcs ${CMAKE_CURRENT_SOURCE_DIR}/test.rc)
+ endif()
+ add_executable(${TEST_TARGET} ${srcs})
+ target_link_libraries(${TEST_TARGET} updatershared)
+ add_test(NAME ${TEST_TARGET} COMMAND ${TEST_TARGET})
+ if (APPLE)
+ set_target_properties(${TEST_TARGET} PROPERTIES LINK_FLAGS "-framework Security -framework Cocoa")
+ endif()
+endmacro()
+
+add_updater_test(TestParseScript)
+add_updater_test(TestFileUtils)
diff --git a/mmc_updater/src/tests/TestFileUtils.cpp b/mmc_updater/src/tests/TestFileUtils.cpp
new file mode 100644
index 00000000..f8535a28
--- /dev/null
+++ b/mmc_updater/src/tests/TestFileUtils.cpp
@@ -0,0 +1,79 @@
+#include "TestFileUtils.h"
+
+#include "FileUtils.h"
+#include "TestUtils.h"
+
+void TestFileUtils::testDirName()
+{
+ std::string dirName;
+ std::string fileName;
+
+#ifdef PLATFORM_WINDOWS
+ // absolute paths
+ dirName = FileUtils::dirname("E:/Some Dir/App.exe");
+ TEST_COMPARE(dirName,"E:/Some Dir");
+ fileName = FileUtils::fileName("E:/Some Dir/App.exe");
+ TEST_COMPARE(fileName,"App.exe");
+
+ dirName = FileUtils::dirname("C:/Users/kitteh/AppData/Local/Temp/MultiMC5-yidaaa/MultiMC.exe");
+ TEST_COMPARE(dirName,"C:/Users/kitteh/AppData/Local/Temp/MultiMC5-yidaaa");
+ fileName = FileUtils::fileName("C:/Users/kitteh/AppData/Local/Temp/MultiMC5-yidaaa/MultiMC.exe");
+ TEST_COMPARE(fileName,"MultiMC.exe");
+
+#else
+ // absolute paths
+ dirName = FileUtils::dirname("/home/tester/foo bar/baz");
+ TEST_COMPARE(dirName,"/home/tester/foo bar");
+ fileName = FileUtils::fileName("/home/tester/foo bar/baz");
+ TEST_COMPARE(fileName,"baz");
+#endif
+ // current directory
+ dirName = FileUtils::dirname("App.exe");
+ TEST_COMPARE(dirName,".");
+ fileName = FileUtils::fileName("App.exe");
+ TEST_COMPARE(fileName,"App.exe");
+
+ // relative paths
+ dirName = FileUtils::dirname("Foo/App.exe");
+ TEST_COMPARE(dirName,"Foo");
+ fileName = FileUtils::fileName("Foo/App.exe");
+ TEST_COMPARE(fileName,"App.exe");
+}
+
+void TestFileUtils::testIsRelative()
+{
+#ifdef PLATFORM_WINDOWS
+ TEST_COMPARE(FileUtils::isRelative("temp"),true);
+ TEST_COMPARE(FileUtils::isRelative("D:/temp"),false);
+ TEST_COMPARE(FileUtils::isRelative("d:/temp"),false);
+#else
+ TEST_COMPARE(FileUtils::isRelative("/tmp"),false);
+ TEST_COMPARE(FileUtils::isRelative("tmp"),true);
+#endif
+}
+
+void TestFileUtils::testSymlinkFileExists()
+{
+#ifdef PLATFORM_UNIX
+ const char* linkName = "link-name";
+ FileUtils::removeFile(linkName);
+ FileUtils::createSymLink(linkName, "target-that-does-not-exist");
+ TEST_COMPARE(FileUtils::fileExists(linkName), true);
+#endif
+}
+
+void TestFileUtils::testStandardDirs()
+{
+ std::string tmpDir = FileUtils::tempPath();
+ TEST_COMPARE(FileUtils::fileExists(tmpDir.data()), true);
+}
+
+int main(int,char**)
+{
+ TestList<TestFileUtils> tests;
+ tests.addTest(&TestFileUtils::testDirName);
+ tests.addTest(&TestFileUtils::testIsRelative);
+ tests.addTest(&TestFileUtils::testSymlinkFileExists);
+ tests.addTest(&TestFileUtils::testStandardDirs);
+ return TestUtils::runTest(tests);
+}
diff --git a/mmc_updater/src/tests/TestFileUtils.h b/mmc_updater/src/tests/TestFileUtils.h
new file mode 100644
index 00000000..1a45164b
--- /dev/null
+++ b/mmc_updater/src/tests/TestFileUtils.h
@@ -0,0 +1,10 @@
+#pragma once
+
+class TestFileUtils
+{
+ public:
+ void testDirName();
+ void testIsRelative();
+ void testSymlinkFileExists();
+ void testStandardDirs();
+};
diff --git a/mmc_updater/src/tests/TestParseScript.cpp b/mmc_updater/src/tests/TestParseScript.cpp
new file mode 100644
index 00000000..f4453957
--- /dev/null
+++ b/mmc_updater/src/tests/TestParseScript.cpp
@@ -0,0 +1,24 @@
+#include "TestParseScript.h"
+
+#include "TestUtils.h"
+#include "UpdateScript.h"
+
+#include <iostream>
+#include <algorithm>
+
+void TestParseScript::testParse()
+{
+ UpdateScript script;
+
+ script.parse("file_list.xml");
+
+ TEST_COMPARE(script.isValid(),true);
+}
+
+int main(int,char**)
+{
+ TestList<TestParseScript> tests;
+ tests.addTest(&TestParseScript::testParse);
+ return TestUtils::runTest(tests);
+}
+
diff --git a/mmc_updater/src/tests/TestParseScript.h b/mmc_updater/src/tests/TestParseScript.h
new file mode 100644
index 00000000..528e97a8
--- /dev/null
+++ b/mmc_updater/src/tests/TestParseScript.h
@@ -0,0 +1,8 @@
+#pragma once
+
+class TestParseScript
+{
+ public:
+ void testParse();
+};
+
diff --git a/mmc_updater/src/tests/TestUtils.h b/mmc_updater/src/tests/TestUtils.h
new file mode 100644
index 00000000..68d97da5
--- /dev/null
+++ b/mmc_updater/src/tests/TestUtils.h
@@ -0,0 +1,108 @@
+#pragma once
+
+#include <iostream>
+#include <functional>
+#include <string>
+#include <vector>
+
+template <class T>
+class TestList
+{
+ public:
+ void addTest(void (T::*test)())
+ {
+ m_tests.push_back(std::mem_fun(test));
+ }
+
+ int size() const
+ {
+ return m_tests.size();
+ }
+
+ void runTest(T* testInstance, int i)
+ {
+ m_tests.at(i)(testInstance);
+ }
+
+ private:
+ std::vector<std::mem_fun_t<void,T> > m_tests;
+};
+
+class TestUtils
+{
+ public:
+ template <class X, class Y>
+ static void compare(const X& x, const Y& y, const char* xString, const char* yString)
+ {
+ if (x != y)
+ {
+ throw "Actual and expected values differ. "
+ "Actual: " + toString(x,xString) +
+ " Expected: " + toString(y,yString);
+ }
+ }
+
+ template <typename T>
+ static std::string toString(T value, const char* context)
+ {
+ return "Unprintable: " + std::string(context);
+ }
+
+ template <class T>
+ static int runTest(class TestList<T>& tests) throw ()
+ {
+ std::string errorText;
+ try
+ {
+ T testInstance;
+ for (int i=0; i < tests.size(); i++)
+ {
+ tests.runTest(&testInstance,i);
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ errorText = ex.what();
+ }
+ catch (const std::string& error)
+ {
+ errorText = error;
+ }
+ catch (...)
+ {
+ errorText = "Unknown exception";
+ }
+
+ if (errorText.empty())
+ {
+ std::cout << "Test passed" << std::endl;
+ return 0;
+ }
+ else
+ {
+ std::cout << "Test failed: " << errorText << std::endl;
+ return 1;
+ }
+ }
+};
+
+template <>
+inline std::string TestUtils::toString(const std::string& value, const char*)
+{
+ return value;
+}
+template <>
+inline std::string TestUtils::toString(std::string value, const char*)
+{
+ return value;
+}
+template <>
+inline std::string TestUtils::toString(const char* value, const char*)
+{
+ return value;
+}
+
+#define TEST_COMPARE(x,y) \
+ TestUtils::compare(x,y,#x,#y);
+
+
diff --git a/mmc_updater/src/tests/file_list.xml b/mmc_updater/src/tests/file_list.xml
new file mode 100644
index 00000000..06ba501d
--- /dev/null
+++ b/mmc_updater/src/tests/file_list.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<update version="3">
+ <install>
+ <file>
+ <name>$APP_FILENAME</name>
+ <hash>$UPDATED_APP_HASH</hash>
+ <size>$UPDATED_APP_SIZE</size>
+ <permissions>0755</permissions>
+ <package>app-pkg</package>
+ <is-main-binary>true</is-main-binary>
+ </file>
+ <file>
+ <name>$UPDATER_FILENAME</name>
+ <hash>$UPDATER_HASH</hash>
+ <size>$UPDATER_SIZE</size>
+ <permissions>0755</permissions>
+ </file>
+ <!-- Test symlink !-->
+ <file>
+ <name>test-dir/app-symlink</name>
+ <target>../app</target>
+ </file>
+ <!-- Test file in new directory !-->
+ <file>
+ <name>new-dir/new-dir2/new-file.txt</name>
+ <hash>$TEST_FILENAME</hash>
+ <size>$TEST_SIZE</size>
+ <package>app-pkg</package>
+ <permissions>0644</permissions>
+ </file>
+ </install>
+ <uninstall>
+ <!-- TODO - List some files to uninstall here !-->
+ <file>file-to-uninstall.txt</file>
+ <file>symlink-to-file-to-uninstall.txt</file>
+ </uninstall>
+</update>
diff --git a/mmc_updater/src/tests/new_app.cpp b/mmc_updater/src/tests/new_app.cpp
new file mode 100644
index 00000000..7fad1380
--- /dev/null
+++ b/mmc_updater/src/tests/new_app.cpp
@@ -0,0 +1,8 @@
+#include <iostream>
+
+int main(int,char**)
+{
+ std::cout << "new app starting" << std::endl;
+ return 0;
+}
+
diff --git a/mmc_updater/src/tests/old_app.cpp b/mmc_updater/src/tests/old_app.cpp
new file mode 100644
index 00000000..476a3405
--- /dev/null
+++ b/mmc_updater/src/tests/old_app.cpp
@@ -0,0 +1,7 @@
+#include <iostream>
+
+int main(int,char**)
+{
+ std::cout << "old app starting" << std::endl;
+ return 0;
+}
diff --git a/mmc_updater/src/tests/test.manifest b/mmc_updater/src/tests/test.manifest
new file mode 100644
index 00000000..8b4dbb98
--- /dev/null
+++ b/mmc_updater/src/tests/test.manifest
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity name="MultiMC.Test.0" type="win32" version="5.0.0.0" />
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
+ </dependentAssembly>
+ </dependency>
+ <description>Custom Minecraft launcher for managing multiple installs.</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!--The ID below indicates app support for Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!--The ID below indicates app support for Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly> \ No newline at end of file
diff --git a/mmc_updater/src/tests/test.rc b/mmc_updater/src/tests/test.rc
new file mode 100644
index 00000000..a288dba6
--- /dev/null
+++ b/mmc_updater/src/tests/test.rc
@@ -0,0 +1,28 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+1 RT_MANIFEST "test.manifest"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 1,0,0,0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "FileDescription", "Testcase"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "ProductName", "MultiMC Testcase"
+ VALUE "ProductVersion", "5"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04b0 // Unicode
+ END
+END
diff --git a/package/linux/MultiMC b/package/linux/MultiMC
new file mode 100755
index 00000000..3579913c
--- /dev/null
+++ b/package/linux/MultiMC
@@ -0,0 +1,77 @@
+#!/bin/bash
+# Basic start script for running MultiMC with the libs packaged with it.
+
+function printerror {
+ printf "$1"
+ if which zenity >/dev/null; then zenity --error --text="$1" &>/dev/null;
+ elif which kdialog >/dev/null; then kdialog --error "$1" &>/dev/null;
+ fi
+}
+
+if [[ $EUID -eq 0 ]]; then
+ printerror "This program should not be run using sudo or as the root user!\n"
+ exit 1
+fi
+
+
+MMC_DIR="$(dirname "$(readlink -f "$0")")"
+echo "MultiMC Dir: ${MMC_DIR}"
+
+# Set up env
+export LD_LIBRARY_PATH="${MMC_DIR}/bin":$LD_LIBRARY_PATH
+export QT_PLUGIN_PATH="${MMC_DIR}/plugins"
+export QT_FONTPATH="${MMC_DIR}/fonts"
+
+# Detect missing dependencies...
+DEPS_LIST=`ldd "${MMC_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | awk -vORS=", " '{ print $1 }'`
+if [ "x$DEPS_LIST" = "x" ]; then
+ # We have all our dependencies. Run MultiMC.
+ echo "No missing dependencies found."
+
+ # Just to be sure...
+ chmod +x "${MMC_DIR}/bin/MultiMC"
+
+ # Run MultiMC
+ "${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" $@
+
+ # Exit with MultiMC's exit code.
+ exit $?
+else
+ # apt
+ if which apt-file &>/dev/null; then
+ LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
+ COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do apt-file -l search $LIBRARY; done`
+ COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
+ INSTALL_CMD="sudo apt-get install $COMMAND_LIBS"
+ # pacman
+ elif which pkgfile &>/dev/null; then
+ LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
+ COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pkgfile $LIBRARY; done`
+ COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
+ INSTALL_CMD="sudo pacman -S $COMMAND_LIBS"
+ # yum
+ elif which yum &>/dev/null; then
+ LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
+ COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do yum whatprovides $LIBRARY; done`
+ COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
+ INSTALL_CMD="sudo yum install $COMMAND_LIBS"
+ # zypper
+ elif which zypper &>/dev/null; then
+ LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
+ COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do zypper wp $LIBRARY; done`
+ COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
+ INSTALL_CMD="sudo zypper install $COMMAND_LIBS"
+ # emerge
+ elif which pfl &>/dev/null; then
+ LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
+ COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pfl $LIBRARY; done`
+ COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
+ INSTALL_CMD="sudo emerge $COMMAND_LIBS"
+ fi
+
+ MESSAGE="Error: MultiMC is missing the following libraries that it needs to work correctly:\n\t${DEPS_LIST}\nPlease install them from your distribution's package manager."
+ MESSAGE="$MESSAGE\n\nHint: $INSTALL_CMD\n"
+
+ printerror "$MESSAGE"
+ exit 1
+fi
diff --git a/resources/MultiMC.icns b/resources/MultiMC.icns
new file mode 100644
index 00000000..f96fd5a4
--- /dev/null
+++ b/resources/MultiMC.icns
Binary files differ
diff --git a/resources/MultiMC.ico b/resources/MultiMC.ico
new file mode 100644
index 00000000..734af0fb
--- /dev/null
+++ b/resources/MultiMC.ico
Binary files differ
diff --git a/resources/MultiMC.manifest b/resources/MultiMC.manifest
new file mode 100644
index 00000000..3acf8f7f
--- /dev/null
+++ b/resources/MultiMC.manifest
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity name="MultiMC.Application.5" type="win32" version="5.0.0.0" />
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
+ </dependentAssembly>
+ </dependency>
+ <description>Custom Minecraft launcher for managing multiple installs.</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!--The ID below indicates app support for Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!--The ID below indicates app support for Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly> \ No newline at end of file
diff --git a/resources/backgrounds/backgrounds.qrc b/resources/backgrounds/backgrounds.qrc
new file mode 100644
index 00000000..55de139e
--- /dev/null
+++ b/resources/backgrounds/backgrounds.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource prefix="/backgrounds">
+ <file alias="kitteh">catbgrnd2.png</file>
+ </qresource>
+</RCC>
diff --git a/resources/backgrounds/catbgrnd2.png b/resources/backgrounds/catbgrnd2.png
new file mode 100644
index 00000000..2b949e0b
--- /dev/null
+++ b/resources/backgrounds/catbgrnd2.png
Binary files differ
diff --git a/resources/instances/brick.png b/resources/instances/brick.png
new file mode 100644
index 00000000..0b534366
--- /dev/null
+++ b/resources/instances/brick.png
Binary files differ
diff --git a/resources/instances/chicken.png b/resources/instances/chicken.png
new file mode 100644
index 00000000..f870467a
--- /dev/null
+++ b/resources/instances/chicken.png
Binary files differ
diff --git a/resources/instances/chicken128.png b/resources/instances/chicken128.png
new file mode 100644
index 00000000..71f6dedc
--- /dev/null
+++ b/resources/instances/chicken128.png
Binary files differ
diff --git a/resources/instances/creeper.png b/resources/instances/creeper.png
new file mode 100644
index 00000000..a67ecfc3
--- /dev/null
+++ b/resources/instances/creeper.png
Binary files differ
diff --git a/resources/instances/creeper128.png b/resources/instances/creeper128.png
new file mode 100644
index 00000000..41b7d07d
--- /dev/null
+++ b/resources/instances/creeper128.png
Binary files differ
diff --git a/resources/instances/derp.png b/resources/instances/derp.png
new file mode 100644
index 00000000..4c361942
--- /dev/null
+++ b/resources/instances/derp.png
Binary files differ
diff --git a/resources/instances/diamond.png b/resources/instances/diamond.png
new file mode 100644
index 00000000..376ab901
--- /dev/null
+++ b/resources/instances/diamond.png
Binary files differ
diff --git a/resources/instances/dirt.png b/resources/instances/dirt.png
new file mode 100644
index 00000000..9e19eb8f
--- /dev/null
+++ b/resources/instances/dirt.png
Binary files differ
diff --git a/resources/instances/enderman.png b/resources/instances/enderman.png
new file mode 100644
index 00000000..9f3a72b3
--- /dev/null
+++ b/resources/instances/enderman.png
Binary files differ
diff --git a/resources/instances/enderpearl.png b/resources/instances/enderpearl.png
new file mode 100644
index 00000000..a818eb8e
--- /dev/null
+++ b/resources/instances/enderpearl.png
Binary files differ
diff --git a/resources/instances/enderpearl128.png b/resources/instances/enderpearl128.png
new file mode 100644
index 00000000..0a5bf91a
--- /dev/null
+++ b/resources/instances/enderpearl128.png
Binary files differ
diff --git a/resources/instances/ftb_glow.png b/resources/instances/ftb_glow.png
new file mode 100644
index 00000000..c4e6fd5d
--- /dev/null
+++ b/resources/instances/ftb_glow.png
Binary files differ
diff --git a/resources/instances/ftb_glow128.png b/resources/instances/ftb_glow128.png
new file mode 100644
index 00000000..86632b21
--- /dev/null
+++ b/resources/instances/ftb_glow128.png
Binary files differ
diff --git a/resources/instances/ftb_logo.png b/resources/instances/ftb_logo.png
new file mode 100644
index 00000000..20df7171
--- /dev/null
+++ b/resources/instances/ftb_logo.png
Binary files differ
diff --git a/resources/instances/ftb_logo128.png b/resources/instances/ftb_logo128.png
new file mode 100644
index 00000000..e725b7fe
--- /dev/null
+++ b/resources/instances/ftb_logo128.png
Binary files differ
diff --git a/resources/instances/gear.png b/resources/instances/gear.png
new file mode 100644
index 00000000..da9ba2f9
--- /dev/null
+++ b/resources/instances/gear.png
Binary files differ
diff --git a/resources/instances/gear128.png b/resources/instances/gear128.png
new file mode 100644
index 00000000..75c68a66
--- /dev/null
+++ b/resources/instances/gear128.png
Binary files differ
diff --git a/resources/instances/gold.png b/resources/instances/gold.png
new file mode 100644
index 00000000..9bedda16
--- /dev/null
+++ b/resources/instances/gold.png
Binary files differ
diff --git a/resources/instances/grass.png b/resources/instances/grass.png
new file mode 100644
index 00000000..f1694547
--- /dev/null
+++ b/resources/instances/grass.png
Binary files differ
diff --git a/resources/instances/herobrine.png b/resources/instances/herobrine.png
new file mode 100644
index 00000000..e5460da3
--- /dev/null
+++ b/resources/instances/herobrine.png
Binary files differ
diff --git a/resources/instances/herobrine128.png b/resources/instances/herobrine128.png
new file mode 100644
index 00000000..13f1494c
--- /dev/null
+++ b/resources/instances/herobrine128.png
Binary files differ
diff --git a/resources/instances/infinity.png b/resources/instances/infinity.png
new file mode 100644
index 00000000..bd94a3dc
--- /dev/null
+++ b/resources/instances/infinity.png
Binary files differ
diff --git a/resources/instances/infinity128.png b/resources/instances/infinity128.png
new file mode 100644
index 00000000..226847fb
--- /dev/null
+++ b/resources/instances/infinity128.png
Binary files differ
diff --git a/resources/instances/instances.qrc b/resources/instances/instances.qrc
new file mode 100644
index 00000000..ec3cdf21
--- /dev/null
+++ b/resources/instances/instances.qrc
@@ -0,0 +1,35 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource prefix="/icons/instances">
+ <!-- Source: Mojang -->
+ <file alias="brick">brick.png</file>
+ <file alias="diamond">diamond.png</file>
+ <file alias="dirt">dirt.png</file>
+ <file alias="gold">gold.png</file>
+ <file alias="grass">grass.png</file>
+ <file alias="stone">stone.png</file>
+ <file alias="tnt">tnt.png</file>
+ <file alias="iron">iron.png</file>
+ <file alias="planks">planks.png</file>
+
+ <!-- Source: Unknown -->
+ <file alias="derp">derp.png</file>
+ <file alias="enderman">enderman.png</file>
+
+ <!-- Our own. -->
+ <file alias="chicken">chicken128.png</file>
+ <file alias="creeper">creeper128.png</file>
+ <file alias="enderpearl">enderpearl128.png</file>
+ <file alias="ftb-glow">ftb_glow128.png</file>
+ <file alias="ftb-logo">ftb_logo128.png</file>
+ <file alias="gear">gear128.png</file>
+ <file alias="herobrine">herobrine128.png</file>
+ <file alias="infinity">infinity128.png</file>
+ <file alias="magitech">magitech128.png</file>
+ <file alias="meat">meat128.png</file>
+ <file alias="netherstar">netherstar128.png</file>
+ <file alias="skeleton">skeleton128.png</file>
+ <file alias="squarecreeper">squarecreeper128.png</file>
+ <file alias="steve">steve128.png</file>
+ </qresource>
+</RCC>
diff --git a/resources/instances/iron.png b/resources/instances/iron.png
new file mode 100644
index 00000000..28960782
--- /dev/null
+++ b/resources/instances/iron.png
Binary files differ
diff --git a/resources/instances/magitech.png b/resources/instances/magitech.png
new file mode 100644
index 00000000..6fd8ff60
--- /dev/null
+++ b/resources/instances/magitech.png
Binary files differ
diff --git a/resources/instances/magitech128.png b/resources/instances/magitech128.png
new file mode 100644
index 00000000..0f81a199
--- /dev/null
+++ b/resources/instances/magitech128.png
Binary files differ
diff --git a/resources/instances/meat.png b/resources/instances/meat.png
new file mode 100644
index 00000000..6694859d
--- /dev/null
+++ b/resources/instances/meat.png
Binary files differ
diff --git a/resources/instances/meat128.png b/resources/instances/meat128.png
new file mode 100644
index 00000000..fefc9bf1
--- /dev/null
+++ b/resources/instances/meat128.png
Binary files differ
diff --git a/resources/instances/netherstar.png b/resources/instances/netherstar.png
new file mode 100644
index 00000000..43cb5113
--- /dev/null
+++ b/resources/instances/netherstar.png
Binary files differ
diff --git a/resources/instances/netherstar128.png b/resources/instances/netherstar128.png
new file mode 100644
index 00000000..132085f0
--- /dev/null
+++ b/resources/instances/netherstar128.png
Binary files differ
diff --git a/resources/instances/planks.png b/resources/instances/planks.png
new file mode 100644
index 00000000..7fcf8467
--- /dev/null
+++ b/resources/instances/planks.png
Binary files differ
diff --git a/resources/instances/skeleton.png b/resources/instances/skeleton.png
new file mode 100644
index 00000000..0c8d3505
--- /dev/null
+++ b/resources/instances/skeleton.png
Binary files differ
diff --git a/resources/instances/skeleton128.png b/resources/instances/skeleton128.png
new file mode 100644
index 00000000..55fcf5a9
--- /dev/null
+++ b/resources/instances/skeleton128.png
Binary files differ
diff --git a/resources/instances/squarecreeper.png b/resources/instances/squarecreeper.png
new file mode 100644
index 00000000..b78c4ae0
--- /dev/null
+++ b/resources/instances/squarecreeper.png
Binary files differ
diff --git a/resources/instances/squarecreeper128.png b/resources/instances/squarecreeper128.png
new file mode 100644
index 00000000..c82d8406
--- /dev/null
+++ b/resources/instances/squarecreeper128.png
Binary files differ
diff --git a/resources/instances/steve.png b/resources/instances/steve.png
new file mode 100644
index 00000000..07c6acde
--- /dev/null
+++ b/resources/instances/steve.png
Binary files differ
diff --git a/resources/instances/steve128.png b/resources/instances/steve128.png
new file mode 100644
index 00000000..a07cbd2f
--- /dev/null
+++ b/resources/instances/steve128.png
Binary files differ
diff --git a/resources/instances/stone.png b/resources/instances/stone.png
new file mode 100644
index 00000000..34f9a751
--- /dev/null
+++ b/resources/instances/stone.png
Binary files differ
diff --git a/resources/instances/tnt.png b/resources/instances/tnt.png
new file mode 100644
index 00000000..e40d404d
--- /dev/null
+++ b/resources/instances/tnt.png
Binary files differ
diff --git a/resources/multimc.rc b/resources/multimc.rc
new file mode 100644
index 00000000..ca92e2c3
--- /dev/null
+++ b/resources/multimc.rc
@@ -0,0 +1,29 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+IDI_ICON1 ICON DISCARDABLE "MultiMC.ico"
+1 RT_MANIFEST "MultiMC.manifest"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 1,0,0,0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "FileDescription", "Minecraft Launcher"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "ProductName", "MultiMC"
+ VALUE "ProductVersion", "5"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04b0 // Unicode
+ END
+END
diff --git a/resources/multimc/16x16/about.png b/resources/multimc/16x16/about.png
new file mode 100644
index 00000000..a6a986e1
--- /dev/null
+++ b/resources/multimc/16x16/about.png
Binary files differ
diff --git a/resources/multimc/16x16/bug.png b/resources/multimc/16x16/bug.png
new file mode 100644
index 00000000..0c5b78b4
--- /dev/null
+++ b/resources/multimc/16x16/bug.png
Binary files differ
diff --git a/resources/multimc/16x16/cat.png b/resources/multimc/16x16/cat.png
new file mode 100644
index 00000000..e6e31b44
--- /dev/null
+++ b/resources/multimc/16x16/cat.png
Binary files differ
diff --git a/resources/multimc/16x16/centralmods.png b/resources/multimc/16x16/centralmods.png
new file mode 100644
index 00000000..c1b91c76
--- /dev/null
+++ b/resources/multimc/16x16/centralmods.png
Binary files differ
diff --git a/resources/multimc/16x16/checkupdate.png b/resources/multimc/16x16/checkupdate.png
new file mode 100644
index 00000000..f3742058
--- /dev/null
+++ b/resources/multimc/16x16/checkupdate.png
Binary files differ
diff --git a/resources/multimc/16x16/copy.png b/resources/multimc/16x16/copy.png
new file mode 100644
index 00000000..ccaed9e1
--- /dev/null
+++ b/resources/multimc/16x16/copy.png
Binary files differ
diff --git a/resources/multimc/16x16/help.png b/resources/multimc/16x16/help.png
new file mode 100644
index 00000000..e6edf6ba
--- /dev/null
+++ b/resources/multimc/16x16/help.png
Binary files differ
diff --git a/resources/multimc/16x16/new.png b/resources/multimc/16x16/new.png
new file mode 100644
index 00000000..2e56f589
--- /dev/null
+++ b/resources/multimc/16x16/new.png
Binary files differ
diff --git a/resources/multimc/16x16/news.png b/resources/multimc/16x16/news.png
new file mode 100644
index 00000000..872e85db
--- /dev/null
+++ b/resources/multimc/16x16/news.png
Binary files differ
diff --git a/resources/multimc/16x16/noaccount.png b/resources/multimc/16x16/noaccount.png
new file mode 100644
index 00000000..b49bcf36
--- /dev/null
+++ b/resources/multimc/16x16/noaccount.png
Binary files differ
diff --git a/resources/multimc/16x16/refresh.png b/resources/multimc/16x16/refresh.png
new file mode 100644
index 00000000..86b6f82c
--- /dev/null
+++ b/resources/multimc/16x16/refresh.png
Binary files differ
diff --git a/resources/multimc/16x16/settings.png b/resources/multimc/16x16/settings.png
new file mode 100644
index 00000000..b916cd24
--- /dev/null
+++ b/resources/multimc/16x16/settings.png
Binary files differ
diff --git a/resources/multimc/16x16/viewfolder.png b/resources/multimc/16x16/viewfolder.png
new file mode 100644
index 00000000..98b8a944
--- /dev/null
+++ b/resources/multimc/16x16/viewfolder.png
Binary files differ
diff --git a/resources/multimc/22x22/about.png b/resources/multimc/22x22/about.png
new file mode 100644
index 00000000..57775e25
--- /dev/null
+++ b/resources/multimc/22x22/about.png
Binary files differ
diff --git a/resources/multimc/22x22/bug.png b/resources/multimc/22x22/bug.png
new file mode 100644
index 00000000..90481bba
--- /dev/null
+++ b/resources/multimc/22x22/bug.png
Binary files differ
diff --git a/resources/multimc/22x22/cat.png b/resources/multimc/22x22/cat.png
new file mode 100644
index 00000000..3ea7ba69
--- /dev/null
+++ b/resources/multimc/22x22/cat.png
Binary files differ
diff --git a/resources/multimc/22x22/centralmods.png b/resources/multimc/22x22/centralmods.png
new file mode 100644
index 00000000..a10f9a2b
--- /dev/null
+++ b/resources/multimc/22x22/centralmods.png
Binary files differ
diff --git a/resources/multimc/22x22/checkupdate.png b/resources/multimc/22x22/checkupdate.png
new file mode 100644
index 00000000..badb200c
--- /dev/null
+++ b/resources/multimc/22x22/checkupdate.png
Binary files differ
diff --git a/resources/multimc/22x22/copy.png b/resources/multimc/22x22/copy.png
new file mode 100644
index 00000000..ea236a24
--- /dev/null
+++ b/resources/multimc/22x22/copy.png
Binary files differ
diff --git a/resources/multimc/22x22/help.png b/resources/multimc/22x22/help.png
new file mode 100644
index 00000000..da79b3e3
--- /dev/null
+++ b/resources/multimc/22x22/help.png
Binary files differ
diff --git a/resources/multimc/22x22/new.png b/resources/multimc/22x22/new.png
new file mode 100644
index 00000000..c707fbbf
--- /dev/null
+++ b/resources/multimc/22x22/new.png
Binary files differ
diff --git a/resources/multimc/22x22/news.png b/resources/multimc/22x22/news.png
new file mode 100644
index 00000000..1953bf7b
--- /dev/null
+++ b/resources/multimc/22x22/news.png
Binary files differ
diff --git a/resources/multimc/22x22/refresh.png b/resources/multimc/22x22/refresh.png
new file mode 100644
index 00000000..45b5535c
--- /dev/null
+++ b/resources/multimc/22x22/refresh.png
Binary files differ
diff --git a/resources/multimc/22x22/settings.png b/resources/multimc/22x22/settings.png
new file mode 100644
index 00000000..daf56aad
--- /dev/null
+++ b/resources/multimc/22x22/settings.png
Binary files differ
diff --git a/resources/multimc/22x22/viewfolder.png b/resources/multimc/22x22/viewfolder.png
new file mode 100644
index 00000000..b645167f
--- /dev/null
+++ b/resources/multimc/22x22/viewfolder.png
Binary files differ
diff --git a/resources/multimc/24x24/cat.png b/resources/multimc/24x24/cat.png
new file mode 100644
index 00000000..c93245f6
--- /dev/null
+++ b/resources/multimc/24x24/cat.png
Binary files differ
diff --git a/resources/multimc/24x24/noaccount.png b/resources/multimc/24x24/noaccount.png
new file mode 100644
index 00000000..ac12437c
--- /dev/null
+++ b/resources/multimc/24x24/noaccount.png
Binary files differ
diff --git a/resources/multimc/32x32/about.png b/resources/multimc/32x32/about.png
new file mode 100644
index 00000000..5174c4f1
--- /dev/null
+++ b/resources/multimc/32x32/about.png
Binary files differ
diff --git a/resources/multimc/32x32/bug.png b/resources/multimc/32x32/bug.png
new file mode 100644
index 00000000..ada46653
--- /dev/null
+++ b/resources/multimc/32x32/bug.png
Binary files differ
diff --git a/resources/multimc/32x32/cat.png b/resources/multimc/32x32/cat.png
new file mode 100644
index 00000000..78ff98e9
--- /dev/null
+++ b/resources/multimc/32x32/cat.png
Binary files differ
diff --git a/resources/multimc/32x32/centralmods.png b/resources/multimc/32x32/centralmods.png
new file mode 100644
index 00000000..cd2b8208
--- /dev/null
+++ b/resources/multimc/32x32/centralmods.png
Binary files differ
diff --git a/resources/multimc/32x32/checkupdate.png b/resources/multimc/32x32/checkupdate.png
new file mode 100644
index 00000000..754005f9
--- /dev/null
+++ b/resources/multimc/32x32/checkupdate.png
Binary files differ
diff --git a/resources/multimc/32x32/copy.png b/resources/multimc/32x32/copy.png
new file mode 100644
index 00000000..c137b0f1
--- /dev/null
+++ b/resources/multimc/32x32/copy.png
Binary files differ
diff --git a/resources/multimc/32x32/help.png b/resources/multimc/32x32/help.png
new file mode 100644
index 00000000..b3854278
--- /dev/null
+++ b/resources/multimc/32x32/help.png
Binary files differ
diff --git a/resources/multimc/32x32/new.png b/resources/multimc/32x32/new.png
new file mode 100644
index 00000000..a3555ba4
--- /dev/null
+++ b/resources/multimc/32x32/new.png
Binary files differ
diff --git a/resources/multimc/32x32/news.png b/resources/multimc/32x32/news.png
new file mode 100644
index 00000000..c579fd44
--- /dev/null
+++ b/resources/multimc/32x32/news.png
Binary files differ
diff --git a/resources/multimc/32x32/noaccount.png b/resources/multimc/32x32/noaccount.png
new file mode 100644
index 00000000..a73afc94
--- /dev/null
+++ b/resources/multimc/32x32/noaccount.png
Binary files differ
diff --git a/resources/multimc/32x32/refresh.png b/resources/multimc/32x32/refresh.png
new file mode 100644
index 00000000..afa2a9d7
--- /dev/null
+++ b/resources/multimc/32x32/refresh.png
Binary files differ
diff --git a/resources/multimc/32x32/settings.png b/resources/multimc/32x32/settings.png
new file mode 100644
index 00000000..a9c0817c
--- /dev/null
+++ b/resources/multimc/32x32/settings.png
Binary files differ
diff --git a/resources/multimc/32x32/viewfolder.png b/resources/multimc/32x32/viewfolder.png
new file mode 100644
index 00000000..74ab8fa6
--- /dev/null
+++ b/resources/multimc/32x32/viewfolder.png
Binary files differ
diff --git a/resources/multimc/48x48/about.png b/resources/multimc/48x48/about.png
new file mode 100644
index 00000000..b4ac71b8
--- /dev/null
+++ b/resources/multimc/48x48/about.png
Binary files differ
diff --git a/resources/multimc/48x48/bug.png b/resources/multimc/48x48/bug.png
new file mode 100644
index 00000000..298f9397
--- /dev/null
+++ b/resources/multimc/48x48/bug.png
Binary files differ
diff --git a/resources/multimc/48x48/cat.png b/resources/multimc/48x48/cat.png
new file mode 100644
index 00000000..25912a3c
--- /dev/null
+++ b/resources/multimc/48x48/cat.png
Binary files differ
diff --git a/resources/multimc/48x48/centralmods.png b/resources/multimc/48x48/centralmods.png
new file mode 100644
index 00000000..d927e39b
--- /dev/null
+++ b/resources/multimc/48x48/centralmods.png
Binary files differ
diff --git a/resources/multimc/48x48/checkupdate.png b/resources/multimc/48x48/checkupdate.png
new file mode 100644
index 00000000..2e2c7d6b
--- /dev/null
+++ b/resources/multimc/48x48/checkupdate.png
Binary files differ
diff --git a/resources/multimc/48x48/copy.png b/resources/multimc/48x48/copy.png
new file mode 100644
index 00000000..ea40e34b
--- /dev/null
+++ b/resources/multimc/48x48/copy.png
Binary files differ
diff --git a/resources/multimc/48x48/help.png b/resources/multimc/48x48/help.png
new file mode 100644
index 00000000..82d828fa
--- /dev/null
+++ b/resources/multimc/48x48/help.png
Binary files differ
diff --git a/resources/multimc/48x48/new.png b/resources/multimc/48x48/new.png
new file mode 100644
index 00000000..a81753b3
--- /dev/null
+++ b/resources/multimc/48x48/new.png
Binary files differ
diff --git a/resources/multimc/48x48/news.png b/resources/multimc/48x48/news.png
new file mode 100644
index 00000000..0f82d857
--- /dev/null
+++ b/resources/multimc/48x48/news.png
Binary files differ
diff --git a/resources/multimc/48x48/noaccount.png b/resources/multimc/48x48/noaccount.png
new file mode 100644
index 00000000..4703796c
--- /dev/null
+++ b/resources/multimc/48x48/noaccount.png
Binary files differ
diff --git a/resources/multimc/48x48/refresh.png b/resources/multimc/48x48/refresh.png
new file mode 100644
index 00000000..0b08b238
--- /dev/null
+++ b/resources/multimc/48x48/refresh.png
Binary files differ
diff --git a/resources/multimc/48x48/settings.png b/resources/multimc/48x48/settings.png
new file mode 100644
index 00000000..6674eb23
--- /dev/null
+++ b/resources/multimc/48x48/settings.png
Binary files differ
diff --git a/resources/multimc/48x48/viewfolder.png b/resources/multimc/48x48/viewfolder.png
new file mode 100644
index 00000000..0492a736
--- /dev/null
+++ b/resources/multimc/48x48/viewfolder.png
Binary files differ
diff --git a/resources/multimc/64x64/about.png b/resources/multimc/64x64/about.png
new file mode 100644
index 00000000..b83e9269
--- /dev/null
+++ b/resources/multimc/64x64/about.png
Binary files differ
diff --git a/resources/multimc/64x64/bug.png b/resources/multimc/64x64/bug.png
new file mode 100644
index 00000000..156b0315
--- /dev/null
+++ b/resources/multimc/64x64/bug.png
Binary files differ
diff --git a/resources/multimc/64x64/cat.png b/resources/multimc/64x64/cat.png
new file mode 100644
index 00000000..2cc21f80
--- /dev/null
+++ b/resources/multimc/64x64/cat.png
Binary files differ
diff --git a/resources/multimc/64x64/centralmods.png b/resources/multimc/64x64/centralmods.png
new file mode 100644
index 00000000..8831f437
--- /dev/null
+++ b/resources/multimc/64x64/centralmods.png
Binary files differ
diff --git a/resources/multimc/64x64/checkupdate.png b/resources/multimc/64x64/checkupdate.png
new file mode 100644
index 00000000..dd1e29ac
--- /dev/null
+++ b/resources/multimc/64x64/checkupdate.png
Binary files differ
diff --git a/resources/multimc/64x64/copy.png b/resources/multimc/64x64/copy.png
new file mode 100644
index 00000000..d12cf9c8
--- /dev/null
+++ b/resources/multimc/64x64/copy.png
Binary files differ
diff --git a/resources/multimc/64x64/help.png b/resources/multimc/64x64/help.png
new file mode 100644
index 00000000..0f3948c2
--- /dev/null
+++ b/resources/multimc/64x64/help.png
Binary files differ
diff --git a/resources/multimc/64x64/new.png b/resources/multimc/64x64/new.png
new file mode 100644
index 00000000..c3c6796c
--- /dev/null
+++ b/resources/multimc/64x64/new.png
Binary files differ
diff --git a/resources/multimc/64x64/news.png b/resources/multimc/64x64/news.png
new file mode 100644
index 00000000..e306eed3
--- /dev/null
+++ b/resources/multimc/64x64/news.png
Binary files differ
diff --git a/resources/multimc/64x64/refresh.png b/resources/multimc/64x64/refresh.png
new file mode 100644
index 00000000..8373d819
--- /dev/null
+++ b/resources/multimc/64x64/refresh.png
Binary files differ
diff --git a/resources/multimc/64x64/settings.png b/resources/multimc/64x64/settings.png
new file mode 100644
index 00000000..e3ff58fa
--- /dev/null
+++ b/resources/multimc/64x64/settings.png
Binary files differ
diff --git a/resources/multimc/64x64/viewfolder.png b/resources/multimc/64x64/viewfolder.png
new file mode 100644
index 00000000..7d531f9c
--- /dev/null
+++ b/resources/multimc/64x64/viewfolder.png
Binary files differ
diff --git a/resources/multimc/8x8/noaccount.png b/resources/multimc/8x8/noaccount.png
new file mode 100644
index 00000000..466e4c07
--- /dev/null
+++ b/resources/multimc/8x8/noaccount.png
Binary files differ
diff --git a/resources/multimc/index.theme b/resources/multimc/index.theme
new file mode 100644
index 00000000..776792b7
--- /dev/null
+++ b/resources/multimc/index.theme
@@ -0,0 +1,33 @@
+[Icon Theme]
+Name=multimc
+Comment=MultiMC Default Icons
+Inherits=default
+Directories=scalable/apps,8x8,16x16,22x22,24x24,32x32,48x48
+
+[scalable/apps]
+Size=48
+Type=scalable
+MinSize=1
+MaxSize=512
+Context=Applications
+
+[8x8]
+Size=8
+
+[16x16]
+Size=16
+
+[22x22]
+Size=22
+
+[24x24]
+Size=24
+
+[32x32]
+Size=32
+
+[48x48]
+Size=48
+
+[64x64]
+Size=64 \ No newline at end of file
diff --git a/resources/multimc/multimc.qrc b/resources/multimc/multimc.qrc
new file mode 100644
index 00000000..363347de
--- /dev/null
+++ b/resources/multimc/multimc.qrc
@@ -0,0 +1,111 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource prefix="/icons/multimc">
+ <file>index.theme</file>
+ <!-- Logo. Our own. -->
+ <file>scalable/apps/multimc.svg</file>
+
+ <!-- OK console icon. Our own -->
+ <file>scalable/console.svg</file>
+
+ <!-- ERROR console icon. Our own -->
+ <file>scalable/console_error.svg</file>
+
+ <!-- About dialog. GPLv2, http://openiconlibrary.sourceforge.net/gallery2/?./Icons/actions/help-contents.png -->
+ <file>16x16/about.png</file>
+ <file>22x22/about.png</file>
+ <file>32x32/about.png</file>
+ <file>48x48/about.png</file>
+ <file>64x64/about.png</file>
+
+ <!-- Report bug. Our own. -->
+ <file>scalable/bug.svg</file>
+ <file>16x16/bug.png</file>
+ <file>22x22/bug.png</file>
+ <file>32x32/bug.png</file>
+ <file>48x48/bug.png</file>
+ <file>64x64/bug.png</file>
+
+ <!-- The cat button. Freeware, http://findicons.com/icon/73096/black_cat -->
+ <file>16x16/cat.png</file>
+ <file>22x22/cat.png</file>
+ <file>24x24/cat.png</file>
+ <file>32x32/cat.png</file>
+ <file>48x48/cat.png</file>
+ <file>64x64/cat.png</file>
+
+ <!-- Show mods folder. CC-BY-SA 3.0 http://openiconlibrary.sourceforge.net/gallery2/?./Icons/places/oxygen-style/folder-favorites.png -->
+ <file>scalable/centralmods.svg</file>
+ <file>16x16/centralmods.png</file>
+ <file>22x22/centralmods.png</file>
+ <file>32x32/centralmods.png</file>
+ <file>48x48/centralmods.png</file>
+ <file>64x64/centralmods.png</file>
+
+ <!-- Update. GPLv2, https://code.google.com/p/gnome-colors/ -->
+ <file>scalable/checkupdate.svg</file>
+ <file>16x16/checkupdate.png</file>
+ <file>22x22/checkupdate.png</file>
+ <file>32x32/checkupdate.png</file>
+ <file>48x48/checkupdate.png</file>
+ <file>64x64/checkupdate.png</file>
+
+ <!-- copy instance. CC-BY-SA 3.0, http://openiconlibrary.sourceforge.net/gallery2/?./Icons/actions/edit-copy-6.png -->
+ <file>16x16/copy.png</file>
+ <file>22x22/copy.png</file>
+ <file>32x32/copy.png</file>
+ <file>48x48/copy.png</file>
+ <file>64x64/copy.png</file>
+
+ <!-- Help. CC-BY-SA 3.0, http://openiconlibrary.sourceforge.net/gallery2/?./Icons/actions/help.png -->
+ <file>16x16/help.png</file>
+ <file>22x22/help.png</file>
+ <file>32x32/help.png</file>
+ <file>48x48/help.png</file>
+ <file>64x64/help.png</file>
+
+ <!-- New instance. GPLv2, http://openiconlibrary.sourceforge.net/gallery2/?./Icons/actions/document-new-3.png -->
+ <file>16x16/new.png</file>
+ <file>22x22/new.png</file>
+ <file>32x32/new.png</file>
+ <file>48x48/new.png</file>
+ <file>64x64/new.png</file>
+
+ <!-- Open news. Our own. -->
+ <file>scalable/news.svg</file>
+ <file>16x16/news.png</file>
+ <file>22x22/news.png</file>
+ <file>32x32/news.png</file>
+ <file>48x48/news.png</file>
+ <file>64x64/news.png</file>
+
+ <!-- Refresh, CC-BY-SA 3.0, Oxygen icons. -->
+ <file>16x16/refresh.png</file>
+ <file>22x22/refresh.png</file>
+ <file>32x32/refresh.png</file>
+ <file>48x48/refresh.png</file>
+ <file>64x64/refresh.png</file>
+
+ <!-- Settings, LGPL-2.1, http://openiconlibrary.sourceforge.net/gallery2/?./Icons/apps/system-settings-3.png -->
+ <file>16x16/settings.png</file>
+ <file>22x22/settings.png</file>
+ <file>32x32/settings.png</file>
+ <file>48x48/settings.png</file>
+ <file>64x64/settings.png</file>
+
+ <!-- View folder. CC-BY-SA 3.0, http://openiconlibrary.sourceforge.net/gallery2/?./Icons/actions/document-open-folder.png -->
+ <file>scalable/viewfolder.svg</file>
+ <file>16x16/viewfolder.png</file>
+ <file>22x22/viewfolder.png</file>
+ <file>32x32/viewfolder.png</file>
+ <file>48x48/viewfolder.png</file>
+ <file>64x64/viewfolder.png</file>
+
+ <!-- Default minecraft skin, desaturated, cutout of head, Mojang -->
+ <file>8x8/noaccount.png</file>
+ <file>16x16/noaccount.png</file>
+ <file>24x24/noaccount.png</file>
+ <file>32x32/noaccount.png</file>
+ <file>48x48/noaccount.png</file>
+ </qresource>
+</RCC>
diff --git a/resources/multimc/scalable/apps/multimc.svg b/resources/multimc/scalable/apps/multimc.svg
new file mode 100644
index 00000000..178509ac
--- /dev/null
+++ b/resources/multimc/scalable/apps/multimc.svg
@@ -0,0 +1,1993 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64px"
+ height="64px"
+ id="svg4427"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="multimc.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/infinity128.png"
+ inkscape:export-xdpi="180"
+ inkscape:export-ydpi="180">
+ <defs
+ id="defs4429">
+ <linearGradient
+ id="linearGradient5668"
+ inkscape:collect="always">
+ <stop
+ id="stop5670"
+ offset="0"
+ style="stop-color:#75b54b;stop-opacity:1;" />
+ <stop
+ id="stop5672"
+ offset="1"
+ style="stop-color:#75b54b;stop-opacity:0.6" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5084"
+ inkscape:collect="always">
+ <stop
+ id="stop5086"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:0.8" />
+ <stop
+ id="stop5088"
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0.35" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5668"
+ id="linearGradient5072"
+ x1="6.7342591"
+ y1="28.510933"
+ x2="50.506943"
+ y2="61.773685"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.01532073,-0.00938002)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5084"
+ id="linearGradient5082"
+ x1="14.312115"
+ y1="9.7948904"
+ x2="44.097023"
+ y2="82.973114"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5668"
+ id="linearGradient3281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.01532073,-0.00938002)"
+ x1="6.7342591"
+ y1="28.510933"
+ x2="50.506943"
+ y2="61.773685" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5668"
+ id="linearGradient3283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.01532073,-0.00938002)"
+ x1="6.7342591"
+ y1="28.510933"
+ x2="50.506943"
+ y2="61.773685" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5668"
+ id="linearGradient3286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1879555,0,0,0.84178237,-0.01820035,-0.00789594)"
+ x1="6.7342591"
+ y1="28.510933"
+ x2="50.506943"
+ y2="61.773685" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5084"
+ id="linearGradient3288"
+ gradientUnits="userSpaceOnUse"
+ x1="14.312115"
+ y1="9.7948904"
+ x2="44.097023"
+ y2="82.973114" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5084"
+ id="linearGradient3290"
+ gradientUnits="userSpaceOnUse"
+ x1="14.312115"
+ y1="9.7948904"
+ x2="44.097023"
+ y2="82.973114" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5084"
+ id="linearGradient3293"
+ gradientUnits="userSpaceOnUse"
+ x1="14.312115"
+ y1="9.7948904"
+ x2="44.097023"
+ y2="82.973114"
+ gradientTransform="scale(1.1879555,0.84178237)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="6"
+ inkscape:cx="10.09561"
+ inkscape:cy="35.232628"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4446"
+ empspacing="16"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ spacingx="4px"
+ spacingy="4px" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4432">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="opacity:0.34999999999999998;fill:#552200;fill-opacity:1;stroke:none"
+ id="rect5674"
+ width="64.125"
+ height="64"
+ x="-0.125"
+ y="0.1249999" />
+ <rect
+ style="fill:#74b44a;fill-opacity:1;stroke:none"
+ id="rect4448"
+ width="4"
+ height="4"
+ x="0"
+ y="0" />
+ <rect
+ y="0"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4450"
+ style="fill:#76b64c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#73b349;fill-opacity:1;stroke:none"
+ id="rect4452"
+ width="4"
+ height="4"
+ x="8"
+ y="0" />
+ <rect
+ y="0"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4454"
+ style="fill:#66a63c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#66a63c;fill-opacity:1;stroke:none"
+ id="rect4456"
+ width="4"
+ height="4"
+ x="16"
+ y="0" />
+ <rect
+ y="0"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4458"
+ style="fill:#6faf45;fill-opacity:1;stroke:none" />
+ <rect
+ y="4"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4460"
+ style="fill:#75b54b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#6cac42;fill-opacity:1;stroke:none"
+ id="rect4462"
+ width="4"
+ height="4"
+ x="4"
+ y="4" />
+ <rect
+ y="4"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4464"
+ style="fill:#8ab95a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#81b051;fill-opacity:1;stroke:none"
+ id="rect4466"
+ width="4"
+ height="4"
+ x="12"
+ y="4" />
+ <rect
+ y="4"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4468"
+ style="fill:#83b253;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4470"
+ width="4"
+ height="4"
+ x="20"
+ y="4" />
+ <rect
+ y="0"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4472"
+ style="fill:#5f9f35;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#6cac42;fill-opacity:1;stroke:none"
+ id="rect4474"
+ width="4"
+ height="4"
+ x="28"
+ y="0" />
+ <rect
+ y="0"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4476"
+ style="fill:#7ebe54;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#76b64c;fill-opacity:1;stroke:none"
+ id="rect4478"
+ width="4"
+ height="4"
+ x="36"
+ y="0" />
+ <rect
+ y="0"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4480"
+ style="fill:#6aaa40;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#67a73d;fill-opacity:1;stroke:none"
+ id="rect4482"
+ width="4"
+ height="4"
+ x="44"
+ y="0" />
+ <rect
+ style="fill:#68a83e;fill-opacity:1;stroke:none"
+ id="rect4484"
+ width="4"
+ height="4"
+ x="24"
+ y="4" />
+ <rect
+ y="4"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4486"
+ style="fill:#62a238;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#5f9f35;fill-opacity:1;stroke:none"
+ id="rect4488"
+ width="4"
+ height="4"
+ x="32"
+ y="4" />
+ <rect
+ y="4"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4490"
+ style="fill:#93c263;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#90bf60;fill-opacity:1;stroke:none"
+ id="rect4492"
+ width="4"
+ height="4"
+ x="40"
+ y="4" />
+ <rect
+ y="4"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4494"
+ style="fill:#73b349;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#69a93f;fill-opacity:1;stroke:none"
+ id="rect4496"
+ width="4"
+ height="4"
+ x="48"
+ y="0" />
+ <rect
+ y="0"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4498"
+ style="fill:#61a137;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#509026;fill-opacity:1;stroke:none"
+ id="rect4500"
+ width="4"
+ height="4"
+ x="56"
+ y="0" />
+ <rect
+ y="0"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4502"
+ style="fill:#6dad43;fill-opacity:1;stroke:none" />
+ <rect
+ y="4"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4508"
+ style="fill:#61a137;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#6cac42;fill-opacity:1;stroke:none"
+ id="rect4510"
+ width="4"
+ height="4"
+ x="52"
+ y="4" />
+ <rect
+ y="4"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4512"
+ style="fill:#67a73d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#6bab41;fill-opacity:1;stroke:none"
+ id="rect4514"
+ width="4"
+ height="4"
+ x="60"
+ y="4" />
+ <rect
+ y="8"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4520"
+ style="fill:#8dbc5d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4522"
+ width="4"
+ height="4"
+ x="4"
+ y="8" />
+ <rect
+ y="8"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4524"
+ style="fill:#9ccb6c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#64a43a;fill-opacity:1;stroke:none"
+ id="rect4526"
+ width="4"
+ height="4"
+ x="12"
+ y="8" />
+ <rect
+ y="8"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4528"
+ style="fill:#69a93f;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4530"
+ width="4"
+ height="4"
+ x="20"
+ y="8" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4532"
+ width="4"
+ height="4"
+ x="0"
+ y="12" />
+ <rect
+ y="12"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4534"
+ style="fill:#6c6c6c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4536"
+ width="4"
+ height="4"
+ x="8"
+ y="12" />
+ <rect
+ y="12"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4538"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#71b147;fill-opacity:1;stroke:none"
+ id="rect4540"
+ width="4"
+ height="4"
+ x="16"
+ y="12" />
+ <rect
+ y="12"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4542"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#70b046;fill-opacity:1;stroke:none"
+ id="rect4544"
+ width="4"
+ height="4"
+ x="24"
+ y="8" />
+ <rect
+ y="8"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4546"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#74b44a;fill-opacity:1;stroke:none"
+ id="rect4548"
+ width="4"
+ height="4"
+ x="32"
+ y="8" />
+ <rect
+ y="8"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4550"
+ style="fill:#7fbf55;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#92c162;fill-opacity:1;stroke:none"
+ id="rect4552"
+ width="4"
+ height="4"
+ x="40"
+ y="8" />
+ <rect
+ y="8"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4554"
+ style="fill:#97c667;fill-opacity:1;stroke:none" />
+ <rect
+ y="12"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4556"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4558"
+ width="4"
+ height="4"
+ x="28"
+ y="12" />
+ <rect
+ y="12"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4560"
+ style="fill:#5f9f35;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4562"
+ width="4"
+ height="4"
+ x="36"
+ y="12" />
+ <rect
+ y="12"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4564"
+ style="fill:#6dad43;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4566"
+ width="4"
+ height="4"
+ x="44"
+ y="12" />
+ <rect
+ y="8"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4568"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#57972d;fill-opacity:1;stroke:none"
+ id="rect4570"
+ width="4"
+ height="4"
+ x="52"
+ y="8" />
+ <rect
+ y="8"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4572"
+ style="fill:#60a036;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4574"
+ width="4"
+ height="4"
+ x="60"
+ y="8" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4576"
+ width="4"
+ height="4"
+ x="48"
+ y="12" />
+ <rect
+ y="12"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4578"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4580"
+ width="4"
+ height="4"
+ x="56"
+ y="12" />
+ <rect
+ y="12"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4582"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ y="16"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4584"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4586"
+ width="4"
+ height="4"
+ x="4"
+ y="16" />
+ <rect
+ y="16"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4588"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4590"
+ width="4"
+ height="4"
+ x="12"
+ y="16" />
+ <rect
+ y="16"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4592"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4594"
+ width="4"
+ height="4"
+ x="20"
+ y="16" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4596"
+ width="4"
+ height="4"
+ x="0"
+ y="20" />
+ <rect
+ y="20"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4598"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4600"
+ width="4"
+ height="4"
+ x="8"
+ y="20" />
+ <rect
+ y="20"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4602"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4604"
+ width="4"
+ height="4"
+ x="16"
+ y="20" />
+ <rect
+ y="20"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4606"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4608"
+ width="4"
+ height="4"
+ x="24"
+ y="16" />
+ <rect
+ y="16"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4610"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4612"
+ width="4"
+ height="4"
+ x="32"
+ y="16" />
+ <rect
+ y="16"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4614"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4616"
+ width="4"
+ height="4"
+ x="40"
+ y="16" />
+ <rect
+ y="16"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4618"
+ style="fill:#6c6c6c;fill-opacity:1;stroke:none" />
+ <rect
+ y="20"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4620"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4622"
+ width="4"
+ height="4"
+ x="28"
+ y="20" />
+ <rect
+ y="20"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4624"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4626"
+ width="4"
+ height="4"
+ x="36"
+ y="20" />
+ <rect
+ y="20"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4628"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4630"
+ width="4"
+ height="4"
+ x="44"
+ y="20" />
+ <rect
+ y="16"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4632"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4634"
+ width="4"
+ height="4"
+ x="52"
+ y="16" />
+ <rect
+ y="16"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4636"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4638"
+ width="4"
+ height="4"
+ x="60"
+ y="16" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4640"
+ width="4"
+ height="4"
+ x="48"
+ y="20" />
+ <rect
+ y="20"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4642"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4644"
+ width="4"
+ height="4"
+ x="56"
+ y="20" />
+ <rect
+ y="20"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4646"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4648"
+ width="4"
+ height="4"
+ x="0"
+ y="24" />
+ <rect
+ y="24"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4650"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4652"
+ width="4"
+ height="4"
+ x="8"
+ y="24" />
+ <rect
+ y="24"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4654"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#878787;fill-opacity:1;stroke:none"
+ id="rect4656"
+ width="4"
+ height="4"
+ x="16"
+ y="24" />
+ <rect
+ y="24"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4658"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ y="28"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4660"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4662"
+ width="4"
+ height="4"
+ x="4"
+ y="28" />
+ <rect
+ y="28"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4664"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4666"
+ width="4"
+ height="4"
+ x="12"
+ y="28" />
+ <rect
+ y="28"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4668"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4670"
+ width="4"
+ height="4"
+ x="20"
+ y="28" />
+ <rect
+ y="24"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4672"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4674"
+ width="4"
+ height="4"
+ x="28"
+ y="24" />
+ <rect
+ y="24"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4676"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4678"
+ width="4"
+ height="4"
+ x="36"
+ y="24" />
+ <rect
+ y="24"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4680"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4682"
+ width="4"
+ height="4"
+ x="44"
+ y="24" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4684"
+ width="4"
+ height="4"
+ x="24"
+ y="28" />
+ <rect
+ y="28"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4686"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4688"
+ width="4"
+ height="4"
+ x="32"
+ y="28" />
+ <rect
+ y="28"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4690"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4692"
+ width="4"
+ height="4"
+ x="40"
+ y="28" />
+ <rect
+ y="28"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4694"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4696"
+ width="4"
+ height="4"
+ x="48"
+ y="24" />
+ <rect
+ y="24"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4698"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4700"
+ width="4"
+ height="4"
+ x="56"
+ y="24" />
+ <rect
+ y="24"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4702"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ y="28"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4704"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4706"
+ width="4"
+ height="4"
+ x="52"
+ y="28" />
+ <rect
+ y="28"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4708"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4710"
+ width="4"
+ height="4"
+ x="60"
+ y="28" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4448-5"
+ width="4"
+ height="4"
+ x="0"
+ y="32" />
+ <rect
+ y="32"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4450-2"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4452-3"
+ width="4"
+ height="4"
+ x="8"
+ y="32" />
+ <rect
+ y="32"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4454-7"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4456-2"
+ width="4"
+ height="4"
+ x="16"
+ y="32" />
+ <rect
+ y="32"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4458-4"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ y="36"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4460-9"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4462-7"
+ width="4"
+ height="4"
+ x="4"
+ y="36" />
+ <rect
+ y="36"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4464-3"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4466-7"
+ width="4"
+ height="4"
+ x="12"
+ y="36" />
+ <rect
+ y="36"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4468-8"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4470-9"
+ width="4"
+ height="4"
+ x="20"
+ y="36" />
+ <rect
+ y="32"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4472-9"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4474-6"
+ width="4"
+ height="4"
+ x="28"
+ y="32" />
+ <rect
+ y="32"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4476-7"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4478-8"
+ width="4"
+ height="4"
+ x="36"
+ y="32" />
+ <rect
+ y="32"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4480-1"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4482-6"
+ width="4"
+ height="4"
+ x="44"
+ y="32" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4484-7"
+ width="4"
+ height="4"
+ x="24"
+ y="36" />
+ <rect
+ y="36"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4486-1"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4488-9"
+ width="4"
+ height="4"
+ x="32"
+ y="36" />
+ <rect
+ y="36"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4490-9"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4492-3"
+ width="4"
+ height="4"
+ x="40"
+ y="36" />
+ <rect
+ y="36"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4494-9"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4496-4"
+ width="4"
+ height="4"
+ x="48"
+ y="32" />
+ <rect
+ y="32"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4498-2"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4500-1"
+ width="4"
+ height="4"
+ x="56"
+ y="32" />
+ <rect
+ y="32"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4502-7"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ y="36"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4508-4"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4510-6"
+ width="4"
+ height="4"
+ x="52"
+ y="36" />
+ <rect
+ y="36"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4512-8"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4514-1"
+ width="4"
+ height="4"
+ x="60"
+ y="36" />
+ <rect
+ y="40"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4520-0"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4522-3"
+ width="4"
+ height="4"
+ x="4"
+ y="40" />
+ <rect
+ y="40"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4524-1"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4526-3"
+ width="4"
+ height="4"
+ x="12"
+ y="40" />
+ <rect
+ y="40"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4528-7"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4530-0"
+ width="4"
+ height="4"
+ x="20"
+ y="40" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4532-2"
+ width="4"
+ height="4"
+ x="0"
+ y="44" />
+ <rect
+ y="44"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4534-0"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4536-9"
+ width="4"
+ height="4"
+ x="8"
+ y="44" />
+ <rect
+ y="44"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4538-0"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4540-2"
+ width="4"
+ height="4"
+ x="16"
+ y="44" />
+ <rect
+ y="44"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4542-9"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4544-6"
+ width="4"
+ height="4"
+ x="24"
+ y="40" />
+ <rect
+ y="40"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4546-9"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4548-9"
+ width="4"
+ height="4"
+ x="32"
+ y="40" />
+ <rect
+ y="40"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4550-8"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4552-7"
+ width="4"
+ height="4"
+ x="40"
+ y="40" />
+ <rect
+ y="40"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4554-6"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ y="44"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4556-1"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4558-9"
+ width="4"
+ height="4"
+ x="28"
+ y="44" />
+ <rect
+ y="44"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4560-7"
+ style="fill:#6c6c6c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4562-5"
+ width="4"
+ height="4"
+ x="36"
+ y="44" />
+ <rect
+ y="44"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4564-8"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4566-2"
+ width="4"
+ height="4"
+ x="44"
+ y="44" />
+ <rect
+ y="40"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4568-9"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4570-1"
+ width="4"
+ height="4"
+ x="52"
+ y="40" />
+ <rect
+ y="40"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4572-9"
+ style="fill:#878787;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4574-5"
+ width="4"
+ height="4"
+ x="60"
+ y="40" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4576-8"
+ width="4"
+ height="4"
+ x="48"
+ y="44" />
+ <rect
+ y="44"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4578-7"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4580-6"
+ width="4"
+ height="4"
+ x="56"
+ y="44" />
+ <rect
+ y="44"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4582-0"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ y="48"
+ x="0"
+ height="4"
+ width="4"
+ id="rect4930"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4932"
+ width="4"
+ height="4"
+ x="4"
+ y="48" />
+ <rect
+ y="48"
+ x="8"
+ height="4"
+ width="4"
+ id="rect4934"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4936"
+ width="4"
+ height="4"
+ x="12"
+ y="48" />
+ <rect
+ y="48"
+ x="16"
+ height="4"
+ width="4"
+ id="rect4938"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4940"
+ width="4"
+ height="4"
+ x="20"
+ y="48" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4942"
+ width="4"
+ height="4"
+ x="0"
+ y="52" />
+ <rect
+ y="52"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4944"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4946"
+ width="4"
+ height="4"
+ x="8"
+ y="52" />
+ <rect
+ y="52"
+ x="12"
+ height="4"
+ width="4"
+ id="rect4948"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4950"
+ width="4"
+ height="4"
+ x="16"
+ y="52" />
+ <rect
+ y="52"
+ x="20"
+ height="4"
+ width="4"
+ id="rect4952"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4954"
+ width="4"
+ height="4"
+ x="24"
+ y="48" />
+ <rect
+ y="48"
+ x="28"
+ height="4"
+ width="4"
+ id="rect4956"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4958"
+ width="4"
+ height="4"
+ x="32"
+ y="48" />
+ <rect
+ y="48"
+ x="36"
+ height="4"
+ width="4"
+ id="rect4960"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4962"
+ width="4"
+ height="4"
+ x="40"
+ y="48" />
+ <rect
+ y="48"
+ x="44"
+ height="4"
+ width="4"
+ id="rect4964"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ y="52"
+ x="24"
+ height="4"
+ width="4"
+ id="rect4966"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4968"
+ width="4"
+ height="4"
+ x="28"
+ y="52" />
+ <rect
+ y="52"
+ x="32"
+ height="4"
+ width="4"
+ id="rect4970"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4972"
+ width="4"
+ height="4"
+ x="36"
+ y="52" />
+ <rect
+ y="52"
+ x="40"
+ height="4"
+ width="4"
+ id="rect4974"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4976"
+ width="4"
+ height="4"
+ x="44"
+ y="52" />
+ <rect
+ y="48"
+ x="48"
+ height="4"
+ width="4"
+ id="rect4978"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4980"
+ width="4"
+ height="4"
+ x="52"
+ y="48" />
+ <rect
+ y="48"
+ x="56"
+ height="4"
+ width="4"
+ id="rect4982"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b9855c;fill-opacity:1;stroke:none"
+ id="rect4984"
+ width="4"
+ height="4"
+ x="60"
+ y="48" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect4986"
+ width="4"
+ height="4"
+ x="48"
+ y="52" />
+ <rect
+ y="52"
+ x="52"
+ height="4"
+ width="4"
+ id="rect4988"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4990"
+ width="4"
+ height="4"
+ x="56"
+ y="52" />
+ <rect
+ y="52"
+ x="60"
+ height="4"
+ width="4"
+ id="rect4992"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect4994"
+ width="4"
+ height="4"
+ x="0"
+ y="56" />
+ <rect
+ y="56"
+ x="4"
+ height="4"
+ width="4"
+ id="rect4996"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect4998"
+ width="4"
+ height="4"
+ x="8"
+ y="56" />
+ <rect
+ y="56"
+ x="12"
+ height="4"
+ width="4"
+ id="rect5000"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect5002"
+ width="4"
+ height="4"
+ x="16"
+ y="56" />
+ <rect
+ y="56"
+ x="20"
+ height="4"
+ width="4"
+ id="rect5004"
+ style="fill:#593d29;fill-opacity:1;stroke:none" />
+ <rect
+ y="60"
+ x="0"
+ height="4"
+ width="4"
+ id="rect5006"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect5008"
+ width="4"
+ height="4"
+ x="4"
+ y="60" />
+ <rect
+ y="60"
+ x="8"
+ height="4"
+ width="4"
+ id="rect5010"
+ style="fill:#b9855c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect5012"
+ width="4"
+ height="4"
+ x="12"
+ y="60" />
+ <rect
+ y="60"
+ x="16"
+ height="4"
+ width="4"
+ id="rect5014"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect5016"
+ width="4"
+ height="4"
+ x="20"
+ y="60" />
+ <rect
+ y="56"
+ x="24"
+ height="4"
+ width="4"
+ id="rect5018"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect5020"
+ width="4"
+ height="4"
+ x="28"
+ y="56" />
+ <rect
+ y="56"
+ x="32"
+ height="4"
+ width="4"
+ id="rect5022"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect5024"
+ width="4"
+ height="4"
+ x="36"
+ y="56" />
+ <rect
+ y="56"
+ x="40"
+ height="4"
+ width="4"
+ id="rect5026"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect5028"
+ width="4"
+ height="4"
+ x="44"
+ y="56" />
+ <rect
+ style="fill:#878787;fill-opacity:1;stroke:none"
+ id="rect5030"
+ width="4"
+ height="4"
+ x="24"
+ y="60" />
+ <rect
+ y="60"
+ x="28"
+ height="4"
+ width="4"
+ id="rect5032"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect5034"
+ width="4"
+ height="4"
+ x="32"
+ y="60" />
+ <rect
+ y="60"
+ x="36"
+ height="4"
+ width="4"
+ id="rect5036"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect5038"
+ width="4"
+ height="4"
+ x="40"
+ y="60" />
+ <rect
+ y="60"
+ x="44"
+ height="4"
+ width="4"
+ id="rect5040"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#79553a;fill-opacity:1;stroke:none"
+ id="rect5042"
+ width="4"
+ height="4"
+ x="48"
+ y="56" />
+ <rect
+ y="56"
+ x="52"
+ height="4"
+ width="4"
+ id="rect5044"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect5046"
+ width="4"
+ height="4"
+ x="56"
+ y="56" />
+ <rect
+ y="56"
+ x="60"
+ height="4"
+ width="4"
+ id="rect5048"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ y="60"
+ x="48"
+ height="4"
+ width="4"
+ id="rect5050"
+ style="fill:#966c4a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#966c4a;fill-opacity:1;stroke:none"
+ id="rect5052"
+ width="4"
+ height="4"
+ x="52"
+ y="60" />
+ <rect
+ y="60"
+ x="56"
+ height="4"
+ width="4"
+ id="rect5054"
+ style="fill:#79553a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#593d29;fill-opacity:1;stroke:none"
+ id="rect5056"
+ width="4"
+ height="4"
+ x="60"
+ y="60" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3279"
+ style="font-size:76.18933868px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3293);fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans"
+ d="m 37.233107,43.070771 c -0.913385,-0.751568 -2.091894,-2.0042 -3.535529,-3.7579 -1.974039,2.505279 -3.83019,4.311158 -5.568458,5.41764 -2.180273,1.33615 -4.9645,2.004221 -8.352688,2.004214 -3.97749,7e-6 -7.277313,-1.054292 -9.8994808,-3.1629 -2.7695088,-2.212974 -4.1542564,-5.146221 -4.1542467,-8.799752 -9.7e-6,-3.507351 1.3847379,-6.451037 4.1542467,-8.831067 2.4748538,-2.10857 5.8041408,-3.162868 9.9878698,-3.162899 2.150754,3.1e-5 4.021637,0.313189 5.612652,0.939475 1.856121,0.688978 3.417645,1.628452 4.684576,2.818425 1.178474,1.064766 2.356983,2.317398 3.535529,3.757901 1.973965,-2.505241 3.830116,-4.311119 5.568458,-5.41764 2.180198,-1.336112 4.964424,-2.004182 8.352687,-2.004214 3.977416,3.2e-5 7.277239,1.05433 9.899482,3.1629 2.769434,2.213012 4.154182,5.146259 4.154247,8.799751 -6.5e-5,3.50739 -1.384813,6.451076 -4.154247,8.831068 -2.47493,2.108607 -5.804215,3.162907 -9.987869,3.162899 -2.15083,8e-6 -4.021712,-0.31315 -5.612653,-0.939475 -1.591032,-0.563676 -3.152556,-1.503151 -4.684576,-2.818426 M 19.290297,42.63235 c 4.861324,1.1e-5 8.750403,-2.505254 11.667246,-7.515802 -3.7418,-5.46981 -7.630877,-8.204724 -11.667246,-8.20475 -2.946295,2.6e-5 -5.17073,0.751606 -6.67331,2.254741 -1.620467,1.607569 -2.430691,3.476079 -2.430677,5.605534 -1.4e-5,2.338267 0.81021,4.227655 2.430677,5.668168 1.649894,1.461418 3.874329,2.19212 6.67331,2.192109 M 48.104859,26.974429 c -4.389996,2.7e-5 -8.279074,2.505292 -11.667245,7.515802 3.712261,5.469847 7.601339,8.204762 11.667245,8.20475 2.946219,1.2e-5 5.170654,-0.751567 6.673311,-2.25474 1.62039,-1.607532 2.430615,-3.476042 2.430677,-5.605536 -6.2e-5,-2.338228 -0.810287,-4.227615 -2.430677,-5.668166 -1.64997,-1.46138 -3.874405,-2.192083 -6.673311,-2.19211" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3272"
+ style="font-size:76.18933868px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3286);fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans"
+ d="M 35.535529,40.267381 C 34.622143,39.515813 33.443636,38.26318 32,36.50948 c -1.97404,2.505279 -3.830191,4.311157 -5.568458,5.417641 -2.180273,1.33615 -4.964499,2.004221 -8.352688,2.004213 -3.97749,8e-6 -7.277313,-1.054291 -9.8994809,-3.1629 -2.7695089,-2.212973 -4.1542564,-5.14622 -4.1542466,-8.799751 -9.8e-6,-3.507351 1.3847377,-6.451037 4.1542466,-8.831067 2.4748539,-2.10857 5.8041399,-3.162869 9.9878699,-3.1629 2.150754,3.1e-5 4.021636,0.313189 5.612653,0.939476 1.856121,0.688977 3.417644,1.628452 4.684575,2.818425 1.178474,1.064765 2.356983,2.317397 3.535529,3.757901 1.973964,-2.505241 3.830115,-4.31112 5.568458,-5.417641 2.180198,-1.336111 4.964425,-2.004183 8.352688,-2.004214 3.977415,3.1e-5 7.277238,1.054331 9.899481,3.162901 2.769433,2.213011 4.154181,5.146259 4.154247,8.799751 -6.6e-5,3.50739 -1.384814,6.451076 -4.154247,8.831067 -2.474929,2.108608 -5.804216,3.162907 -9.98787,3.1629 -2.150829,7e-6 -4.021712,-0.313151 -5.612651,-0.939475 -1.591033,-0.563676 -3.152557,-1.503151 -4.684577,-2.818426 m -17.94281,-0.438422 c 4.861324,1.2e-5 8.750402,-2.505253 11.667246,-7.515802 -3.741799,-5.469809 -7.630877,-8.204723 -11.667246,-8.20475 -2.946294,2.7e-5 -5.170729,0.751607 -6.673311,2.25474 -1.6204657,1.607571 -2.4306903,3.47608 -2.4306761,5.605536 -1.42e-5,2.338266 0.8102104,4.227653 2.4306761,5.668168 1.649895,1.461417 3.87433,2.19212 6.673311,2.192108 m 28.814562,-15.65792 c -4.389996,2.7e-5 -8.279075,2.505292 -11.667246,7.515802 3.712261,5.469847 7.60134,8.204761 11.667246,8.20475 2.94622,1.1e-5 5.170655,-0.751569 6.673311,-2.25474 1.620391,-1.607532 2.430616,-3.476042 2.430676,-5.605536 -6e-5,-2.338228 -0.810285,-4.227615 -2.430676,-5.668168 -1.64997,-1.461379 -3.874405,-2.192081 -6.673311,-2.192108" />
+ <path
+ style="font-size:76.18933868px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;opacity:0.3;fill:#ccff00;fill-opacity:1;stroke:none;font-family:Sans"
+ d="m 18.1875,19.84375 c -4.183729,3.1e-5 -7.525146,1.07893 -10,3.1875 -2.7695089,2.38003 -4.1562597,5.305149 -4.15625,8.8125 -9.7e-6,3.65353 1.3867411,6.599527 4.15625,8.8125 -1.5212822,-1.916504 -2.2812572,-4.2297 -2.28125,-6.9375 -9.7e-6,-3.507351 1.3867411,-6.43247 4.15625,-8.8125 2.474854,-2.10857 5.816271,-3.187469 10,-3.1875 2.150754,3.1e-5 4.033984,0.342464 5.625,0.96875 1.856121,0.688978 3.389319,1.622527 4.65625,2.8125 0.06409,0.05791 0.12341,0.128483 0.1875,0.1875 -0.686074,-0.747192 -1.376449,-1.442646 -2.0625,-2.0625 -1.266931,-1.189973 -2.800129,-2.123522 -4.65625,-2.8125 -1.591016,-0.626286 -3.474246,-0.968719 -5.625,-0.96875 z m 27.75,0.09375 c -3.388264,3.1e-5 -6.163553,0.695138 -8.34375,2.03125 -1.704583,1.085031 -3.678235,3.085676 -5.609375,5.515625 0.579636,0.617601 1.170346,1.291505 1.75,2 1.973964,-2.505241 3.996032,-4.534104 5.734375,-5.640625 2.180197,-1.336112 4.955486,-2.031219 8.34375,-2.03125 3.977415,3.1e-5 7.284007,1.07893 9.90625,3.1875 -0.534738,-0.676996 -1.150988,-1.296453 -1.875,-1.875 -2.622243,-2.10857 -5.928835,-3.187469 -9.90625,-3.1875 z m 8.3125,7.59375 c 0.852375,1.223658 1.281206,2.679142 1.28125,4.375 -6.1e-5,2.129494 -0.81711,4.017469 -2.4375,5.625 -1.502657,1.503172 -3.710031,2.250011 -6.65625,2.25 -2.487851,7e-6 -4.988305,-0.967727 -7.34375,-3.015625 2.940596,3.289974 6.065444,4.890634 9.21875,4.890625 2.946219,1.1e-5 5.153593,-0.746828 6.65625,-2.25 1.62039,-1.607531 2.437439,-3.495506 2.4375,-5.625 -6.1e-5,-2.338228 -0.81711,-4.215698 -2.4375,-5.65625 C 54.734909,27.917887 54.506918,27.70901 54.25,27.53125 z M 29.28125,32.1875 c -2.916844,5.010548 -6.794926,7.531261 -11.65625,7.53125 -2.419266,1e-5 -4.259506,-0.33008 -5.820312,-1.421875 0.327066,0.474769 0.553213,0.705259 1.007812,1.109375 1.649895,1.461418 3.888519,2.187511 6.6875,2.1875 4.861324,1.1e-5 8.739406,-2.520702 11.65625,-7.53125 z m 4.625,6.09375 c -0.03847,0.04882 -0.08662,0.07671 -0.125,0.125 0.644368,0.697893 1.225264,1.274763 1.71875,1.6875 -0.496316,-0.544589 -1.011044,-1.10464 -1.59375,-1.8125 z"
+ id="text5100"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccscsccccccccccccccccccccscscccccsccscccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="font-size:76.18933868px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;opacity:0.6;fill:#ccff00;fill-opacity:1;stroke:none;font-family:Sans"
+ d="m 18.497319,20.200444 c -4.183729,3.1e-5 -7.525146,1.07893 -9.9999998,3.1875 -2.769509,2.38003 -4.15626,5.305149 -4.15625,8.8125 -10e-6,3.65353 1.386741,6.599526 4.15625,8.8125 0.04016,0.03229 0.08452,0.06195 0.125,0.09375 -2.099258,-2.088931 -3.156258,-4.725391 -3.15625,-7.90625 -10e-6,-3.507351 1.386741,-6.43247 4.15625,-8.8125 2.4748538,-2.10857 5.8162708,-3.187469 9.9999998,-3.1875 2.150754,3.1e-5 4.033984,0.342464 5.625,0.96875 1.753945,0.651051 3.209663,1.526594 4.4375,2.625 -0.294623,-0.289675 -0.611631,-0.546309 -0.90625,-0.8125 -1.266931,-1.189973 -2.800129,-2.123522 -4.65625,-2.8125 -1.591016,-0.626286 -3.474246,-0.968719 -5.625,-0.96875 z m 27.75,0.09375 c -3.388264,3.1e-5 -6.163553,0.695138 -8.34375,2.03125 -1.699949,1.082082 -3.715349,3.033428 -5.640625,5.453125 0.333421,0.377464 0.666573,0.748711 1,1.15625 1.973964,-2.505241 4.027282,-4.502854 5.765625,-5.609375 2.180197,-1.336112 4.955486,-2.031219 8.34375,-2.03125 3.960394,3.1e-5 7.258204,1.065688 9.875,3.15625 -0.3384,-0.344593 -0.699118,-0.653406 -1.09375,-0.96875 -2.622243,-2.10857 -5.928835,-3.187469 -9.90625,-3.1875 z m 7.71875,6.875 c 1.240618,1.358666 1.874946,3.047801 1.875,5.09375 -6.1e-5,2.129494 -0.81711,4.017469 -2.4375,5.625 -1.502657,1.503172 -3.710031,2.250011 -6.65625,2.25 -2.255932,6e-6 -4.477939,-0.847369 -6.625,-2.53125 2.502325,2.328598 5.097121,3.531257 7.75,3.53125 2.946219,1.1e-5 5.153593,-0.746828 6.65625,-2.25 1.62039,-1.607531 2.437439,-3.495506 2.4375,-5.625 -6.1e-5,-2.338228 -0.81711,-4.215698 -2.4375,-5.65625 -0.174038,-0.154146 -0.375679,-0.299613 -0.5625,-0.4375 z m -24.375,5.375 c -2.916844,5.010548 -6.794926,7.531261 -11.65625,7.53125 -2.449108,1e-5 -4.461911,-0.568603 -6.03125,-1.6875 0.15565,0.165457 0.294325,0.344945 0.46875,0.5 1.649895,1.461418 3.888519,2.187511 6.6875,2.1875 4.861324,1.1e-5 8.739406,-2.520702 11.65625,-7.53125 z m 3.875,5.21875 c -0.04366,0.0554 -0.08146,0.10153 -0.125,0.15625 0.97366,1.114659 1.851375,2.034323 2.53125,2.59375 0.05858,0.05029 0.128837,0.07581 0.1875,0.125 -0.742667,-0.732421 -1.599645,-1.667381 -2.59375,-2.875 z"
+ id="text5058-0"
+ sodipodi:nodetypes="ccsccscccccccccccccccccccscscccccsccsccccccc" />
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/bug.svg b/resources/multimc/scalable/bug.svg
new file mode 100644
index 00000000..178e3c23
--- /dev/null
+++ b/resources/multimc/scalable/bug.svg
@@ -0,0 +1,387 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="bug.svg"
+ inkscape:export-filename="/home/peterix/minecraft/src/MultiMC5/resources/multimc/64x64/bug.png"
+ inkscape:export-xdpi="180"
+ inkscape:export-ydpi="180">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4218">
+ <stop
+ id="stop4220"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:0.18222222" />
+ <stop
+ id="stop4222"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4204">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.2;"
+ offset="0"
+ id="stop4206" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop4208" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3928">
+ <stop
+ style="stop-color:#ee2200;stop-opacity:1;"
+ offset="0"
+ id="stop3930" />
+ <stop
+ style="stop-color:#5d0000;stop-opacity:1"
+ offset="1"
+ id="stop3932" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3914">
+ <stop
+ style="stop-color:#d41e00;stop-opacity:1;"
+ offset="0"
+ id="stop3916" />
+ <stop
+ style="stop-color:#560000;stop-opacity:1"
+ offset="1"
+ id="stop3918" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3982"
+ x="-0.1382716"
+ width="1.2765432"
+ y="-0.1382716"
+ height="1.2765432">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.17283951"
+ id="feGaussianBlur3984" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3928"
+ id="radialGradient4007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1999988,1.6000096,-0.64514251,0.48385298,660.86724,457.21535)"
+ cx="50"
+ cy="1039.8744"
+ fx="50"
+ fy="1039.8744"
+ r="5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3914"
+ id="radialGradient4009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9840484,3.4721317,-1.0766621,0.61522625,1063.4143,213.8808)"
+ cx="53.469341"
+ cy="1036.8372"
+ fx="53.469341"
+ fy="1036.8372"
+ r="4.0321208" />
+ <filter
+ inkscape:collect="always"
+ id="filter4097">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.55555556"
+ id="feGaussianBlur4099" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4161">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.12345679"
+ id="feGaussianBlur4163" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3928"
+ id="radialGradient4174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.195069,-1.6036951,1.3789506,-1.0275877,-1295.4553,2174.0912)"
+ cx="49.742828"
+ cy="1033.5878"
+ fx="49.742828"
+ fy="1033.5878"
+ r="5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4204"
+ id="linearGradient4212"
+ x1="70.5"
+ y1="1035.3622"
+ x2="69"
+ y2="1036.8622"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4218"
+ id="linearGradient4216"
+ x1="84"
+ y1="1035.3622"
+ x2="85"
+ y2="1034.3622"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-15,-1.5)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3928"
+ id="radialGradient4237"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.195069,-1.6036951,1.3789506,-1.0275877,-1295.4553,2174.0912)"
+ cx="49.742828"
+ cy="1033.5878"
+ fx="49.742828"
+ fy="1033.5878"
+ r="5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4204"
+ id="linearGradient4239"
+ gradientUnits="userSpaceOnUse"
+ x1="70.5"
+ y1="1035.3622"
+ x2="69"
+ y2="1036.8622" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4218"
+ id="linearGradient4241"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-15,-1.5)"
+ x1="84"
+ y1="1035.3622"
+ x2="85"
+ y2="1034.3622" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="-143.08597"
+ inkscape:cy="-12.323554"
+ inkscape:document-units="px"
+ inkscape:current-layer="g3986"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="1026"
+ inkscape:window-x="-3"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4013"
+ d="m 10.25,1022.0497 0,4 -4,4 -4,0 0,2 4,0 0,2 c 2e-6,1.4503 0.117423,2.7742 0.3125,4 l -4.3125,0 0,2 4.75,0 c 0.379006,1.4252 0.851671,2.6914 1.46875,3.7813 l -2.21875,2.2187 0,2 2,0 1.875,-1.875 c 1.218087,1.3481 2.651451,2.2896 4.125,2.9375 l 0,2.9375 2,0 0,-2.25 c 5.000011,1.25 10.000061,-0.7499 10,-3.75 2.599309,0 4.428024,-3.7452 4.0625,-8 l 1.9375,0 0,-2 -2.25,0 c -0.338089,-1.3524 -0.923138,-2.7153 -1.78125,-3.9687 l 2.03125,-2.0313 0,-2 -2,0 -1.875,1.875 c -1.104498,-0.9979 -2.47625,-1.8385 -4.125,-2.5 l 0,-3.375 -2,0 0,2.75 c -1.736548,-0.4618 -3.716809,-0.75 -6,-0.75 l -2,0 0,-4 -2,0 z"
+ style="opacity:0.69037655;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4097)" />
+ <g
+ id="g3986"
+ transform="matrix(2,0,0,2,-89,-1043.3622)">
+ <rect
+ transform="translate(0,1020.3622)"
+ y="24"
+ x="51"
+ height="3"
+ width="1"
+ id="rect3876"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <rect
+ y="1040.3622"
+ x="45.5"
+ height="1"
+ width="2.5"
+ id="rect3874"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <path
+ transform="translate(0,1020.3622)"
+ inkscape:connector-curvature="0"
+ id="path3878"
+ d="m 46.5,24.5 1,0.5 2,-2 0.5,0 -1,-1 z"
+ style="fill:#000000;stroke:none"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ transform="translate(0,1020.3622)"
+ y="19"
+ x="58"
+ height="1"
+ width="2"
+ id="rect3870"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <path
+ transform="translate(0,1020.3622)"
+ inkscape:connector-curvature="0"
+ id="path3868"
+ d="M 57,16.5 58.5,15 59,16 57.5,17.5 C 56.533856,16.56406 55.788428,17.105786 57,16.5 z"
+ style="fill:#000000;stroke:none"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ transform="translate(0,1020.3622)"
+ inkscape:connector-curvature="0"
+ id="path3866"
+ d="m 47,18 0,-2 2,-2 2,0 c 0.815212,1.242754 1.053351,2.549628 0,4 -1.239551,0.522502 -2.476067,1.061913 -4,0 z"
+ style="fill:#333333;stroke:none"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ transform="translate(0,1020.3622)"
+ y="13"
+ x="54"
+ height="2"
+ width="1"
+ id="rect3872"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <rect
+ transform="translate(0,1020.3622)"
+ y="12"
+ x="49"
+ height="3"
+ width="1"
+ id="rect3880"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <rect
+ transform="translate(0,1020.3622)"
+ y="16"
+ x="45"
+ height="1"
+ width="3"
+ id="rect3882"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ style="opacity:0.80753138;fill:#1a1a1a;stroke:none;filter:url(#filter3982)"
+ d="m 47.75,1039.1747 0.140625,-2.1562 0.640625,-1.2032 1.125,-0.8125 2.03125,-0.062 -0.21875,3.9375 z"
+ id="path3948"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g4232"
+ transform="translate(-17,5.5)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4165"
+ d="m 68,1028.8622 c 0.803076,1.5119 0.470137,2.7762 0,4 -1.461682,0.7487 -2.788281,0.7094 -4,0 1.5e-5,10 10.000061,9.0001 10,6 3.000056,0 4.000023,-10 -6,-10 z"
+ style="fill:url(#radialGradient4237);fill-opacity:1;stroke:none" />
+ <path
+ style="fill:url(#linearGradient4239);fill-opacity:1;stroke:none"
+ d="m 68,1032.8622 6,6 c 6.1e-5,3.0001 -9.999985,4 -10,-6 1.211719,0.7094 2.538318,0.7487 4,0 z"
+ id="path4194"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path4196"
+ d="m 68,1032.8622 5.999942,6 c 3.000056,0 4.000029,-10 -5.999994,-10 0.803076,1.5119 0.470189,2.7762 5.2e-5,4 z"
+ style="fill:url(#linearGradient4241);fill-opacity:1;stroke:none" />
+ </g>
+ <path
+ transform="matrix(1.5,0,0,1.5,-27.5,1011.8622)"
+ d="M 54,16.5 C 54,16.776142 53.776142,17 53.5,17 53.223858,17 53,16.776142 53,16.5 53,16.223858 53.223858,16 53.5,16 c 0.276142,0 0.5,0.223858 0.5,0.5 z"
+ sodipodi:ry="0.5"
+ sodipodi:rx="0.5"
+ sodipodi:cy="16.5"
+ sodipodi:cx="53.5"
+ id="path3936"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.5,0,0,1.5,-28,1010.8622)"
+ d="M 56,18.5 C 56,18.776142 55.776142,19 55.5,19 55.223858,19 55,18.776142 55,18.5 55,18.223858 55.223858,18 55.5,18 c 0.276142,0 0.5,0.223858 0.5,0.5 z"
+ sodipodi:ry="0.5"
+ sodipodi:rx="0.5"
+ sodipodi:cy="18.5"
+ sodipodi:cx="55.5"
+ id="path3938"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.5,0,0,1.5,-25,1009.3622)"
+ d="M 50,20.5 C 50,20.776142 49.776142,21 49.5,21 49.223858,21 49,20.776142 49,20.5 49,20.223858 49.223858,20 49.5,20 c 0.276142,0 0.5,0.223858 0.5,0.5 z"
+ sodipodi:ry="0.5"
+ sodipodi:rx="0.5"
+ sodipodi:cy="20.5"
+ sodipodi:cx="49.5"
+ id="path3940"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.5,0,0,1.5,-26,1008.8622)"
+ d="M 52,22.5 C 52,22.776142 51.776142,23 51.5,23 51.223858,23 51,22.776142 51,22.5 51,22.223858 51.223858,22 51.5,22 c 0.276142,0 0.5,0.223858 0.5,0.5 z"
+ sodipodi:ry="0.5"
+ sodipodi:rx="0.5"
+ sodipodi:cy="22.5"
+ sodipodi:cx="51.5"
+ id="path3942"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.5,0,0,1.5,-26.5,1009.3622)"
+ d="M 54,23.5 C 54,23.776142 53.776142,24 53.5,24 53.223858,24 53,23.776142 53,23.5 53,23.223858 53.223858,23 53.5,23 c 0.276142,0 0.5,0.223858 0.5,0.5 z"
+ sodipodi:ry="0.5"
+ sodipodi:rx="0.5"
+ sodipodi:cy="23.5"
+ sodipodi:cx="53.5"
+ id="path3944"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.5,0,0,1.5,-27.5,1010.3622)"
+ d="M 57,20.5 C 57,20.776142 56.776142,21 56.5,21 56.223858,21 56,20.776142 56,20.5 56,20.223858 56.223858,20 56.5,20 c 0.276142,0 0.5,0.223858 0.5,0.5 z"
+ sodipodi:ry="0.5"
+ sodipodi:rx="0.5"
+ sodipodi:cy="20.5"
+ sodipodi:cx="56.5"
+ id="path3946"
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/centralmods.svg b/resources/multimc/scalable/centralmods.svg
new file mode 100644
index 00000000..a8b123d0
--- /dev/null
+++ b/resources/multimc/scalable/centralmods.svg
@@ -0,0 +1,346 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) --><svg height="128" id="svg2811" inkscape:export-filename="/home/pinheiro/pics/oxygen/scalable/places/folder-bookmarks.png" inkscape:export-xdpi="360" inkscape:export-ydpi="360" inkscape:output_extension="org.inkscape.output.svgz.inkscape" inkscape:version="0.46" sodipodi:docbase="/home/david/oxygen/trunk/scalable/places" sodipodi:docname="folder-bookmarks.svgz" sodipodi:version="0.32" version="1.0" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <metadata>
+ <rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <cc:Work rdf:about="">
+ <dc:title></dc:title>
+ <dc:description></dc:description>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>unsorted</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title></dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title></dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date></dc:date>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/ or http://creativecommons.org/licenses/LGPL/2.1/"/>
+ <dc:language>en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs2813">
+ <linearGradient id="linearGradient3629" inkscape:collect="always">
+ <stop id="stop3631" offset="0" style="stop-color:#ffda36;stop-opacity:1;"/>
+ <stop id="stop3633" offset="1" style="stop-color:#ffda36;stop-opacity:0;"/>
+ </linearGradient>
+ <inkscape:perspective id="perspective99" inkscape:persp3d-origin="64 : 42.666667 : 1" inkscape:vp_x="0 : 64 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="128 : 64 : 1" sodipodi:type="inkscape:persp3d"/>
+ <linearGradient gradientTransform="matrix(1.0033808,0,0,1,-8.2378002,8)" gradientUnits="userSpaceOnUse" id="linearGradient3067" x1="122.74438" x2="122.39215" y1="96.721588" y2="20.043535">
+ <stop id="stop3069" offset="0" style="stop-color:#88c4ff;stop-opacity:1;"/>
+ <stop id="stop3077" offset="0.13053299" style="stop-color:#b3d9ff;stop-opacity:1;"/>
+ <stop id="stop3071" offset="0.72006166" style="stop-color:#71A8F5"/>
+ <stop id="stop3073" offset="1" style="stop-color:#508ed9;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient2822">
+ <stop id="stop2824" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop2826" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.0033404,0,0,1,-8.2374684,8)" gradientUnits="userSpaceOnUse" id="XMLID_9_" x1="71.9995" x2="71.9995" y1="14.2578" y2="19.9583">
+ <stop id="stop46" offset="0.25" style="stop-color:#71a8f5;stop-opacity:0;"/>
+ <stop id="stop48" offset="1" style="stop-color:#0057ae;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.0033876,0,0,1,1.7561237,8)" gradientUnits="userSpaceOnUse" id="XMLID_8_" x1="72.0005" x2="72.0005" y1="96" y2="20.0005">
+ <stop id="stop35" offset="0" style="stop-color:#cfe7ff;stop-opacity:1;"/>
+ <stop id="stop37" offset="0.5917" style="stop-color:#71A8F5"/>
+ <stop id="stop39" offset="1" style="stop-color:#2C72C7"/>
+ </linearGradient>
+ <filter height="1.768" id="filter2807" inkscape:collect="always" width="1.0512" x="-0.0256" y="-0.384">
+ <feGaussianBlur id="feGaussianBlur2809" inkscape:collect="always" stdDeviation="1.28"/>
+ </filter>
+ <linearGradient gradientTransform="translate(-7.999995,8)" gradientUnits="userSpaceOnUse" id="XMLID_6_" x1="72.0005" x2="72.0005" y1="96" y2="4.882812e-04">
+ <stop id="stop7" offset="0" style="stop-color:#00479E"/>
+ <stop id="stop9" offset="0.0769" style="stop-color:#2C72C7"/>
+ <stop id="stop11" offset="0.58579999" style="stop-color:#6ea1df;stop-opacity:1;"/>
+ <stop id="stop13" offset="0.96450001" style="stop-color:#adcbee;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="translate(10,0)" gradientUnits="userSpaceOnUse" id="linearGradient2828" inkscape:collect="always" x1="22.413761" x2="22.413761" xlink:href="#linearGradient2822" y1="28.5" y2="34.472866"/>
+ <linearGradient gradientTransform="matrix(1.0033876,0,0,1,-8.2438763,8)" gradientUnits="userSpaceOnUse" id="linearGradient2838" inkscape:collect="always" x1="72.0005" x2="72.0005" xlink:href="#XMLID_8_" y1="96" y2="20.0005"/>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath2844">
+ <path d="M 133.86613,100.978 C 133.79288,102.369 132.59585,103.5 131.19913,103.5 L 16.812952,103.5 C 15.415236,103.5 14.2192,102.368 14.145947,100.979 C 14.145947,100.979 10.508666,30.967 10.508666,30.957 C 10.461513,30.301 10.673223,29.685 11.105684,29.22 C 11.539144,28.756 12.140172,28.501 12.799403,28.501 L 135.21268,28.501 C 135.8719,28.501 136.47293,28.756 136.90339,29.219 C 137.33384,29.682 137.54254,30.298 137.49137,30.966 L 133.86613,100.978 z" id="path2846" style="fill:#ff00bf"/>
+ </clipPath>
+ <filter height="1.8302754" id="filter2848" inkscape:collect="always" width="1.0247144" x="-0.012357198" y="-0.41513768">
+ <feGaussianBlur id="feGaussianBlur2850" inkscape:collect="always" stdDeviation="0.65388509"/>
+ </filter>
+ <linearGradient gradientTransform="matrix(1.0033808,0,0,1,-8.2378,8)" gradientUnits="userSpaceOnUse" id="linearGradient3109" inkscape:collect="always" x1="122.74438" x2="122.74438" xlink:href="#linearGradient3067" y1="96.721588" y2="20"/>
+ <linearGradient id="linearGradient3290">
+ <stop id="stop3292" offset="0" style="stop-color:yellow;stop-opacity:1;"/>
+ <stop id="stop3294" offset="1" style="stop-color:#ffb66d;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3273">
+ <stop id="stop3275" offset="0" style="stop-color:#ffffff;stop-opacity:0.55035973;"/>
+ <stop id="stop3277" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3291">
+ <stop id="stop3293" offset="0" style="stop-color:black;stop-opacity:1"/>
+ <stop id="stop3295" offset="1" style="stop-color:black;stop-opacity:0"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3638">
+ <stop id="stop3640" offset="0" style="stop-color:#ffffff;stop-opacity:0;"/>
+ <stop id="stop3661" offset="0.06868132" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3659" offset="0.5" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3642" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient12948">
+ <stop id="stop12950" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop12952" offset="1" style="stop-color:#c0c0c0;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient1563">
+ <stop id="stop1565" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop1567" offset="1" style="stop-color:white;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="41.233166" cy="39.832623" fx="41.409943" fy="44.369892" gradientTransform="matrix(1,0,0,0.75,0,9.9581557)" gradientUnits="userSpaceOnUse" id="radialGradient3673" inkscape:collect="always" r="8.1317282" xlink:href="#linearGradient3309"/>
+ <radialGradient cx="41.233166" cy="39.832623" fx="41.409943" fy="44.369892" gradientTransform="matrix(1,0,0,0.75,0,9.9581557)" gradientUnits="userSpaceOnUse" id="radialGradient3669" inkscape:collect="always" r="8.1317282" xlink:href="#linearGradient3309"/>
+ <filter id="filter3663" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3665" inkscape:collect="always" stdDeviation="0.24178075"/>
+ </filter>
+ <radialGradient cx="41.233166" cy="39.832623" fx="41.409943" fy="44.369892" gradientTransform="matrix(1,0,0,0.75,0,9.9581557)" gradientUnits="userSpaceOnUse" id="radialGradient3638" inkscape:collect="always" r="8.1317282" xlink:href="#linearGradient3640"/>
+ <radialGradient cx="122.69361" cy="52.672272" fx="122.55822" fy="51.026066" gradientTransform="matrix(1.2538745,-0.4598801,0.2575547,0.7022291,-43.321636,71.69735)" gradientUnits="userSpaceOnUse" id="radialGradient3620" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <radialGradient cx="121.58587" cy="52.85474" fx="121.58587" fy="52.85474" gradientTransform="matrix(2.2248115,-0.5961364,0.2773621,1.0351295,-163.57967,70.62501)" gradientUnits="userSpaceOnUse" id="radialGradient3618" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <filter height="1.2126913" id="filter3604" inkscape:collect="always" width="1.2307636" x="-0.11538182" y="-0.10634566">
+ <feGaussianBlur id="feGaussianBlur3606" inkscape:collect="always" stdDeviation="0.25849222"/>
+ </filter>
+ <radialGradient cx="122.69361" cy="52.672272" fx="122.55822" fy="51.026066" gradientTransform="matrix(1.2538745,-0.4598801,0.2575547,0.7022291,-43.321636,71.69735)" gradientUnits="userSpaceOnUse" id="radialGradient3570" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <filter height="1.3358284" id="filter3564" inkscape:collect="always" width="1.3643636" x="-0.18218182" y="-0.1679142">
+ <feGaussianBlur id="feGaussianBlur3566" inkscape:collect="always" stdDeviation="0.40814561"/>
+ </filter>
+ <radialGradient cx="121.58587" cy="52.85474" fx="121.58587" fy="52.85474" gradientTransform="matrix(2.2248115,-0.5961364,0.2773621,1.0351295,-163.57967,70.62501)" gradientUnits="userSpaceOnUse" id="radialGradient3382" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <radialGradient cx="64.101562" cy="48.703125" fx="64.101562" fy="48.703125" gradientTransform="matrix(1,0,0,0.6476898,0,17.158608)" gradientUnits="userSpaceOnUse" id="radialGradient3372" inkscape:collect="always" r="56.812496" xlink:href="#linearGradient3309"/>
+ <filter id="filter3362" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3364" inkscape:collect="always" stdDeviation="0.3785028"/>
+ </filter>
+ <radialGradient cx="26.573795" cy="73.493042" fx="35.587811" fy="102.79941" gradientTransform="matrix(4.6812453,-5.2700969e-7,3.571426e-8,0.3172375,-85.242554,44.725131)" gradientUnits="userSpaceOnUse" id="radialGradient3300" inkscape:collect="always" r="60.700562" xlink:href="#linearGradient3946"/>
+ <filter id="filter3285" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3287" inkscape:collect="always" stdDeviation="0.26585998"/>
+ </filter>
+ <radialGradient cx="64.35347" cy="106.71302" fx="64.35347" fy="106.71302" gradientTransform="matrix(0.4443044,1.2598841e-8,0,0.1825067,35.563425,89.646593)" gradientUnits="userSpaceOnUse" id="radialGradient3267" inkscape:collect="always" r="60.700505" xlink:href="#linearGradient3946"/>
+ <radialGradient cx="64.07962" cy="66.197433" fx="64.07962" fy="66.197433" gradientTransform="matrix(1,0,0,0.9554688,0,2.9478533)" gradientUnits="userSpaceOnUse" id="radialGradient3263" inkscape:collect="always" r="60.700504" xlink:href="#XMLID_1_"/>
+ <filter id="filter3259" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3261" inkscape:collect="always" stdDeviation="0.23739589"/>
+ </filter>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2475" inkscape:collect="always" x1="64.07962" x2="64.07962" xlink:href="#XMLID_1_" y1="-14.227339" y2="120.44466"/>
+ <radialGradient cx="64.35347" cy="98.207405" fx="64.35347" fy="98.207405" gradientTransform="matrix(9.1358439e-2,-2.9656957e-8,4.5207376e-8,0.1392616,58.276716,94.261412)" gradientUnits="userSpaceOnUse" id="radialGradient3956" inkscape:collect="always" r="60.700505" xlink:href="#linearGradient3946"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient3934" inkscape:collect="always" spreadMethod="reflect" x1="28.637825" x2="31.289474" xlink:href="#linearGradient3844" y1="120.84999" y2="122.08743"/>
+ <filter id="filter3928" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3930" inkscape:collect="always" stdDeviation="0.18346262"/>
+ </filter>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2728" inkscape:collect="always" spreadMethod="reflect" x1="28.637825" x2="31.289474" xlink:href="#linearGradient3844" y1="120.84999" y2="122.08743"/>
+ <filter height="1.3101371" id="filter3838" inkscape:collect="always" width="1.3563383" x="-0.17816916" y="-0.15506857">
+ <feGaussianBlur id="feGaussianBlur3840" inkscape:collect="always" stdDeviation="0.46259975"/>
+ </filter>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient3800" inkscape:collect="always" spreadMethod="reflect" x1="63.948792" x2="67.219337" xlink:href="#linearGradient3363" y1="12.034382" y2="12.034382"/>
+ <filter id="filter3401" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3403" inkscape:collect="always" stdDeviation="0.11157909"/>
+ </filter>
+ <filter id="filter3391" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3393" inkscape:collect="always" stdDeviation="0.55939545"/>
+ </filter>
+ <linearGradient gradientTransform="translate(-152,0)" gradientUnits="userSpaceOnUse" id="linearGradient3385" inkscape:collect="always" x1="216.88614" x2="216.88614" xlink:href="#linearGradient3379" y1="122.5867" y2="37.969955"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient3369" inkscape:collect="always" x1="177.42397" x2="177.60074" xlink:href="#linearGradient3363" y1="22.377773" y2="93.022789"/>
+ <linearGradient gradientTransform="translate(-37.771032,-0.1213203)" gradientUnits="userSpaceOnUse" id="linearGradient3345" inkscape:collect="always" x1="261.50107" x2="200.17728" xlink:href="#linearGradient1563" y1="77.652245" y2="31.10997"/>
+ <linearGradient gradientTransform="translate(-170.08594,0)" gradientUnits="userSpaceOnUse" id="linearGradient3315" inkscape:collect="always" x1="219.22163" x2="219.22163" xlink:href="#linearGradient3363" y1="28.149843" y2="116.41813"/>
+ <radialGradient cx="69.526619" cy="60.115833" fx="69.526619" fy="60.115833" gradientTransform="matrix(0.5227399,0,-1.554444e-8,0.5266221,349.81061,60.575712)" gradientUnits="userSpaceOnUse" id="radialGradient3304" inkscape:collect="always" r="111.65377" xlink:href="#linearGradient3290"/>
+ <radialGradient cx="69.526619" cy="60.115833" fx="69.526619" fy="89.655701" gradientTransform="matrix(0.9439139,-0.3301918,0.332644,0.9509241,-16.097695,27.249949)" gradientUnits="userSpaceOnUse" id="radialGradient2906" inkscape:collect="always" r="111.65377" xlink:href="#linearGradient3290"/>
+ <linearGradient gradientTransform="matrix(0,1.022977,-1.022977,0,111.9686,137.8125)" gradientUnits="userSpaceOnUse" id="linearGradient3230" inkscape:collect="always" x1="-88.058083" x2="-45.096584" xlink:href="#linearGradient3711" y1="-131.93112" y2="-131.93112"/>
+ <radialGradient cx="343.99899" cy="92" fx="343.99899" fy="92" gradientUnits="userSpaceOnUse" id="radialGradient3228" inkscape:collect="always" r="36" xlink:href="#linearGradient3711"/>
+ <linearGradient gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)" gradientUnits="userSpaceOnUse" id="linearGradient3226" inkscape:collect="always" x1="-70.002899" x2="-11.91648" xlink:href="#linearGradient26907" y1="-383.9971" y2="-383.9971"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2149" inkscape:collect="always" x1="62.112335" x2="67.887672" xlink:href="#linearGradient3081" y1="90.513916" y2="39.095695"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="XMLID_4_" x1="64.000504" x2="64.000504" y1="108.8652" y2="92.865196">
+ <stop id="stop7270" offset="0" style="stop-color:#EEEEEC"/>
+ <stop id="stop7272" offset="1" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <linearGradient gradientTransform="translate(175.0067,11.74752)" gradientUnits="userSpaceOnUse" id="XMLID_3_" x1="63.9995" x2="63.9995" y1="92.865196" y2="120.8652">
+ <stop id="stop7261" offset="0" style="stop-color:#888A85"/>
+ <stop id="stop7263" offset="0.3226" style="stop-color:#A6A7A3"/>
+ <stop id="stop7265" offset="1" style="stop-color:#EEEEEC"/>
+ </linearGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="XMLID_1_" x1="95.693398" x2="32.308601" y1="141.1738" y2="77.789001">
+ <stop id="stop7227" offset="0" style="stop-color:#ffc60a;stop-opacity:1;"/>
+ <stop id="stop7233" offset="1" style="stop-color:#b03b00;stop-opacity:1;"/>
+ </linearGradient>
+ <foreignObject height="1" id="foreignObject7221" requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/" width="1" x="0" y="0">
+ <i:pgfRef xlink:href="#adobe_illustrator_pgf"/>
+ </foreignObject>
+ <linearGradient id="linearGradient5073" inkscape:collect="always">
+ <stop id="stop5075" offset="0" style="stop-color:#000000;stop-opacity:1;"/>
+ <stop id="stop5077" offset="1" style="stop-color:#000000;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="80.738739" cy="155.37218" fx="80.738739" fy="177.29686" gradientTransform="matrix(1,0,0,0.111111,0,138.1081)" gradientUnits="userSpaceOnUse" id="radialGradient5079" inkscape:collect="always" r="64.796692" xlink:href="#linearGradient5073"/>
+ <linearGradient id="linearGradient2690">
+ <stop id="stop2692" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop2694" offset="1" style="stop-color:#c0c0c0;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient2684">
+ <stop id="stop2686" offset="0" style="stop-color:#ffffff;stop-opacity:0.55035973;"/>
+ <stop id="stop2688" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient2678">
+ <stop id="stop2680" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop2682" offset="1" style="stop-color:white;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient2668">
+ <stop id="stop2670" offset="0" style="stop-color:#ffffff;stop-opacity:0;"/>
+ <stop id="stop2672" offset="0.06868132" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop2674" offset="0.5" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop2676" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient2662">
+ <stop id="stop2664" offset="0" style="stop-color:yellow;stop-opacity:1;"/>
+ <stop id="stop2666" offset="1" style="stop-color:#f07800;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3081">
+ <stop id="stop3083" offset="0" style="stop-color:#28691f;stop-opacity:1;"/>
+ <stop id="stop3085" offset="1" style="stop-color:#00bf00;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)" gradientUnits="userSpaceOnUse" id="linearGradient3711" x1="-84.002403" x2="-23.516129" y1="-383.9971" y2="-383.9975">
+ <stop id="stop3713" offset="0" style="stop-color:white;stop-opacity:1;"/>
+ <stop id="stop3715" offset="1" style="stop-color:white;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)" gradientUnits="userSpaceOnUse" id="linearGradient26907" x1="-84.002403" x2="-12.0029" y1="-383.9971" y2="-383.9971">
+ <stop id="stop26909" offset="0" style="stop-color:#888a85;stop-opacity:1;"/>
+ <stop id="stop26911" offset="1" style="stop-color:#2e3436;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3309">
+ <stop id="stop3311" offset="0" style="stop-color:#f4ff3f;stop-opacity:1;"/>
+ <stop id="stop3313" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3363" inkscape:collect="always">
+ <stop id="stop3365" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3367" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3379" inkscape:collect="always">
+ <stop id="stop3381" offset="0" style="stop-color:#fffc07;stop-opacity:1;"/>
+ <stop id="stop3383" offset="1" style="stop-color:#fffc07;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3844" inkscape:collect="always">
+ <stop id="stop3846" offset="0" style="stop-color:#faff64;stop-opacity:1;"/>
+ <stop id="stop3848" offset="1" style="stop-color:#faff64;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient id="linearGradient3946">
+ <stop id="stop3948" offset="0" style="stop-color:#7e0000;stop-opacity:1;"/>
+ <stop id="stop3950" offset="1" style="stop-color:#673400;stop-opacity:0;"/>
+ </linearGradient>
+ <inkscape:perspective id="perspective102" inkscape:persp3d-origin="64 : 42.666667 : 1" inkscape:vp_x="0 : 64 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="128 : 64 : 1" sodipodi:type="inkscape:persp3d"/>
+ <linearGradient id="linearGradient3640">
+ <stop id="stop3643" offset="0" style="stop-color:#7e0000;stop-opacity:0.50990099"/>
+ <stop id="stop3645" offset="1" style="stop-color:#673400;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="64.07962" cy="66.197433" fx="64.07962" fy="66.197433" gradientTransform="matrix(1,0,0,0.9554688,0,2.9478533)" gradientUnits="userSpaceOnUse" id="radialGradient2810" inkscape:collect="always" r="60.700504" xlink:href="#XMLID_1_"/>
+ <radialGradient cx="69.526619" cy="60.115833" fx="69.526619" fy="89.655701" gradientTransform="matrix(0.9439139,-0.3301918,0.332644,0.9509241,-16.097695,27.249949)" gradientUnits="userSpaceOnUse" id="radialGradient2812" inkscape:collect="always" r="111.65377" xlink:href="#linearGradient3290"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2814" inkscape:collect="always" x1="64.07962" x2="64.07962" xlink:href="#XMLID_1_" y1="-14.227339" y2="120.44466"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2816" inkscape:collect="always" spreadMethod="reflect" x1="63.948792" x2="67.219337" xlink:href="#linearGradient3363" y1="12.034382" y2="12.034382"/>
+ <radialGradient cx="64.101562" cy="48.703125" fx="64.101562" fy="48.703125" gradientTransform="matrix(1,0,0,0.6476898,0,17.158608)" gradientUnits="userSpaceOnUse" id="radialGradient2818" inkscape:collect="always" r="56.812496" xlink:href="#linearGradient3309"/>
+ <radialGradient cx="41.233166" cy="39.832623" fx="41.409943" fy="44.369892" gradientTransform="matrix(1,0,0,0.75,0,9.9581557)" gradientUnits="userSpaceOnUse" id="radialGradient2820" inkscape:collect="always" r="8.1317282" xlink:href="#linearGradient3309"/>
+ <linearGradient gradientTransform="translate(-170.08594,0)" gradientUnits="userSpaceOnUse" id="linearGradient2823" inkscape:collect="always" x1="219.22163" x2="219.22163" xlink:href="#linearGradient3363" y1="28.149843" y2="116.41813"/>
+ <radialGradient cx="41.233166" cy="39.832623" fx="41.409943" fy="44.369892" gradientTransform="matrix(1,0,0,0.75,0,9.9581557)" gradientUnits="userSpaceOnUse" id="radialGradient2825" inkscape:collect="always" r="8.1317282" xlink:href="#linearGradient3309"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2827" inkscape:collect="always" x1="177.42397" x2="177.60074" xlink:href="#linearGradient3363" y1="22.377773" y2="93.022789"/>
+ <linearGradient gradientTransform="translate(-37.771032,-0.1213203)" gradientUnits="userSpaceOnUse" id="linearGradient2829" inkscape:collect="always" x1="261.50107" x2="200.17728" xlink:href="#linearGradient1563" y1="77.652245" y2="31.10997"/>
+ <linearGradient gradientTransform="matrix(0.479981,0,0,0.479981,33.177757,62.637707)" gradientUnits="userSpaceOnUse" id="linearGradient2831" inkscape:collect="always" spreadMethod="reflect" x1="80.100487" x2="77.714729" xlink:href="#linearGradient3273" y1="44.807674" y2="101.4734"/>
+ <radialGradient cx="64.35347" cy="98.207405" fx="64.35347" fy="98.207405" gradientTransform="matrix(9.1358439e-2,-2.9656957e-8,4.5207376e-8,0.1392616,58.276716,94.261412)" gradientUnits="userSpaceOnUse" id="radialGradient2833" inkscape:collect="always" r="60.700505" xlink:href="#linearGradient3946"/>
+ <linearGradient gradientTransform="translate(-152,0)" gradientUnits="userSpaceOnUse" id="linearGradient2835" inkscape:collect="always" x1="216.88614" x2="216.88614" xlink:href="#linearGradient3379" y1="122.5867" y2="37.969955"/>
+ <radialGradient cx="121.58587" cy="52.85474" fx="121.58587" fy="52.85474" gradientTransform="matrix(2.2248115,-0.5961364,0.2773621,1.0351295,-163.57967,70.62501)" gradientUnits="userSpaceOnUse" id="radialGradient2837" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <radialGradient cx="122.69361" cy="52.672272" fx="122.55822" fy="51.026066" gradientTransform="matrix(1.2538745,-0.4598801,0.2575547,0.7022291,-43.321636,71.69735)" gradientUnits="userSpaceOnUse" id="radialGradient2839" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <radialGradient cx="121.58587" cy="52.85474" fx="121.58587" fy="52.85474" gradientTransform="matrix(2.2248115,-0.5961364,0.2773621,1.0351295,-163.57967,70.62501)" gradientUnits="userSpaceOnUse" id="radialGradient2841" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <radialGradient cx="122.69361" cy="52.672272" fx="122.55822" fy="51.026066" gradientTransform="matrix(1.2538745,-0.4598801,0.2575547,0.7022291,-43.321636,71.69735)" gradientUnits="userSpaceOnUse" id="radialGradient2843" inkscape:collect="always" r="3.1883843" xlink:href="#linearGradient1563"/>
+ <linearGradient gradientTransform="translate(-152,0)" gradientUnits="userSpaceOnUse" id="linearGradient2845" inkscape:collect="always" x1="216.88614" x2="216.88614" xlink:href="#linearGradient3379" y1="122.5867" y2="37.969955"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient2847" inkscape:collect="always" spreadMethod="reflect" x1="28.637825" x2="31.289474" xlink:href="#linearGradient3844" y1="120.84999" y2="122.08743"/>
+ <radialGradient cx="64.35347" cy="106.71302" fx="64.35347" fy="106.71302" gradientTransform="matrix(0.4443044,1.2598841e-8,0,0.1825067,35.563425,89.646593)" gradientUnits="userSpaceOnUse" id="radialGradient2849" inkscape:collect="always" r="60.700505" xlink:href="#linearGradient3946"/>
+ <radialGradient cx="26.573795" cy="73.493042" fx="35.587811" fy="102.79941" gradientTransform="matrix(4.6812453,-5.2700969e-7,3.571426e-8,0.3172375,-85.242554,44.725131)" gradientUnits="userSpaceOnUse" id="radialGradient2851" inkscape:collect="always" r="60.700562" xlink:href="#linearGradient3946"/>
+ <linearGradient gradientTransform="matrix(0.479981,0,0,0.479981,33.177757,62.637707)" gradientUnits="userSpaceOnUse" id="linearGradient2853" inkscape:collect="always" spreadMethod="reflect" x1="80.100487" x2="77.714729" xlink:href="#linearGradient3273" y1="44.807674" y2="101.4734"/>
+ <linearGradient gradientTransform="translate(-152,0)" gradientUnits="userSpaceOnUse" id="linearGradient2855" inkscape:collect="always" x1="216.88614" x2="216.88614" xlink:href="#linearGradient3379" y1="122.5867" y2="37.969955"/>
+ <radialGradient cx="66.977158" cy="54.133701" fx="66.977158" fy="54.133701" gradientTransform="matrix(1.6011045,0,0,1.0874708,-40.260269,-12.584491)" gradientUnits="userSpaceOnUse" id="radialGradient3635" inkscape:collect="always" r="41.380203" xlink:href="#linearGradient3629"/>
+ <filter id="filter3653" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur3655" inkscape:collect="always" stdDeviation="2.160955"/>
+ </filter>
+ <radialGradient cx="64.111018" cy="72.034855" fx="64.111018" fy="72.034855" gradientTransform="matrix(1,0,0,0.6792004,0,23.108756)" gradientUnits="userSpaceOnUse" id="radialGradient3659" inkscape:collect="always" r="41.380203" xlink:href="#linearGradient3629"/>
+ <radialGradient cx="66.890625" cy="94.021507" fx="66.890625" fy="94.021507" gradientTransform="matrix(1.3950344,0,0,0.9475079,-26.4241,-2.1179212)" gradientUnits="userSpaceOnUse" id="radialGradient3663" inkscape:collect="always" r="41.380203" xlink:href="#linearGradient3629"/>
+ </defs>
+ <sodipodi:namedview bordercolor="#666666" borderlayer="true" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="1.7230397" inkscape:cy="67.535605" inkscape:document-units="px" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:showpageshadow="true" inkscape:window-height="750" inkscape:window-width="991" inkscape:window-x="45" inkscape:window-y="0" inkscape:zoom="2.34375" pagecolor="#ffffff" showborder="true" showgrid="true">
+ <inkscape:grid enabled="true" id="grid3665" spacingx="4px" spacingy="4px" type="xygrid" visible="true"/>
+ </sodipodi:namedview>
+ <g id="layer1" inkscape:groupmode="layer" inkscape:label="Livello 1">
+ <path d="M 117.983,19 C 117.992,17.35 116.64999,16 114.99999,16 L 44.961007,16 L 38.375006,9.414 C 37.597005,8.636 36.061006,8 34.961,8 L 13.999998,8 C 11.799996,8 9.999999,9.8 9.999999,12 L 9.999999,19 C 9.999999,19 10.999999,104 7,104 L 121,104 C 116.99999,104 117.983,19 117.983,19 z" id="path15" style="fill:url(#XMLID_6_)"/>
+ <g id="g17" style="opacity:0.6;filter:url(#filter2807)" transform="matrix(1.0033404,0,0,1,-8.2374684,8)">
+ <path d="M 132,95.146667 C 132,96.877333 128.4,98.293333 124,98.293333 L 20,98.293333 C 15.6,98.293333 12,96.877333 12,95.146667 C 12,93.416 15.6,92 20,92 L 124,92 C 128.4,92 132,93.416 132,95.146667 z" id="path19"/>
+ </g>
+ <path d="M 124.36598,101.004 C 124.27969,102.652 122.85389,104 121.19831,104 L 6.812906,104 C 5.157329,104 3.731522,102.652 3.644228,101.004 L 0.007982,30.992 C -0.112423,29.347 1.143808,28 2.799384,28 L 125.21183,28 C 126.86741,28 128.11762,29.346 127.9912,30.991 L 124.36598,101.004 z" id="path30" style="opacity:0.9;fill:url(#linearGradient3109);fill-opacity:1"/>
+ <path d="M 10.580094,28 C 10.551086,23.609 10.516087,20.392 10.500078,19 L 10.500078,12 C 10.500078,10.07 12.07033,8.5 14.000627,8.5 L 34.964921,8.5 C 35.937077,8.5 37.339294,9.081 38.026407,9.768 L 44.759457,16.5 L 115.01648,16.5 C 115.68059,16.5 116.30469,16.76 116.77576,17.233 C 117.24683,17.706 117.50487,18.333 117.50087,18.997 C 117.50087,18.997 117.57288,18.925 117.60989,18.888 C 117.48787,19.495 117.47487,21.062 117.44687,25.161 L 117.43687,28 L 117.92894,28 C 117.95994,22.468 117.99995,19 117.99995,19 C 118.00896,17.35 116.66674,16 115.01648,16 L 44.966498,16 L 38.379463,9.414 C 37.60134,8.636 36.065099,8 34.964921,8 L 14.000627,8 C 11.80028,8 10,9.8 10,12 L 10,19 C 10,19 10.041009,22.468 10.073018,28 L 10.580094,28 z" id="path32" style="fill:#5293ee;fill-opacity:1"/>
+ <path d="M 9.8848616,22 C 9.8938916,23.75 9.9029216,25.755 9.9109516,28 L 118.09511,28 C 118.10414,25.755 118.11317,23.75 118.1212,22 L 9.8848616,22 z" id="path50" style="fill:url(#XMLID_9_);opacity:0.5"/>
+ <path clip-path="url(#clipPath2844)" d="M 12.8125,28.5 C 12.153269,28.5 11.52721,28.754751 11.09375,29.21875 C 10.661289,29.68375 10.452847,30.312749 10.5,30.96875 C 10.5,30.968937 10.559992,32.232788 10.5625,32.28125 C 10.647737,31.886257 10.815287,31.518165 11.09375,31.21875 C 11.52721,30.754751 12.153269,30.5 12.8125,30.5 L 135.21875,30.5 C 135.87797,30.500001 136.47579,30.75575 136.90625,31.21875 C 137.18342,31.516877 137.35439,31.883216 137.4375,32.28125 L 137.5,30.96875 C 137.55117,30.30075 137.3367,29.68175 136.90625,29.21875 C 136.47579,28.75575 135.87797,28.500001 135.21875,28.5 L 12.8125,28.5 z" id="path2788" style="fill:url(#linearGradient2828);fill-opacity:1;filter:url(#filter2848)" transform="translate(-10,0)"/>
+ <path d="M 125.21268,28 L 2.799403,28 C 1.143815,28 -0.1124237,29.347 0.007982,30.992 L 3.645263,101.004 C 3.731547,102.652 5.157364,104 6.812952,104 L 121.19913,104 C 122.85472,104 124.28053,102.652 124.36682,101.004 L 127.99106,30.991 C 128.11849,29.346 126.86827,28 125.21268,28 z M 123.86613,100.978 C 123.79288,102.369 122.59585,103.5 121.19913,103.5 L 6.812952,103.5 C 5.415236,103.5 4.2192,102.368 4.145947,100.979 C 4.145947,100.979 0.508666,30.967 0.508666,30.957 C 0.461513,30.301 0.673223,29.685 1.105684,29.22 C 1.539144,28.756 2.140172,28.501 2.799403,28.501 L 125.21268,28.501 C 125.8719,28.501 126.47293,28.756 126.90339,29.219 C 127.33384,29.682 127.54254,30.298 127.49137,30.966 L 123.86613,100.978 z" id="path2836" style="fill:url(#linearGradient2838)"/>
+ <path d="M 65.931929,44.786008 C 55.175741,44.634267 51.043165,70.978907 35.902168,70.978907 L 97.750451,73.3925 C 81.772038,73.3925 79.337655,44.975127 65.931929,44.786008 z" id="path2857" sodipodi:nodetypes="cccs" style="fill:url(#radialGradient3635);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3653)" transform="matrix(1.0562371,0,0,1.0562371,-3.6968591,-4.2252662)"/>
+ <g id="g2758" inkscape:label="Layer 1" transform="matrix(0.6943204,0,0,0.6943204,21.245436,37.651516)">
+ <g id="g3675">
+ <path d="M 64.03125,8.2 C 56.162818,8.2100117 46.828561,34.754451 40.46875,39.3875 C 34.10894,44.020548 5.9844574,44.776082 3.5625,52.2625 C 1.1405426,59.748917 23.465249,76.813524 25.90625,84.29375 C 28.347251,91.77398 20.40967,118.7394 26.78125,123.35625 C 33.15283,127.9731 56.287818,112.02251 64.15625,112.0125 C 72.024682,112.00249 95.202691,127.89555 101.5625,123.2625 C 107.92231,118.62945 99.890544,91.686414 102.3125,84.2 C 104.73446,76.713583 127.03475,59.58648 124.59375,52.10625 C 122.15275,44.626021 94.027829,43.941849 87.65625,39.325 C 81.28467,34.708152 71.899685,8.1899879 64.03125,8.2 z M 64.03125,12.10625 C 64.208046,12.245423 65.56776,12.912264 67.15625,14.85625 C 68.97167,17.077947 71.031426,20.410059 73.0625,23.95 C 75.093573,27.48994 77.113982,31.248819 79.09375,34.5125 C 81.073519,37.776182 82.75512,40.528991 85.40625,42.45 C 88.057376,44.371009 91.18831,45.11637 94.90625,45.98125 C 98.624192,46.846129 102.81606,47.591152 106.8125,48.41875 C 110.80894,49.246347 114.60465,50.166787 117.28125,51.2 C 119.62327,52.104061 120.71845,53.200764 120.90625,53.325 C 120.82618,53.533062 120.57672,54.994782 119.21875,57.10625 C 117.66679,59.519356 115.1453,62.518181 112.40625,65.54375 C 109.66721,68.569316 106.71091,71.652346 104.21875,74.54375 C 101.72659,77.435155 99.632744,79.897501 98.625,83.0125 C 97.617256,86.127495 97.892393,89.334266 98.21875,93.1375 C 98.545107,96.940738 99.114622,101.17466 99.5625,105.23125 C 100.01038,109.28783 100.31178,113.17888 100.15625,116.04375 C 100.02016,118.55052 99.34151,119.89095 99.28125,120.10625 C 99.057443,120.09786 97.552762,120.37027 95.125,119.73125 C 92.350417,119.00093 88.723899,117.49504 85,115.825 C 81.276103,114.15497 77.426259,112.30169 73.90625,110.825 C 70.386242,109.3483 67.4302,108.13334 64.15625,108.1375 C 60.882303,108.14167 57.891241,109.3706 54.375,110.85625 C 50.858761,112.3419 47.032137,114.20799 43.3125,115.8875 C 39.592862,117.567 35.960216,119.05638 33.1875,119.79375 C 30.761373,120.43895 29.286908,120.19088 29.0625,120.2 C 29.004012,119.9864 28.29872,118.6439 28.15625,116.1375 C 27.993428,113.27303 28.281199,109.38271 28.71875,105.325 C 29.156299,101.2673 29.714573,97.035302 30.03125,93.23125 C 30.347928,89.427198 30.609418,86.187425 29.59375,83.075 C 28.578082,79.962573 26.468263,77.522553 23.96875,74.6375 C 21.469238,71.752452 18.527988,68.687339 15.78125,65.66875 C 13.034512,62.650158 10.495601,59.671649 8.9375,57.2625 C 7.5741618,55.154496 7.3592053,53.657399 7.28125,53.45 C 7.2962039,53.537785 8.2681026,52.326785 10.84375,51.325 C 13.517705,50.284977 17.34943,49.350265 21.34375,48.5125 C 25.33807,47.674737 29.534272,46.949339 33.25,46.075 C 36.96573,45.200663 40.103767,44.44025 42.75,42.5125 C 45.396234,40.584748 47.059794,37.812458 49.03125,34.54375 C 51.002705,31.275042 53.009191,27.526347 55.03125,23.98125 C 57.053308,20.436153 59.096493,17.08256 60.90625,14.85625 C 62.489787,12.908229 63.857465,12.244552 64.03125,12.10625 z" id="path3961" style="opacity:1;fill:url(#radialGradient2810);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3259)"/>
+ <path d="M 153.09403,94.713757 C 144.53658,107.09689 92.616372,93.013297 78.414631,98.001518 C 64.21289,102.98974 32.50348,146.4474 18.082028,142.13539 C 3.6605746,137.82337 1.0106378,84.092245 -8.1220219,72.127031 C -17.254681,60.161818 -68.384124,43.433534 -68.739625,28.385431 C -69.095125,13.337327 -18.812666,-5.7867426 -10.255219,-18.169872 C -1.697772,-30.553002 -1.5880954,-84.349316 12.613645,-89.337536 C 26.815387,-94.325757 60.541592,-52.41396 74.963045,-48.101941 C 89.384498,-43.789923 140.58172,-60.30959 149.71438,-48.344376 C 158.84704,-36.379162 129.40853,8.6478227 129.76403,23.695927 C 130.11953,38.74403 161.65148,82.330628 153.09403,94.713757 z" id="path3574" inkscape:flatsided="false" inkscape:randomized="0" inkscape:rounded="0.20136392" sodipodi:arg1="0.60469864" sodipodi:arg2="1.2330172" sodipodi:cx="52.952892" sodipodi:cy="25.510532" sodipodi:r1="121.72647" sodipodi:r2="76.832565" sodipodi:sides="5" sodipodi:type="star" style="opacity:1;fill:url(#radialGradient2812);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" transform="matrix(0.4934214,0.1726044,-0.1726044,0.4934214,42.377875,49.908537)"/>
+ <path d="M 64.03125,8 C 56.162818,8.0100117 46.828561,34.554451 40.46875,39.1875 C 34.10894,43.820548 5.984457,44.576082 3.5625,52.0625 C 1.140543,59.548917 23.465249,76.613524 25.90625,84.09375 C 28.347251,91.57398 20.40967,118.5394 26.78125,123.15625 C 33.15283,127.7731 56.287818,111.82251 64.15625,111.8125 C 72.024682,111.80249 95.202691,127.69555 101.5625,123.0625 C 107.92231,118.42945 99.890544,91.486414 102.3125,84 C 104.73446,76.513583 127.03475,59.38648 124.59375,51.90625 C 122.15275,44.426021 94.027829,43.741849 87.65625,39.125 C 81.28467,34.508152 71.899685,7.9899879 64.03125,8 z M 64.03125,11.90625 C 64.208046,12.045423 65.56776,12.712264 67.15625,14.65625 C 68.97167,16.877947 71.031426,20.210059 73.0625,23.75 C 75.093573,27.28994 77.113982,31.048819 79.09375,34.3125 C 81.073519,37.576182 82.75512,40.328991 85.40625,42.25 C 88.057376,44.171009 91.18831,44.91637 94.90625,45.78125 C 98.624192,46.646129 102.81606,47.391152 106.8125,48.21875 C 110.80894,49.046347 114.60465,49.966787 117.28125,51 C 119.62327,51.904061 120.71845,53.000764 120.90625,53.125 C 120.82618,53.333062 120.57672,54.794782 119.21875,56.90625 C 117.66679,59.319356 115.1453,62.318181 112.40625,65.34375 C 109.66721,68.369316 106.71091,71.452346 104.21875,74.34375 C 101.72659,77.235155 99.632744,79.697501 98.625,82.8125 C 97.617256,85.927495 97.892393,89.134266 98.21875,92.9375 C 98.545107,96.740738 99.114622,100.97466 99.5625,105.03125 C 100.01038,109.08783 100.31178,112.97888 100.15625,115.84375 C 100.02016,118.35052 99.34151,119.69095 99.28125,119.90625 C 99.057443,119.89786 97.552762,120.17027 95.125,119.53125 C 92.350417,118.80093 88.723899,117.29504 85,115.625 C 81.276103,113.95497 77.426259,112.10169 73.90625,110.625 C 70.386242,109.1483 67.4302,107.93334 64.15625,107.9375 C 60.882303,107.94167 57.891241,109.1706 54.375,110.65625 C 50.858761,112.1419 47.032137,114.00799 43.3125,115.6875 C 39.592862,117.367 35.960216,118.85638 33.1875,119.59375 C 30.761373,120.23895 29.286908,119.99088 29.0625,120 C 29.004012,119.7864 28.29872,118.4439 28.15625,115.9375 C 27.993428,113.07303 28.281199,109.18271 28.71875,105.125 C 29.156299,101.0673 29.714573,96.835302 30.03125,93.03125 C 30.347928,89.227198 30.609418,85.987425 29.59375,82.875 C 28.578082,79.762573 26.468263,77.322553 23.96875,74.4375 C 21.469238,71.552452 18.527988,68.487339 15.78125,65.46875 C 13.034512,62.450158 10.495601,59.471649 8.9375,57.0625 C 7.574162,54.954496 7.359205,53.457399 7.28125,53.25 C 7.296204,53.337785 8.268103,52.126785 10.84375,51.125 C 13.517705,50.084977 17.34943,49.150265 21.34375,48.3125 C 25.33807,47.474737 29.534272,46.749339 33.25,45.875 C 36.96573,45.000663 40.103767,44.24025 42.75,42.3125 C 45.396234,40.384748 47.059794,37.612458 49.03125,34.34375 C 51.002705,31.075042 53.009191,27.326347 55.03125,23.78125 C 57.053308,20.236153 59.096493,16.88256 60.90625,14.65625 C 62.489787,12.708229 63.857465,12.044552 64.03125,11.90625 z" id="path2304" style="opacity:1;fill:url(#linearGradient2814);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 60.98796,9.471226 C 62.846491,8.2143022 64.889907,8.0204702 67.219338,9.471226 L 64.037358,15.614216 L 60.98796,9.471226 z" id="path3409" sodipodi:nodetypes="cccc" style="fill:url(#linearGradient2816);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3838)"/>
+ <path d="M 64.039064,11.90625 C 63.865274,12.044552 62.497594,12.708229 60.914064,14.65625 C 59.104304,16.88256 57.061124,20.236153 55.039064,23.78125 C 53.017004,27.326347 51.010514,31.075042 49.039064,34.34375 C 47.067604,37.612458 45.404044,40.384748 42.757814,42.3125 C 40.111574,44.24025 36.973544,45.000663 33.257814,45.875 C 29.542084,46.749339 25.345884,47.474737 21.351564,48.3125 C 17.357244,49.150265 13.525514,50.084977 10.851564,51.125 C 8.2759131,52.126785 7.3040131,53.337785 7.2890631,53.25 C 7.3670131,53.457399 7.5819731,54.954496 8.9453131,57.0625 C 10.503414,59.471649 13.042324,62.450158 15.789064,65.46875 C 18.535804,68.487339 21.477054,71.552452 23.976564,74.4375 C 26.476074,77.322553 28.585894,79.762573 29.601564,82.875 C 29.865144,83.682722 30.019904,84.511238 30.132814,85.34375 C 32.540654,85.431079 34.961934,85.5 37.414064,85.5 C 64.456484,85.5 88.974124,80.107134 106.91406,71.34375 C 108.71383,69.370041 110.60784,67.338911 112.41406,65.34375 C 115.15311,62.318181 117.67459,59.319356 119.22656,56.90625 C 120.58453,54.794782 120.83398,53.333062 120.91406,53.125 C 120.72626,53.000764 119.63107,51.904061 117.28906,51 C 114.61246,49.966787 110.81674,49.046347 106.82031,48.21875 C 102.82387,47.391152 98.631994,46.646129 94.914064,45.78125 C 91.196124,44.91637 88.065184,44.171009 85.414064,42.25 C 82.762934,40.328991 81.081334,37.576182 79.101564,34.3125 C 77.121794,31.048819 75.101384,27.28994 73.070314,23.75 C 71.039234,20.210059 68.979484,16.877947 67.164064,14.65625 C 65.575574,12.712264 64.215854,12.045423 64.039064,11.90625 z" id="path3370" style="opacity:1;fill:url(#radialGradient2818);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 44.96875,33.9375 L 44.1875,35.21875 C 44.177097,35.219516 44.166653,35.219516 44.15625,35.21875 L 42.8125,36.9375 L 41.53125,38.46875 C 41.521947,38.480223 41.511473,38.490697 41.5,38.5 L 40.03125,39.71875 C 40.021947,39.730223 40.011473,39.740697 40,39.75 L 37.125,41.03125 C 37.115697,41.042723 37.105223,41.053197 37.09375,41.0625 L 33.875,41.96875 L 33.375,45.625 L 35.125,45.21875 L 35.125,45.25 L 39.71875,43.75 L 39.75,43.75 L 42.8125,42.0625 L 42.875,42.03125 L 45.78125,39.09375 L 45.8125,39.0625 L 48.96875,34.03125 L 44.96875,33.9375 z" id="path3667" inkscape:original="M 44.84375 33.71875 L 44 35.09375 L 42.65625 36.8125 L 41.375 38.34375 L 39.90625 39.5625 L 37.03125 40.84375 L 33.6875 41.78125 L 33.09375 45.9375 L 35.1875 45.4375 L 39.8125 43.9375 L 42.96875 42.21875 L 45.96875 39.21875 L 49.375 33.8125 L 44.84375 33.71875 z " inkscape:radius="-0.21269609" sodipodi:type="inkscape:offset" style="opacity:0.64356432;fill:url(#radialGradient2820);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3663)" transform="translate(0,0.3)"/>
+ <path d="M 64.039064,11.90625 C 63.865274,12.044552 62.497594,12.708229 60.914064,14.65625 C 59.104304,16.88256 57.061124,20.236153 55.039064,23.78125 C 53.017004,27.326347 51.010514,31.075042 49.039064,34.34375 C 47.067604,37.612458 45.404044,40.384748 42.757814,42.3125 C 40.111574,44.24025 36.973544,45.000663 33.257814,45.875 C 29.542084,46.749339 25.345884,47.474737 21.351564,48.3125 C 17.357244,49.150265 13.525514,50.084977 10.851564,51.125 C 8.2759131,52.126785 7.3040131,53.337785 7.2890631,53.25 C 7.3670131,53.457399 7.5819731,54.954496 8.9453131,57.0625 C 10.503414,59.471649 13.042324,62.450158 15.789064,65.46875 C 18.535804,68.487339 21.477054,71.552452 23.976564,74.4375 C 26.476074,77.322553 28.585894,79.762573 29.601564,82.875 C 29.865144,83.682722 30.019904,84.511238 30.132814,85.34375 C 32.540654,85.431079 34.961934,85.5 37.414064,85.5 C 64.456484,85.5 88.974124,80.107134 106.91406,71.34375 C 108.71383,69.370041 110.60784,67.338911 112.41406,65.34375 C 115.15311,62.318181 117.67459,59.319356 119.22656,56.90625 C 120.58453,54.794782 120.83398,53.333062 120.91406,53.125 C 120.72626,53.000764 119.63107,51.904061 117.28906,51 C 114.61246,49.966787 110.81674,49.046347 106.82031,48.21875 C 102.82387,47.391152 98.631994,46.646129 94.914064,45.78125 C 91.196124,44.91637 88.065184,44.171009 85.414064,42.25 C 82.762934,40.328991 81.081334,37.576182 79.101564,34.3125 C 77.121794,31.048819 75.101384,27.28994 73.070314,23.75 C 71.039234,20.210059 68.979484,16.877947 67.164064,14.65625 C 65.575574,12.712264 64.215854,12.045423 64.039064,11.90625 z" id="path2910" style="opacity:1;fill:url(#linearGradient2823);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 44.96875,33.9375 L 44.1875,35.21875 C 44.177097,35.219516 44.166653,35.219516 44.15625,35.21875 L 42.8125,36.9375 L 41.53125,38.46875 C 41.521947,38.480223 41.511473,38.490697 41.5,38.5 L 40.03125,39.71875 C 40.021947,39.730223 40.011473,39.740697 40,39.75 L 37.125,41.03125 C 37.115697,41.042723 37.105223,41.053197 37.09375,41.0625 L 33.875,41.96875 L 33.375,45.625 L 35.125,45.21875 L 35.125,45.25 L 39.71875,43.75 L 39.75,43.75 L 42.8125,42.0625 L 42.875,42.03125 L 45.78125,39.09375 L 45.8125,39.0625 L 48.96875,34.03125 L 44.96875,33.9375 z" id="path3671" inkscape:original="M 44.84375 33.71875 L 44 35.09375 L 42.65625 36.8125 L 41.375 38.34375 L 39.90625 39.5625 L 37.03125 40.84375 L 33.6875 41.78125 L 33.09375 45.9375 L 35.1875 45.4375 L 39.8125 43.9375 L 42.96875 42.21875 L 45.96875 39.21875 L 49.375 33.8125 L 44.84375 33.71875 z " inkscape:radius="-0.21269609" sodipodi:type="inkscape:offset" style="opacity:0.64356432;fill:url(#radialGradient2825);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3663)" transform="matrix(-1,0,0,1,128.125,0.3)"/>
+ <g id="g3339" transform="translate(-132.29928,0)">
+ <path d="M 196.34375,11.90625 C 196.16996,12.044552 194.80228,12.708229 193.21875,14.65625 C 191.40899,16.88256 189.36581,20.236153 187.34375,23.78125 C 185.32169,27.326347 183.3152,31.075042 181.34375,34.34375 C 179.37229,37.612458 177.70873,40.384748 175.0625,42.3125 C 172.41626,44.24025 169.27823,45.000663 165.5625,45.875 C 161.84677,46.749339 157.65057,47.474737 153.65625,48.3125 C 149.66193,49.150265 145.8302,50.084977 143.15625,51.125 C 140.5806,52.126785 139.6087,53.337785 139.59375,53.25 C 139.62377,53.329884 139.71528,53.638731 139.84375,54.0625 C 140.2595,53.69998 141.25985,52.862595 143.15625,52.125 C 145.8302,51.084977 149.66193,50.150265 153.65625,49.3125 C 157.65057,48.474737 161.84677,47.749339 165.5625,46.875 C 169.27823,46.000663 172.41626,45.24025 175.0625,43.3125 C 177.70873,41.384748 179.37229,38.612458 181.34375,35.34375 C 183.3152,32.075042 185.32169,28.326347 187.34375,24.78125 C 189.36581,21.236153 191.40899,17.88256 193.21875,15.65625 C 194.80228,13.708229 196.16996,13.044552 196.34375,12.90625 C 196.52054,13.045423 197.88026,13.712264 199.46875,15.65625 C 201.28417,17.877947 203.34392,21.210059 205.375,24.75 C 207.40607,28.28994 209.42648,32.048819 211.40625,35.3125 C 213.38602,38.576182 215.06762,41.328991 217.71875,43.25 C 220.36987,45.171009 223.50081,45.91637 227.21875,46.78125 C 230.93668,47.646129 235.12856,48.391152 239.125,49.21875 C 243.12143,50.046347 246.91715,50.966787 249.59375,52 C 251.51448,52.74144 252.56925,53.579608 253,53.9375 C 253.13371,53.522484 253.18802,53.204851 253.21875,53.125 C 253.03095,53.000764 251.93576,51.904061 249.59375,51 C 246.91715,49.966787 243.12143,49.046347 239.125,48.21875 C 235.12856,47.391152 230.93668,46.646129 227.21875,45.78125 C 223.50081,44.91637 220.36987,44.171009 217.71875,42.25 C 215.06762,40.328991 213.38602,37.576182 211.40625,34.3125 C 209.42648,31.048819 207.40607,27.28994 205.375,23.75 C 203.34392,20.210059 201.28417,16.877947 199.46875,14.65625 C 197.88026,12.712264 196.52054,12.045423 196.34375,11.90625 z" id="path3317" style="opacity:1;fill:url(#linearGradient2827);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 246.78125,49.937636 C 247.42469,50.142466 248.03845,50.379526 248.59375,50.593886 C 250.93576,51.497946 252.03095,52.594656 252.21875,52.718886 C 252.13867,52.926956 251.88922,54.388676 250.53125,56.500136 C 248.97928,58.913246 246.4578,61.912066 243.71875,64.937636 C 241.91253,66.932796 240.01852,68.963926 238.21875,70.937636 C 220.27881,79.701026 195.76117,85.093886 168.71875,85.093886 C 166.59433,85.093886 164.49568,85.039506 162.40625,84.968886 C 162.4184,85.051736 162.42625,85.135936 162.4375,85.218886 C 164.84534,85.306216 167.26662,85.375136 169.71875,85.375136 C 196.76117,85.375136 221.27881,79.982276 239.21875,71.218886 C 241.01852,69.245176 242.91253,67.214046 244.71875,65.218886 C 247.4578,62.193316 249.97928,59.194496 251.53125,56.781386 C 252.88922,54.669926 253.13867,53.208206 253.21875,53.000136 C 253.03095,52.875906 251.93576,51.779196 249.59375,50.875136 C 248.75868,50.552786 247.80629,50.238636 246.78125,49.937636 z" id="path3325" sodipodi:nodetypes="cscsscsccscsscsc" style="opacity:1;fill:url(#linearGradient2829);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ </g>
+ <path d="M 25.190679,119.77989 C 26.414679,122.74238 27.241162,124.11897 31.289475,123.31542 L 30.638356,120.21008 L 29.079766,120.3986 L 28.261711,118.57341 L 25.190679,119.77989 z" id="path3842" sodipodi:nodetypes="cccccc" style="opacity:0.7715356;fill:url(#linearGradient2831);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3928)"/>
+ <path d="M 26.03125,94.25 C 24.983755,105.1142 22.21942,119.85075 26.78125,123.15625 C 33.15283,127.7731 56.287818,111.82251 64.15625,111.8125 C 72.024682,111.80249 95.202691,127.69555 101.5625,123.0625 C 106.10279,119.75495 103.30815,105.10184 102.21875,94.25 L 98.34375,94.25 C 98.677864,97.707156 99.164649,101.42777 99.5625,105.03125 C 100.01038,109.08783 100.31178,112.97888 100.15625,115.84375 C 100.02016,118.35052 99.34151,119.69095 99.28125,119.90625 C 99.057443,119.89786 97.552762,120.17027 95.125,119.53125 C 92.350417,118.80093 88.723899,117.29504 85,115.625 C 81.276103,113.95497 77.426259,112.10169 73.90625,110.625 C 70.386242,109.1483 67.4302,107.93334 64.15625,107.9375 C 60.882303,107.94167 57.891241,109.1706 54.375,110.65625 C 50.858761,112.1419 47.032137,114.00799 43.3125,115.6875 C 39.592862,117.367 35.960216,118.85638 33.1875,119.59375 C 30.761373,120.23895 29.286908,119.99088 29.0625,120 C 29.004012,119.7864 28.29872,118.4439 28.15625,115.9375 C 27.993428,113.07303 28.281199,109.18271 28.71875,105.125 C 29.110886,101.48845 29.580993,97.733027 29.90625,94.25 L 26.03125,94.25 z" id="path3936" style="opacity:1;fill:url(#radialGradient2833);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 64.125001,11.90625 C 63.951211,12.044552 62.583531,12.708229 61.000001,14.65625 C 59.190241,16.88256 57.147061,20.236153 55.125001,23.78125 C 53.102941,27.326347 51.096451,31.075042 49.125001,34.34375 C 47.153541,37.612458 45.489981,40.384748 42.843751,42.3125 C 40.197511,44.24025 37.059481,45.000663 33.343751,45.875 C 29.628021,46.749339 25.431821,47.474737 21.437501,48.3125 C 17.443181,49.150265 13.611451,50.084977 10.937501,51.125 C 8.3618506,52.126785 7.3899506,53.337785 7.3750006,53.25 C 7.4529506,53.457399 7.6679106,54.954496 9.0312506,57.0625 C 10.589351,59.471649 13.128261,62.450158 15.875001,65.46875 C 18.621741,68.487339 21.562991,71.552452 24.062501,74.4375 C 26.562011,77.322553 28.671831,79.762573 29.687501,82.875 C 30.703171,85.987425 30.441681,89.227198 30.125001,93.03125 C 29.808321,96.835302 29.250051,101.0673 28.812501,105.125 C 28.374951,109.18271 28.087181,113.07303 28.250001,115.9375 C 28.392471,118.4439 29.097761,119.7864 29.156251,120 C 29.380661,119.99088 30.855121,120.23895 33.281251,119.59375 C 36.053961,118.85638 39.686611,117.367 43.406251,115.6875 C 47.125881,114.00799 50.952511,112.1419 54.468751,110.65625 C 57.984991,109.1706 60.976051,107.94167 64.250001,107.9375 C 67.523951,107.93334 70.479991,109.1483 74.000001,110.625 C 77.520011,112.10169 81.369851,113.95497 85.093751,115.625 C 88.817651,117.29504 92.444151,118.80093 95.218751,119.53125 C 97.646511,120.17027 99.151181,119.89786 99.375001,119.90625 C 99.435261,119.69095 100.1139,118.35052 100.25,115.84375 C 100.40553,112.97888 100.10412,109.08783 99.656251,105.03125 C 99.208371,100.97466 98.638841,96.740738 98.312501,92.9375 C 97.986141,89.134266 97.710991,85.927495 98.718751,82.8125 C 99.726491,79.697501 101.82033,77.235155 104.3125,74.34375 C 106.80466,71.452346 109.76095,68.369316 112.5,65.34375 C 115.23905,62.318181 117.76053,59.319356 119.3125,56.90625 C 120.67047,54.794782 120.91992,53.333062 121,53.125 C 120.8122,53.000764 119.71701,51.904061 117.375,51 C 114.6984,49.966787 110.90268,49.046347 106.90625,48.21875 C 102.90981,47.391152 98.717931,46.646129 95.000001,45.78125 C 91.282061,44.91637 88.151121,44.171009 85.500001,42.25 C 82.848871,40.328991 81.167271,37.576182 79.187501,34.3125 C 77.207731,31.048819 75.187321,27.28994 73.156251,23.75 C 71.125171,20.210059 69.065421,16.877947 67.250001,14.65625 C 65.661511,12.712264 64.301791,12.045423 64.125001,11.90625 z" id="path3375" sodipodi:nodetypes="cssssssssssssssscssssssscssssssscsssssssc" style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2835);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3391)"/>
+ <g id="g3612" transform="matrix(-1,0,0,1,128.17175,0)">
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3614" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient2837);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)"/>
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3616" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient2839);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3604)"/>
+ </g>
+ <g id="g3608">
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3374" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient2841);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)"/>
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3568" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient2843);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3604)"/>
+ </g>
+ <path d="M 64.125001,11.90625 C 63.951211,12.044552 62.583531,12.708229 61.000001,14.65625 C 59.190241,16.88256 57.147061,20.236153 55.125001,23.78125 C 53.102941,27.326347 51.096451,31.075042 49.125001,34.34375 C 47.153541,37.612458 45.489981,40.384748 42.843751,42.3125 C 40.197511,44.24025 37.059481,45.000663 33.343751,45.875 C 29.628021,46.749339 25.431821,47.474737 21.437501,48.3125 C 17.443181,49.150265 13.611451,50.084977 10.937501,51.125 C 8.3618506,52.126785 7.3899506,53.337785 7.3750006,53.25 C 7.4529506,53.457399 7.6679106,54.954496 9.0312506,57.0625 C 10.589351,59.471649 13.128261,62.450158 15.875001,65.46875 C 18.621741,68.487339 21.562991,71.552452 24.062501,74.4375 C 26.562011,77.322553 28.671831,79.762573 29.687501,82.875 C 30.703171,85.987425 30.441681,89.227198 30.125001,93.03125 C 29.808321,96.835302 29.250051,101.0673 28.812501,105.125 C 28.374951,109.18271 28.087181,113.07303 28.250001,115.9375 C 28.392471,118.4439 29.097761,119.7864 29.156251,120 C 29.380661,119.99088 30.855121,120.23895 33.281251,119.59375 C 36.053961,118.85638 39.686611,117.367 43.406251,115.6875 C 47.125881,114.00799 50.952511,112.1419 54.468751,110.65625 C 57.984991,109.1706 60.976051,107.94167 64.250001,107.9375 C 67.523951,107.93334 70.479991,109.1483 74.000001,110.625 C 77.520011,112.10169 81.369851,113.95497 85.093751,115.625 C 88.817651,117.29504 92.444151,118.80093 95.218751,119.53125 C 97.646511,120.17027 99.151181,119.89786 99.375001,119.90625 C 99.435261,119.69095 100.1139,118.35052 100.25,115.84375 C 100.40553,112.97888 100.10412,109.08783 99.656251,105.03125 C 99.208371,100.97466 98.638841,96.740738 98.312501,92.9375 C 97.986141,89.134266 97.710991,85.927495 98.718751,82.8125 C 99.726491,79.697501 101.82033,77.235155 104.3125,74.34375 C 106.80466,71.452346 109.76095,68.369316 112.5,65.34375 C 115.23905,62.318181 117.76053,59.319356 119.3125,56.90625 C 120.67047,54.794782 120.91992,53.333062 121,53.125 C 120.8122,53.000764 119.71701,51.904061 117.375,51 C 114.6984,49.966787 110.90268,49.046347 106.90625,48.21875 C 102.90981,47.391152 98.717931,46.646129 95.000001,45.78125 C 91.282061,44.91637 88.151121,44.171009 85.500001,42.25 C 82.848871,40.328991 81.167271,37.576182 79.187501,34.3125 C 77.207731,31.048819 75.187321,27.28994 73.156251,23.75 C 71.125171,20.210059 69.065421,16.877947 67.250001,14.65625 C 65.661511,12.712264 64.301791,12.045423 64.125001,11.90625 z" id="path3395" sodipodi:nodetypes="cssssssssssssssscssssssscssssssscsssssssc" style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2845);stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3401)"/>
+ <path d="M 25.190679,119.77989 C 26.414679,122.74238 27.241162,124.11897 31.289475,123.31542 L 30.638356,120.21008 L 29.079766,120.3986 L 28.261711,118.57341 L 25.190679,119.77989 z" id="path3932" sodipodi:nodetypes="cccccc" style="opacity:0.7715356;fill:url(#linearGradient2847);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3928)" transform="matrix(-1,0,0,1,128.10515,0)"/>
+ <path d="M 26.03125,94.25 C 24.983755,105.1142 22.21942,119.85075 26.78125,123.15625 C 33.15283,127.7731 56.287818,110.82251 64.15625,110.8125 C 72.024682,110.80249 95.202691,127.69555 101.5625,123.0625 C 106.10279,119.75495 103.30815,105.10184 102.21875,94.25 L 98.34375,94.25 C 98.677864,97.707156 99.164649,101.42777 99.5625,105.03125 C 100.01038,109.08783 100.31178,112.97888 100.15625,115.84375 C 100.02016,118.35052 99.34151,119.69095 99.28125,119.90625 C 99.057443,119.89786 97.552762,120.17027 95.125,119.53125 C 92.350417,118.80093 88.723899,117.29504 85,115.625 C 81.276103,113.95497 77.426259,112.10169 73.90625,110.625 C 70.386242,109.1483 67.4302,107.93334 64.15625,107.9375 C 60.882303,107.94167 57.891241,109.1706 54.375,110.65625 C 50.858761,112.1419 47.032137,114.00799 43.3125,115.6875 C 39.592862,117.367 35.960216,118.85638 33.1875,119.59375 C 30.761373,120.23895 29.286908,119.99088 29.0625,120 C 29.004012,119.7864 28.29872,118.4439 28.15625,115.9375 C 27.993428,113.07303 28.281199,109.18271 28.71875,105.125 C 29.110886,101.48845 29.580993,97.733027 29.90625,94.25 L 26.03125,94.25 z" id="path3265" sodipodi:nodetypes="csssccsscssssssscsscc" style="opacity:1;fill:url(#radialGradient2849);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3285)"/>
+ <path d="M 3.96875,51.25 C 3.8072482,51.513327 3.653679,51.78066 3.5625,52.0625 C 1.4104994,58.714465 18.791507,72.91663 24.40625,81.25 C 25.066744,82.219734 25.881994,83.560003 26.151902,85.050699 C 27.656842,84.748586 29.576041,82.642021 28.90625,81.25 C 27.768648,78.87567 26.012193,76.796136 23.96875,74.4375 C 21.469238,71.552452 18.527988,68.487339 15.78125,65.46875 C 13.034512,62.450158 10.495601,59.471649 8.9375,57.0625 C 7.574162,54.954496 7.359205,53.457399 7.28125,53.25 C 7.2955626,53.33402 8.1966214,52.223338 10.53125,51.25 L 3.96875,51.25 z M 117.875,51.25 C 119.79897,52.094675 120.73559,53.012104 120.90625,53.125 C 120.82618,53.333062 120.57672,54.794782 119.21875,56.90625 C 117.66679,59.319356 115.1453,62.318181 112.40625,65.34375 C 109.66721,68.369316 106.71091,71.452346 104.21875,74.34375 C 102.16137,76.730721 100.3775,78.839259 99.25,81.25 C 98.389358,83.690958 101.19084,84.749904 102.08854,84.829728 C 102.41731,83.373922 103.07445,82.301223 103.75,81.25 C 109.28341,72.913848 126.77026,58.575986 124.59375,51.90625 C 124.52034,51.681299 124.39982,51.462951 124.28125,51.25 L 117.875,51.25 z" id="rect3289" sodipodi:nodetypes="cscccssssccccssscccscc" style="opacity:0.84158415;fill:url(#radialGradient2851);fill-opacity:1;stroke:none;stroke-width:6.1420002;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter3362)"/>
+ </g>
+ <g id="g3701">
+ <path d="M 64.03125,8.2 C 56.162818,8.2100117 46.828561,34.754451 40.46875,39.3875 C 34.10894,44.020548 5.9844574,44.776082 3.5625,52.2625 C 1.1405426,59.748917 23.465249,76.813524 25.90625,84.29375 C 28.347251,91.77398 20.40967,118.7394 26.78125,123.35625 C 33.15283,127.9731 56.287818,112.02251 64.15625,112.0125 C 72.024682,112.00249 95.202691,127.89555 101.5625,123.2625 C 107.92231,118.62945 99.890544,91.686414 102.3125,84.2 C 104.73446,76.713583 127.03475,59.58648 124.59375,52.10625 C 122.15275,44.626021 94.027829,43.941849 87.65625,39.325 C 81.28467,34.708152 71.899685,8.1899879 64.03125,8.2 z M 64.03125,12.10625 C 64.208046,12.245423 65.56776,12.912264 67.15625,14.85625 C 68.97167,17.077947 71.031426,20.410059 73.0625,23.95 C 75.093573,27.48994 77.113982,31.248819 79.09375,34.5125 C 81.073519,37.776182 82.75512,40.528991 85.40625,42.45 C 88.057376,44.371009 91.18831,45.11637 94.90625,45.98125 C 98.624192,46.846129 102.81606,47.591152 106.8125,48.41875 C 110.80894,49.246347 114.60465,50.166787 117.28125,51.2 C 119.62327,52.104061 120.71845,53.200764 120.90625,53.325 C 120.82618,53.533062 120.57672,54.994782 119.21875,57.10625 C 117.66679,59.519356 115.1453,62.518181 112.40625,65.54375 C 109.66721,68.569316 106.71091,71.652346 104.21875,74.54375 C 101.72659,77.435155 99.632744,79.897501 98.625,83.0125 C 97.617256,86.127495 97.892393,89.334266 98.21875,93.1375 C 98.545107,96.940738 99.114622,101.17466 99.5625,105.23125 C 100.01038,109.28783 100.31178,113.17888 100.15625,116.04375 C 100.02016,118.55052 99.34151,119.89095 99.28125,120.10625 C 99.057443,120.09786 97.552762,120.37027 95.125,119.73125 C 92.350417,119.00093 88.723899,117.49504 85,115.825 C 81.276103,114.15497 77.426259,112.30169 73.90625,110.825 C 70.386242,109.3483 67.4302,108.13334 64.15625,108.1375 C 60.882303,108.14167 57.891241,109.3706 54.375,110.85625 C 50.858761,112.3419 47.032137,114.20799 43.3125,115.8875 C 39.592862,117.567 35.960216,119.05638 33.1875,119.79375 C 30.761373,120.43895 29.286908,120.19088 29.0625,120.2 C 29.004012,119.9864 28.29872,118.6439 28.15625,116.1375 C 27.993428,113.27303 28.281199,109.38271 28.71875,105.325 C 29.156299,101.2673 29.714573,97.035302 30.03125,93.23125 C 30.347928,89.427198 30.609418,86.187425 29.59375,83.075 C 28.578082,79.962573 26.468263,77.522553 23.96875,74.6375 C 21.469238,71.752452 18.527988,68.687339 15.78125,65.66875 C 13.034512,62.650158 10.495601,59.671649 8.9375,57.2625 C 7.5741618,55.154496 7.3592053,53.657399 7.28125,53.45 C 7.2962039,53.537785 8.2681026,52.326785 10.84375,51.325 C 13.517705,50.284977 17.34943,49.350265 21.34375,48.5125 C 25.33807,47.674737 29.534272,46.949339 33.25,46.075 C 36.96573,45.200663 40.103767,44.44025 42.75,42.5125 C 45.396234,40.584748 47.059794,37.812458 49.03125,34.54375 C 51.002705,31.275042 53.009191,27.526347 55.03125,23.98125 C 57.053308,20.436153 59.096493,17.08256 60.90625,14.85625 C 62.489787,12.908229 63.857465,12.244552 64.03125,12.10625 z" id="path3703" style="opacity:1;fill:url(#radialGradient3263);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3259)"/>
+ <path d="M 153.09403,94.713757 C 144.53658,107.09689 92.616372,93.013297 78.414631,98.001518 C 64.21289,102.98974 32.50348,146.4474 18.082028,142.13539 C 3.6605746,137.82337 1.0106378,84.092245 -8.1220219,72.127031 C -17.254681,60.161818 -68.384124,43.433534 -68.739625,28.385431 C -69.095125,13.337327 -18.812666,-5.7867426 -10.255219,-18.169872 C -1.697772,-30.553002 -1.5880954,-84.349316 12.613645,-89.337536 C 26.815387,-94.325757 60.541592,-52.41396 74.963045,-48.101941 C 89.384498,-43.789923 140.58172,-60.30959 149.71438,-48.344376 C 158.84704,-36.379162 129.40853,8.6478227 129.76403,23.695927 C 130.11953,38.74403 161.65148,82.330628 153.09403,94.713757 z" id="path3705" inkscape:flatsided="false" inkscape:randomized="0" inkscape:rounded="0.20136392" sodipodi:arg1="0.60469864" sodipodi:arg2="1.2330172" sodipodi:cx="52.952892" sodipodi:cy="25.510532" sodipodi:r1="121.72647" sodipodi:r2="76.832565" sodipodi:sides="5" sodipodi:type="star" style="opacity:1;fill:url(#radialGradient2906);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" transform="matrix(0.4934214,0.1726044,-0.1726044,0.4934214,42.377875,49.908537)"/>
+ <path d="M 64.03125,8 C 56.162818,8.0100117 46.828561,34.554451 40.46875,39.1875 C 34.10894,43.820548 5.984457,44.576082 3.5625,52.0625 C 1.140543,59.548917 23.465249,76.613524 25.90625,84.09375 C 28.347251,91.57398 20.40967,118.5394 26.78125,123.15625 C 33.15283,127.7731 56.287818,111.82251 64.15625,111.8125 C 72.024682,111.80249 95.202691,127.69555 101.5625,123.0625 C 107.92231,118.42945 99.890544,91.486414 102.3125,84 C 104.73446,76.513583 127.03475,59.38648 124.59375,51.90625 C 122.15275,44.426021 94.027829,43.741849 87.65625,39.125 C 81.28467,34.508152 71.899685,7.9899879 64.03125,8 z M 64.03125,11.90625 C 64.208046,12.045423 65.56776,12.712264 67.15625,14.65625 C 68.97167,16.877947 71.031426,20.210059 73.0625,23.75 C 75.093573,27.28994 77.113982,31.048819 79.09375,34.3125 C 81.073519,37.576182 82.75512,40.328991 85.40625,42.25 C 88.057376,44.171009 91.18831,44.91637 94.90625,45.78125 C 98.624192,46.646129 102.81606,47.391152 106.8125,48.21875 C 110.80894,49.046347 114.60465,49.966787 117.28125,51 C 119.62327,51.904061 120.71845,53.000764 120.90625,53.125 C 120.82618,53.333062 120.57672,54.794782 119.21875,56.90625 C 117.66679,59.319356 115.1453,62.318181 112.40625,65.34375 C 109.66721,68.369316 106.71091,71.452346 104.21875,74.34375 C 101.72659,77.235155 99.632744,79.697501 98.625,82.8125 C 97.617256,85.927495 97.892393,89.134266 98.21875,92.9375 C 98.545107,96.740738 99.114622,100.97466 99.5625,105.03125 C 100.01038,109.08783 100.31178,112.97888 100.15625,115.84375 C 100.02016,118.35052 99.34151,119.69095 99.28125,119.90625 C 99.057443,119.89786 97.552762,120.17027 95.125,119.53125 C 92.350417,118.80093 88.723899,117.29504 85,115.625 C 81.276103,113.95497 77.426259,112.10169 73.90625,110.625 C 70.386242,109.1483 67.4302,107.93334 64.15625,107.9375 C 60.882303,107.94167 57.891241,109.1706 54.375,110.65625 C 50.858761,112.1419 47.032137,114.00799 43.3125,115.6875 C 39.592862,117.367 35.960216,118.85638 33.1875,119.59375 C 30.761373,120.23895 29.286908,119.99088 29.0625,120 C 29.004012,119.7864 28.29872,118.4439 28.15625,115.9375 C 27.993428,113.07303 28.281199,109.18271 28.71875,105.125 C 29.156299,101.0673 29.714573,96.835302 30.03125,93.03125 C 30.347928,89.227198 30.609418,85.987425 29.59375,82.875 C 28.578082,79.762573 26.468263,77.322553 23.96875,74.4375 C 21.469238,71.552452 18.527988,68.487339 15.78125,65.46875 C 13.034512,62.450158 10.495601,59.471649 8.9375,57.0625 C 7.574162,54.954496 7.359205,53.457399 7.28125,53.25 C 7.296204,53.337785 8.268103,52.126785 10.84375,51.125 C 13.517705,50.084977 17.34943,49.150265 21.34375,48.3125 C 25.33807,47.474737 29.534272,46.749339 33.25,45.875 C 36.96573,45.000663 40.103767,44.24025 42.75,42.3125 C 45.396234,40.384748 47.059794,37.612458 49.03125,34.34375 C 51.002705,31.075042 53.009191,27.326347 55.03125,23.78125 C 57.053308,20.236153 59.096493,16.88256 60.90625,14.65625 C 62.489787,12.708229 63.857465,12.044552 64.03125,11.90625 z" id="path3707" style="opacity:1;fill:url(#linearGradient2475);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 60.98796,9.471226 C 62.846491,8.2143022 64.889907,8.0204702 67.219338,9.471226 L 64.037358,15.614216 L 60.98796,9.471226 z" id="path3709" sodipodi:nodetypes="cccc" style="fill:url(#linearGradient3800);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3838)"/>
+ <path d="M 64.039064,11.90625 C 63.865274,12.044552 62.497594,12.708229 60.914064,14.65625 C 59.104304,16.88256 57.061124,20.236153 55.039064,23.78125 C 53.017004,27.326347 51.010514,31.075042 49.039064,34.34375 C 47.067604,37.612458 45.404044,40.384748 42.757814,42.3125 C 40.111574,44.24025 36.973544,45.000663 33.257814,45.875 C 29.542084,46.749339 25.345884,47.474737 21.351564,48.3125 C 17.357244,49.150265 13.525514,50.084977 10.851564,51.125 C 8.2759131,52.126785 7.3040131,53.337785 7.2890631,53.25 C 7.3670131,53.457399 7.5819731,54.954496 8.9453131,57.0625 C 10.503414,59.471649 13.042324,62.450158 15.789064,65.46875 C 18.535804,68.487339 21.477054,71.552452 23.976564,74.4375 C 26.476074,77.322553 28.585894,79.762573 29.601564,82.875 C 29.865144,83.682722 30.019904,84.511238 30.132814,85.34375 C 32.540654,85.431079 34.961934,85.5 37.414064,85.5 C 64.456484,85.5 88.974124,80.107134 106.91406,71.34375 C 108.71383,69.370041 110.60784,67.338911 112.41406,65.34375 C 115.15311,62.318181 117.67459,59.319356 119.22656,56.90625 C 120.58453,54.794782 120.83398,53.333062 120.91406,53.125 C 120.72626,53.000764 119.63107,51.904061 117.28906,51 C 114.61246,49.966787 110.81674,49.046347 106.82031,48.21875 C 102.82387,47.391152 98.631994,46.646129 94.914064,45.78125 C 91.196124,44.91637 88.065184,44.171009 85.414064,42.25 C 82.762934,40.328991 81.081334,37.576182 79.101564,34.3125 C 77.121794,31.048819 75.101384,27.28994 73.070314,23.75 C 71.039234,20.210059 68.979484,16.877947 67.164064,14.65625 C 65.575574,12.712264 64.215854,12.045423 64.039064,11.90625 z" id="path3711" style="opacity:1;fill:url(#radialGradient3372);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 44.96875,33.9375 L 44.1875,35.21875 C 44.177097,35.219516 44.166653,35.219516 44.15625,35.21875 L 42.8125,36.9375 L 41.53125,38.46875 C 41.521947,38.480223 41.511473,38.490697 41.5,38.5 L 40.03125,39.71875 C 40.021947,39.730223 40.011473,39.740697 40,39.75 L 37.125,41.03125 C 37.115697,41.042723 37.105223,41.053197 37.09375,41.0625 L 33.875,41.96875 L 33.375,45.625 L 35.125,45.21875 L 35.125,45.25 L 39.71875,43.75 L 39.75,43.75 L 42.8125,42.0625 L 42.875,42.03125 L 45.78125,39.09375 L 45.8125,39.0625 L 48.96875,34.03125 L 44.96875,33.9375 z" id="path3713" inkscape:original="M 44.84375 33.71875 L 44 35.09375 L 42.65625 36.8125 L 41.375 38.34375 L 39.90625 39.5625 L 37.03125 40.84375 L 33.6875 41.78125 L 33.09375 45.9375 L 35.1875 45.4375 L 39.8125 43.9375 L 42.96875 42.21875 L 45.96875 39.21875 L 49.375 33.8125 L 44.84375 33.71875 z " inkscape:radius="-0.21269609" sodipodi:type="inkscape:offset" style="opacity:0.64356432;fill:url(#radialGradient3669);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3663)" transform="translate(0,0.3)"/>
+ <path d="M 64.039064,11.90625 C 63.865274,12.044552 62.497594,12.708229 60.914064,14.65625 C 59.104304,16.88256 57.061124,20.236153 55.039064,23.78125 C 53.017004,27.326347 51.010514,31.075042 49.039064,34.34375 C 47.067604,37.612458 45.404044,40.384748 42.757814,42.3125 C 40.111574,44.24025 36.973544,45.000663 33.257814,45.875 C 29.542084,46.749339 25.345884,47.474737 21.351564,48.3125 C 17.357244,49.150265 13.525514,50.084977 10.851564,51.125 C 8.2759131,52.126785 7.3040131,53.337785 7.2890631,53.25 C 7.3670131,53.457399 7.5819731,54.954496 8.9453131,57.0625 C 10.503414,59.471649 13.042324,62.450158 15.789064,65.46875 C 18.535804,68.487339 21.477054,71.552452 23.976564,74.4375 C 26.476074,77.322553 28.585894,79.762573 29.601564,82.875 C 29.865144,83.682722 30.019904,84.511238 30.132814,85.34375 C 32.540654,85.431079 34.961934,85.5 37.414064,85.5 C 64.456484,85.5 88.974124,80.107134 106.91406,71.34375 C 108.71383,69.370041 110.60784,67.338911 112.41406,65.34375 C 115.15311,62.318181 117.67459,59.319356 119.22656,56.90625 C 120.58453,54.794782 120.83398,53.333062 120.91406,53.125 C 120.72626,53.000764 119.63107,51.904061 117.28906,51 C 114.61246,49.966787 110.81674,49.046347 106.82031,48.21875 C 102.82387,47.391152 98.631994,46.646129 94.914064,45.78125 C 91.196124,44.91637 88.065184,44.171009 85.414064,42.25 C 82.762934,40.328991 81.081334,37.576182 79.101564,34.3125 C 77.121794,31.048819 75.101384,27.28994 73.070314,23.75 C 71.039234,20.210059 68.979484,16.877947 67.164064,14.65625 C 65.575574,12.712264 64.215854,12.045423 64.039064,11.90625 z" id="path3715" style="opacity:1;fill:url(#linearGradient3315);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 44.96875,33.9375 L 44.1875,35.21875 C 44.177097,35.219516 44.166653,35.219516 44.15625,35.21875 L 42.8125,36.9375 L 41.53125,38.46875 C 41.521947,38.480223 41.511473,38.490697 41.5,38.5 L 40.03125,39.71875 C 40.021947,39.730223 40.011473,39.740697 40,39.75 L 37.125,41.03125 C 37.115697,41.042723 37.105223,41.053197 37.09375,41.0625 L 33.875,41.96875 L 33.375,45.625 L 35.125,45.21875 L 35.125,45.25 L 39.71875,43.75 L 39.75,43.75 L 42.8125,42.0625 L 42.875,42.03125 L 45.78125,39.09375 L 45.8125,39.0625 L 48.96875,34.03125 L 44.96875,33.9375 z" id="path3717" inkscape:original="M 44.84375 33.71875 L 44 35.09375 L 42.65625 36.8125 L 41.375 38.34375 L 39.90625 39.5625 L 37.03125 40.84375 L 33.6875 41.78125 L 33.09375 45.9375 L 35.1875 45.4375 L 39.8125 43.9375 L 42.96875 42.21875 L 45.96875 39.21875 L 49.375 33.8125 L 44.84375 33.71875 z " inkscape:radius="-0.21269609" sodipodi:type="inkscape:offset" style="opacity:0.64356432;fill:url(#radialGradient3673);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3663)" transform="matrix(-1,0,0,1,128.125,0.3)"/>
+ <g id="g3719" transform="translate(-132.29928,0)">
+ <path d="M 196.34375,11.90625 C 196.16996,12.044552 194.80228,12.708229 193.21875,14.65625 C 191.40899,16.88256 189.36581,20.236153 187.34375,23.78125 C 185.32169,27.326347 183.3152,31.075042 181.34375,34.34375 C 179.37229,37.612458 177.70873,40.384748 175.0625,42.3125 C 172.41626,44.24025 169.27823,45.000663 165.5625,45.875 C 161.84677,46.749339 157.65057,47.474737 153.65625,48.3125 C 149.66193,49.150265 145.8302,50.084977 143.15625,51.125 C 140.5806,52.126785 139.6087,53.337785 139.59375,53.25 C 139.62377,53.329884 139.71528,53.638731 139.84375,54.0625 C 140.2595,53.69998 141.25985,52.862595 143.15625,52.125 C 145.8302,51.084977 149.66193,50.150265 153.65625,49.3125 C 157.65057,48.474737 161.84677,47.749339 165.5625,46.875 C 169.27823,46.000663 172.41626,45.24025 175.0625,43.3125 C 177.70873,41.384748 179.37229,38.612458 181.34375,35.34375 C 183.3152,32.075042 185.32169,28.326347 187.34375,24.78125 C 189.36581,21.236153 191.40899,17.88256 193.21875,15.65625 C 194.80228,13.708229 196.16996,13.044552 196.34375,12.90625 C 196.52054,13.045423 197.88026,13.712264 199.46875,15.65625 C 201.28417,17.877947 203.34392,21.210059 205.375,24.75 C 207.40607,28.28994 209.42648,32.048819 211.40625,35.3125 C 213.38602,38.576182 215.06762,41.328991 217.71875,43.25 C 220.36987,45.171009 223.50081,45.91637 227.21875,46.78125 C 230.93668,47.646129 235.12856,48.391152 239.125,49.21875 C 243.12143,50.046347 246.91715,50.966787 249.59375,52 C 251.51448,52.74144 252.56925,53.579608 253,53.9375 C 253.13371,53.522484 253.18802,53.204851 253.21875,53.125 C 253.03095,53.000764 251.93576,51.904061 249.59375,51 C 246.91715,49.966787 243.12143,49.046347 239.125,48.21875 C 235.12856,47.391152 230.93668,46.646129 227.21875,45.78125 C 223.50081,44.91637 220.36987,44.171009 217.71875,42.25 C 215.06762,40.328991 213.38602,37.576182 211.40625,34.3125 C 209.42648,31.048819 207.40607,27.28994 205.375,23.75 C 203.34392,20.210059 201.28417,16.877947 199.46875,14.65625 C 197.88026,12.712264 196.52054,12.045423 196.34375,11.90625 z" id="path3721" style="opacity:1;fill:url(#linearGradient3369);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 246.78125,49.937636 C 247.42469,50.142466 248.03845,50.379526 248.59375,50.593886 C 250.93576,51.497946 252.03095,52.594656 252.21875,52.718886 C 252.13867,52.926956 251.88922,54.388676 250.53125,56.500136 C 248.97928,58.913246 246.4578,61.912066 243.71875,64.937636 C 241.91253,66.932796 240.01852,68.963926 238.21875,70.937636 C 220.27881,79.701026 195.76117,85.093886 168.71875,85.093886 C 166.59433,85.093886 164.49568,85.039506 162.40625,84.968886 C 162.4184,85.051736 162.42625,85.135936 162.4375,85.218886 C 164.84534,85.306216 167.26662,85.375136 169.71875,85.375136 C 196.76117,85.375136 221.27881,79.982276 239.21875,71.218886 C 241.01852,69.245176 242.91253,67.214046 244.71875,65.218886 C 247.4578,62.193316 249.97928,59.194496 251.53125,56.781386 C 252.88922,54.669926 253.13867,53.208206 253.21875,53.000136 C 253.03095,52.875906 251.93576,51.779196 249.59375,50.875136 C 248.75868,50.552786 247.80629,50.238636 246.78125,49.937636 z" id="path3723" sodipodi:nodetypes="cscsscsccscsscsc" style="opacity:1;fill:url(#linearGradient3345);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ </g>
+ <path d="M 25.190679,119.77989 C 26.414679,122.74238 27.241162,124.11897 31.289475,123.31542 L 30.638356,120.21008 L 29.079766,120.3986 L 28.261711,118.57341 L 25.190679,119.77989 z" id="path3725" sodipodi:nodetypes="cccccc" style="opacity:0.7715356;fill:url(#linearGradient2853);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3928)"/>
+ <path d="M 26.03125,94.25 C 24.983755,105.1142 22.21942,119.85075 26.78125,123.15625 C 33.15283,127.7731 56.287818,111.82251 64.15625,111.8125 C 72.024682,111.80249 95.202691,127.69555 101.5625,123.0625 C 106.10279,119.75495 103.30815,105.10184 102.21875,94.25 L 98.34375,94.25 C 98.677864,97.707156 99.164649,101.42777 99.5625,105.03125 C 100.01038,109.08783 100.31178,112.97888 100.15625,115.84375 C 100.02016,118.35052 99.34151,119.69095 99.28125,119.90625 C 99.057443,119.89786 97.552762,120.17027 95.125,119.53125 C 92.350417,118.80093 88.723899,117.29504 85,115.625 C 81.276103,113.95497 77.426259,112.10169 73.90625,110.625 C 70.386242,109.1483 67.4302,107.93334 64.15625,107.9375 C 60.882303,107.94167 57.891241,109.1706 54.375,110.65625 C 50.858761,112.1419 47.032137,114.00799 43.3125,115.6875 C 39.592862,117.367 35.960216,118.85638 33.1875,119.59375 C 30.761373,120.23895 29.286908,119.99088 29.0625,120 C 29.004012,119.7864 28.29872,118.4439 28.15625,115.9375 C 27.993428,113.07303 28.281199,109.18271 28.71875,105.125 C 29.110886,101.48845 29.580993,97.733027 29.90625,94.25 L 26.03125,94.25 z" id="path3727" style="opacity:1;fill:url(#radialGradient3956);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"/>
+ <path d="M 64.125001,11.90625 C 63.951211,12.044552 62.583531,12.708229 61.000001,14.65625 C 59.190241,16.88256 57.147061,20.236153 55.125001,23.78125 C 53.102941,27.326347 51.096451,31.075042 49.125001,34.34375 C 47.153541,37.612458 45.489981,40.384748 42.843751,42.3125 C 40.197511,44.24025 37.059481,45.000663 33.343751,45.875 C 29.628021,46.749339 25.431821,47.474737 21.437501,48.3125 C 17.443181,49.150265 13.611451,50.084977 10.937501,51.125 C 8.3618506,52.126785 7.3899506,53.337785 7.3750006,53.25 C 7.4529506,53.457399 7.6679106,54.954496 9.0312506,57.0625 C 10.589351,59.471649 13.128261,62.450158 15.875001,65.46875 C 18.621741,68.487339 21.562991,71.552452 24.062501,74.4375 C 26.562011,77.322553 28.671831,79.762573 29.687501,82.875 C 30.703171,85.987425 30.441681,89.227198 30.125001,93.03125 C 29.808321,96.835302 29.250051,101.0673 28.812501,105.125 C 28.374951,109.18271 28.087181,113.07303 28.250001,115.9375 C 28.392471,118.4439 29.097761,119.7864 29.156251,120 C 29.380661,119.99088 30.855121,120.23895 33.281251,119.59375 C 36.053961,118.85638 39.686611,117.367 43.406251,115.6875 C 47.125881,114.00799 50.952511,112.1419 54.468751,110.65625 C 57.984991,109.1706 60.976051,107.94167 64.250001,107.9375 C 67.523951,107.93334 70.479991,109.1483 74.000001,110.625 C 77.520011,112.10169 81.369851,113.95497 85.093751,115.625 C 88.817651,117.29504 92.444151,118.80093 95.218751,119.53125 C 97.646511,120.17027 99.151181,119.89786 99.375001,119.90625 C 99.435261,119.69095 100.1139,118.35052 100.25,115.84375 C 100.40553,112.97888 100.10412,109.08783 99.656251,105.03125 C 99.208371,100.97466 98.638841,96.740738 98.312501,92.9375 C 97.986141,89.134266 97.710991,85.927495 98.718751,82.8125 C 99.726491,79.697501 101.82033,77.235155 104.3125,74.34375 C 106.80466,71.452346 109.76095,68.369316 112.5,65.34375 C 115.23905,62.318181 117.76053,59.319356 119.3125,56.90625 C 120.67047,54.794782 120.91992,53.333062 121,53.125 C 120.8122,53.000764 119.71701,51.904061 117.375,51 C 114.6984,49.966787 110.90268,49.046347 106.90625,48.21875 C 102.90981,47.391152 98.717931,46.646129 95.000001,45.78125 C 91.282061,44.91637 88.151121,44.171009 85.500001,42.25 C 82.848871,40.328991 81.167271,37.576182 79.187501,34.3125 C 77.207731,31.048819 75.187321,27.28994 73.156251,23.75 C 71.125171,20.210059 69.065421,16.877947 67.250001,14.65625 C 65.661511,12.712264 64.301791,12.045423 64.125001,11.90625 z" id="path3729" sodipodi:nodetypes="cssssssssssssssscssssssscssssssscsssssssc" style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2855);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3391)"/>
+ <g id="g3731" transform="matrix(-1,0,0,1,128.17175,0)">
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3733" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient3618);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)"/>
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3735" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient3620);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3604)"/>
+ </g>
+ <g id="g3737">
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3739" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient3382);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)"/>
+ <path d="M 122.34464,49.937924 L 118.89749,52.943128 L 119.6046,54.97606 L 123.93563,55.771555 C 124.26838,53.827011 124.96313,51.882468 122.34464,49.937924 z" id="path3741" sodipodi:nodetypes="ccccc" style="opacity:0.60891089;fill:url(#radialGradient3570);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3604)"/>
+ </g>
+ <path d="M 64.125001,11.90625 C 63.951211,12.044552 62.583531,12.708229 61.000001,14.65625 C 59.190241,16.88256 57.147061,20.236153 55.125001,23.78125 C 53.102941,27.326347 51.096451,31.075042 49.125001,34.34375 C 47.153541,37.612458 45.489981,40.384748 42.843751,42.3125 C 40.197511,44.24025 37.059481,45.000663 33.343751,45.875 C 29.628021,46.749339 25.431821,47.474737 21.437501,48.3125 C 17.443181,49.150265 13.611451,50.084977 10.937501,51.125 C 8.3618506,52.126785 7.3899506,53.337785 7.3750006,53.25 C 7.4529506,53.457399 7.6679106,54.954496 9.0312506,57.0625 C 10.589351,59.471649 13.128261,62.450158 15.875001,65.46875 C 18.621741,68.487339 21.562991,71.552452 24.062501,74.4375 C 26.562011,77.322553 28.671831,79.762573 29.687501,82.875 C 30.703171,85.987425 30.441681,89.227198 30.125001,93.03125 C 29.808321,96.835302 29.250051,101.0673 28.812501,105.125 C 28.374951,109.18271 28.087181,113.07303 28.250001,115.9375 C 28.392471,118.4439 29.097761,119.7864 29.156251,120 C 29.380661,119.99088 30.855121,120.23895 33.281251,119.59375 C 36.053961,118.85638 39.686611,117.367 43.406251,115.6875 C 47.125881,114.00799 50.952511,112.1419 54.468751,110.65625 C 57.984991,109.1706 60.976051,107.94167 64.250001,107.9375 C 67.523951,107.93334 70.479991,109.1483 74.000001,110.625 C 77.520011,112.10169 81.369851,113.95497 85.093751,115.625 C 88.817651,117.29504 92.444151,118.80093 95.218751,119.53125 C 97.646511,120.17027 99.151181,119.89786 99.375001,119.90625 C 99.435261,119.69095 100.1139,118.35052 100.25,115.84375 C 100.40553,112.97888 100.10412,109.08783 99.656251,105.03125 C 99.208371,100.97466 98.638841,96.740738 98.312501,92.9375 C 97.986141,89.134266 97.710991,85.927495 98.718751,82.8125 C 99.726491,79.697501 101.82033,77.235155 104.3125,74.34375 C 106.80466,71.452346 109.76095,68.369316 112.5,65.34375 C 115.23905,62.318181 117.76053,59.319356 119.3125,56.90625 C 120.67047,54.794782 120.91992,53.333062 121,53.125 C 120.8122,53.000764 119.71701,51.904061 117.375,51 C 114.6984,49.966787 110.90268,49.046347 106.90625,48.21875 C 102.90981,47.391152 98.717931,46.646129 95.000001,45.78125 C 91.282061,44.91637 88.151121,44.171009 85.500001,42.25 C 82.848871,40.328991 81.167271,37.576182 79.187501,34.3125 C 77.207731,31.048819 75.187321,27.28994 73.156251,23.75 C 71.125171,20.210059 69.065421,16.877947 67.250001,14.65625 C 65.661511,12.712264 64.301791,12.045423 64.125001,11.90625 z" id="path3743" sodipodi:nodetypes="cssssssssssssssscssssssscssssssscsssssssc" style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3385);stroke-width:0.60000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3401)"/>
+ <path d="M 25.190679,119.77989 C 26.414679,122.74238 27.241162,124.11897 31.289475,123.31542 L 30.638356,120.21008 L 29.079766,120.3986 L 28.261711,118.57341 L 25.190679,119.77989 z" id="path3745" sodipodi:nodetypes="cccccc" style="opacity:0.7715356;fill:url(#linearGradient3934);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3928)" transform="matrix(-1,0,0,1,128.10515,0)"/>
+ <path d="M 26.03125,94.25 C 24.983755,105.1142 22.21942,119.85075 26.78125,123.15625 C 33.15283,127.7731 56.287818,110.82251 64.15625,110.8125 C 72.024682,110.80249 95.202691,127.69555 101.5625,123.0625 C 106.10279,119.75495 103.30815,105.10184 102.21875,94.25 L 98.34375,94.25 C 98.677864,97.707156 99.164649,101.42777 99.5625,105.03125 C 100.01038,109.08783 100.31178,112.97888 100.15625,115.84375 C 100.02016,118.35052 99.34151,119.69095 99.28125,119.90625 C 99.057443,119.89786 97.552762,120.17027 95.125,119.53125 C 92.350417,118.80093 88.723899,117.29504 85,115.625 C 81.276103,113.95497 77.426259,112.10169 73.90625,110.625 C 70.386242,109.1483 67.4302,107.93334 64.15625,107.9375 C 60.882303,107.94167 57.891241,109.1706 54.375,110.65625 C 50.858761,112.1419 47.032137,114.00799 43.3125,115.6875 C 39.592862,117.367 35.960216,118.85638 33.1875,119.59375 C 30.761373,120.23895 29.286908,119.99088 29.0625,120 C 29.004012,119.7864 28.29872,118.4439 28.15625,115.9375 C 27.993428,113.07303 28.281199,109.18271 28.71875,105.125 C 29.110886,101.48845 29.580993,97.733027 29.90625,94.25 L 26.03125,94.25 z" id="path3747" sodipodi:nodetypes="csssccsscssssssscsscc" style="opacity:1;fill:url(#radialGradient3267);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3285)"/>
+ <path d="M 3.96875,51.25 C 3.8072482,51.513327 3.653679,51.78066 3.5625,52.0625 C 1.4104994,58.714465 18.791507,72.91663 24.40625,81.25 C 25.066744,82.219734 25.881994,83.560003 26.151902,85.050699 C 27.656842,84.748586 29.576041,82.642021 28.90625,81.25 C 27.768648,78.87567 26.012193,76.796136 23.96875,74.4375 C 21.469238,71.552452 18.527988,68.487339 15.78125,65.46875 C 13.034512,62.450158 10.495601,59.471649 8.9375,57.0625 C 7.574162,54.954496 7.359205,53.457399 7.28125,53.25 C 7.2955626,53.33402 8.1966214,52.223338 10.53125,51.25 L 3.96875,51.25 z M 117.875,51.25 C 119.79897,52.094675 120.73559,53.012104 120.90625,53.125 C 120.82618,53.333062 120.57672,54.794782 119.21875,56.90625 C 117.66679,59.319356 115.1453,62.318181 112.40625,65.34375 C 109.66721,68.369316 106.71091,71.452346 104.21875,74.34375 C 102.16137,76.730721 100.3775,78.839259 99.25,81.25 C 98.389358,83.690958 101.19084,84.749904 102.08854,84.829728 C 102.41731,83.373922 103.07445,82.301223 103.75,81.25 C 109.28341,72.913848 126.77026,58.575986 124.59375,51.90625 C 124.52034,51.681299 124.39982,51.462951 124.28125,51.25 L 117.875,51.25 z" id="path3749" sodipodi:nodetypes="cscccssssccccssscccscc" style="opacity:0.84158415;fill:url(#radialGradient3300);fill-opacity:1;stroke:none;stroke-width:6.1420002;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter3362)"/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/checkupdate.svg b/resources/multimc/scalable/checkupdate.svg
new file mode 100644
index 00000000..fc09cb4c
--- /dev/null
+++ b/resources/multimc/scalable/checkupdate.svg
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) --><svg height="48px" id="svg7270" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.46" sodipodi:docname="system-software-update.svg" sodipodi:version="0.32" width="48px" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <metadata>
+ <rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <cc:Work rdf:about="">
+ <dc:title></dc:title>
+ <dc:description></dc:description>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>unsorted</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library, Source: GNOME-Colors, Source: GNOME-Colors, Source: GNOME-Colors, Source: GNOME-Colors, Source: GNOME-Colors</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title></dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title></dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date></dc:date>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <cc:license rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"/>
+ <dc:language>en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs7272">
+ <linearGradient id="linearGradient4873" inkscape:collect="always">
+ <stop id="stop4875" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop4877" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.5436745,0,0,1.5436393,-63.017183,37.419463)" gradientUnits="userSpaceOnUse" id="linearGradient2704" inkscape:collect="always" x1="62.771355" x2="62.579906" xlink:href="#linearGradient4873" y1="-13.185048" y2="7.4599638"/>
+ <linearGradient id="linearGradient3811">
+ <stop id="stop3813" offset="0" style="stop-color:#42770c;stop-opacity:1"/>
+ <stop id="stop3815" offset="1" style="stop-color:#789e2d;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0.7287372,0,0,0.7287206,15.796514,14.228326)" gradientUnits="userSpaceOnUse" id="linearGradient3927" inkscape:collect="always" x1="15.046636" x2="15.046636" xlink:href="#linearGradient3811" y1="44.787998" y2="3.8851264"/>
+ <linearGradient id="linearGradient5106">
+ <stop id="stop5108" offset="0" style="stop-color:#a7cc5c;stop-opacity:1"/>
+ <stop id="stop5110" offset="1" style="stop-color:#789e2d;stop-opacity:1"/>
+ </linearGradient>
+ <radialGradient cx="62.200348" cy="-8.5060539" fx="62.200348" fy="-8.5060539" gradientTransform="matrix(-5.9025587,-9.7773057e-8,3.8757273e-8,-2.3442794,400.1412,4.1903282)" gradientUnits="userSpaceOnUse" id="radialGradient2707" inkscape:collect="always" r="9.7552834" xlink:href="#linearGradient5106"/>
+ <linearGradient id="linearGradient10691" inkscape:collect="always">
+ <stop id="stop10693" offset="0" style="stop-color:#000000;stop-opacity:1;"/>
+ <stop id="stop10695" offset="1" style="stop-color:#000000;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="6.702713" cy="73.615715" fx="6.702713" fy="73.615715" gradientTransform="scale(1.902215,0.525703)" gradientUnits="userSpaceOnUse" id="radialGradient3042" inkscape:collect="always" r="7.228416" xlink:href="#linearGradient10691"/>
+ <linearGradient id="linearGradient3540" inkscape:collect="always">
+ <stop id="stop3542" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3544" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient7581" inkscape:collect="always" x1="322.26392" x2="329.12961" xlink:href="#linearGradient3540" y1="55.34375" y2="64.329506"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient7579" inkscape:collect="always" x1="325.65164" x2="328.24478" xlink:href="#linearGradient3497" y1="62.475075" y2="61.084908"/>
+ <linearGradient id="linearGradient7088">
+ <stop id="stop7090" offset="0" style="stop-color:#c17d11;stop-opacity:1;"/>
+ <stop id="stop7092" offset="1" style="stop-color:#e9b96e;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0.1516675,0,0,0.1638445,300.96358,54.165417)" gradientUnits="userSpaceOnUse" id="linearGradient7577" inkscape:collect="always" x1="251.94502" x2="161.94502" xlink:href="#linearGradient7088" y1="18.836056" y2="46.195984"/>
+ <linearGradient gradientTransform="matrix(0.8365572,0,0,1,52.219983,0)" gradientUnits="userSpaceOnUse" id="linearGradient7575" inkscape:collect="always" x1="334.77948" x2="327.34198" xlink:href="#linearGradient3582" y1="69.490768" y2="69.490768"/>
+ <linearGradient id="linearGradient3532" inkscape:collect="always">
+ <stop id="stop3534" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3536" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient7573" inkscape:collect="always" x1="316.42142" x2="304.1875" xlink:href="#linearGradient3532" y1="58.144356" y2="67.203125"/>
+ <linearGradient id="linearGradient3516" inkscape:collect="always">
+ <stop id="stop3518" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3520" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="318.26694" cy="81.264877" fx="318.26694" fy="81.264877" gradientTransform="matrix(11.694835,-3.1336215,0.2338827,0.8728621,-3435.8358,1012.645)" gradientUnits="userSpaceOnUse" id="radialGradient7571" inkscape:collect="always" r="15.375" xlink:href="#linearGradient3516"/>
+ <linearGradient id="linearGradient3497" inkscape:collect="always">
+ <stop id="stop3499" offset="0" style="stop-color:#c17d11;stop-opacity:1;"/>
+ <stop id="stop3501" offset="1" style="stop-color:#c17d11;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient7569" inkscape:collect="always" x1="317" x2="313.625" xlink:href="#linearGradient3497" y1="65.25" y2="62.475075"/>
+ <linearGradient id="linearGradient7000" inkscape:collect="always">
+ <stop id="stop7002" offset="0" style="stop-color:#eeeeec;stop-opacity:1"/>
+ <stop id="stop7005" offset="1" style="stop-color:#e9b96e;stop-opacity:1"/>
+ </linearGradient>
+ <radialGradient cx="93.23716" cy="132.00948" fx="93.23716" fy="132.00948" gradientTransform="matrix(0.194642,-0.362756,0.2778653,0.17262,263.08914,83.463419)" gradientUnits="userSpaceOnUse" id="radialGradient7567" inkscape:collect="always" r="48.719242" xlink:href="#linearGradient7000"/>
+ <linearGradient id="linearGradient3582" inkscape:collect="always">
+ <stop id="stop3584" offset="0" style="stop-color:#000000;stop-opacity:1;"/>
+ <stop id="stop3586" offset="1" style="stop-color:#000000;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0.914635,0,0,1,27.274119,0)" gradientUnits="userSpaceOnUse" id="linearGradient7565" inkscape:collect="always" x1="314.81003" x2="321.78522" xlink:href="#linearGradient3582" y1="69.553268" y2="69.490768"/>
+ <linearGradient id="linearGradient3757" inkscape:collect="always">
+ <stop id="stop3759" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3761" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="321.375" cy="70.5" fx="321.375" fy="70.5" gradientTransform="matrix(1,0,0,0.4274194,0,40.366935)" gradientUnits="userSpaceOnUse" id="radialGradient7563" inkscape:collect="always" r="15.5" xlink:href="#linearGradient3757"/>
+ <linearGradient id="linearGradient3737">
+ <stop id="stop3739" offset="0" style="stop-color:#faefde;stop-opacity:1"/>
+ <stop id="stop3741" offset="0.68321055" style="stop-color:#e9b96e;stop-opacity:1"/>
+ <stop id="stop3743" offset="1" style="stop-color:#e9b96e;stop-opacity:0"/>
+ </linearGradient>
+ <radialGradient cx="320.7493" cy="70.499977" fx="320.7493" fy="70.499977" gradientTransform="matrix(1.1851374,0,0,0.1612901,-56.76846,59.129051)" gradientUnits="userSpaceOnUse" id="radialGradient7561" inkscape:collect="always" r="15.500023" xlink:href="#linearGradient3737"/>
+ <linearGradient id="linearGradient3712" inkscape:collect="always">
+ <stop id="stop3714" offset="0" style="stop-color:#e9b96e;stop-opacity:1;"/>
+ <stop id="stop3716" offset="1" style="stop-color:#e9b96e;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="321.7785" cy="96.847473" fx="321.7785" fy="96.847473" gradientTransform="matrix(4.6140944,0,0,0.6613562,-1154.7032,27.949324)" gradientUnits="userSpaceOnUse" id="radialGradient7559" inkscape:collect="always" r="15.5" xlink:href="#linearGradient3712"/>
+ <linearGradient id="linearGradient5967">
+ <stop id="stop5969" offset="0" style="stop-color:#c17d11;stop-opacity:1;"/>
+ <stop id="stop5971" offset="1" style="stop-color:#e9b96e;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0.1549758,0,0,0.1517241,299.35074,56.193103)" gradientUnits="userSpaceOnUse" id="linearGradient7557" inkscape:collect="always" x1="174.01562" x2="30" xlink:href="#linearGradient5967" y1="236" y2="64"/>
+ <linearGradient id="linearGradient6977">
+ <stop id="stop7039" offset="0" style="stop-color:#ebd3ad;stop-opacity:1;"/>
+ <stop id="stop6981" offset="1" style="stop-color:#e9b96e;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0.159975,0,0,0.1765787,298.70076,53.371862)" gradientUnits="userSpaceOnUse" id="linearGradient7555" inkscape:collect="always" x1="52" x2="52" xlink:href="#linearGradient6977" y1="40.482288" y2="76.67421"/>
+ <linearGradient id="linearGradient3703">
+ <stop id="stop3705" offset="0" style="stop-color:black;stop-opacity:0;"/>
+ <stop id="stop3711" offset="0.5" style="stop-color:black;stop-opacity:1;"/>
+ <stop id="stop3707" offset="1" style="stop-color:black;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.179548,0,0,1,-4.219389,0)" gradientUnits="userSpaceOnUse" id="linearGradient7462" inkscape:collect="always" x1="17.554192" x2="17.554192" xlink:href="#linearGradient3703" y1="46.000275" y2="34.999718"/>
+ <radialGradient cx="5" cy="41.5" fx="5" fy="41.5" gradientTransform="matrix(0.99001,0,0,1.1,-14.88523,-86.15)" gradientUnits="userSpaceOnUse" id="radialGradient7460" inkscape:collect="always" r="5" xlink:href="#linearGradient3681"/>
+ <linearGradient id="linearGradient3681" inkscape:collect="always">
+ <stop id="stop3683" offset="0" style="stop-color:black;stop-opacity:1;"/>
+ <stop id="stop3685" offset="1" style="stop-color:black;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="5" cy="41.5" fx="5" fy="41.5" gradientTransform="matrix(0.990017,0,0,1.1,32.1147,-5.15)" gradientUnits="userSpaceOnUse" id="radialGradient7458" inkscape:collect="always" r="5" xlink:href="#linearGradient3681"/>
+ </defs>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="24" inkscape:cy="24" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="690" inkscape:window-width="641" inkscape:window-x="0" inkscape:window-y="331" inkscape:zoom="7" pagecolor="#ffffff" showgrid="true"/>
+ <g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1">
+ <g id="g3713" style="opacity:0.4" transform="matrix(1.000001,0,0,1.2727273,-5e-6,-12.545454)">
+ <rect height="11" id="rect1907" style="opacity:1;fill:url(#radialGradient7458);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" width="4.9352183" x="37.064781" y="35"/>
+ <rect height="11" id="rect3689" style="opacity:1;fill:url(#radialGradient7460);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" transform="scale(-1,-1)" width="4.9351835" x="-9.9351835" y="-46"/>
+ <rect height="11" id="rect3693" style="opacity:1;fill:url(#linearGradient7462);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.20000057;stroke-opacity:1" width="27.129599" x="9.9351835" y="35"/>
+ </g>
+ <g id="g4045" style="display:inline;enable-background:new" transform="translate(-296.00045,-50)">
+ <path d="M 322.875,54.40625 C 322.64926,54.465917 322.49407,54.672835 322.5,54.90625 L 322.59375,61.5 L 318.40625,61.5 L 318.4375,58.9375 C 318.44168,58.796741 318.39241,58.648956 318.28125,58.5625 C 318.17008,58.476044 318.0104,58.461295 317.875,58.5 L 306.5,61.5 C 306.35947,61.54996 306.25368,61.667506 306.21875,61.8125 L 306.15625,62.03125 C 306.05939,62.200213 305.96316,62.375835 305.90625,62.5625 L 303.5625,70.09375 C 303.51406,70.207689 303.5,70.305686 303.5,70.4375 C 303.4981,70.458668 303.5019,70.478889 303.5,70.5 L 303.5,90.84375 C 303.5,91.371002 303.93699,92.499999 304.46875,92.5 L 334.53125,92.5 C 335.06301,92.5 335.5,91.371004 335.5,90.84375 L 335.5,70.5 L 335.4375,70.09375 L 335.40625,70 C 335.40291,69.966972 335.41634,69.938782 335.40625,69.90625 L 332.9375,61.78125 C 332.9115,61.684204 332.85709,61.597145 332.78125,61.53125 L 323.3125,54.5 C 323.18929,54.402174 323.02747,54.367498 322.875,54.40625 z" id="path3678" sodipodi:nodetypes="cccccsccccccsccccccccccccc" style="fill:url(#linearGradient7555);fill-opacity:1;fill-rule:evenodd;stroke:#8f5902;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <rect height="1" id="rect3606" style="opacity:0.18918918;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.50000001;stroke-opacity:1;display:inline;enable-background:new" width="23.340202" x="309" y="66"/>
+ <path d="M 304.92985,70 C 304.41471,70 304,70.406014 304,70.910345 L 304,90.482758 L 304,91.089654 C 304,91.593985 304.41471,91.999999 304.92985,92 L 334.07015,92 C 334.5853,92 335,91.593984 335,91.089654 L 335,70.910345 C 335,70.406014 334.5853,70 334.07015,70 L 304.92985,70 z" id="path3628" sodipodi:nodetypes="cccccccccc" style="fill:url(#linearGradient7557);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <path d="M 304.92985,70 C 304.41471,70 304,70.406014 304,70.910345 L 304,90.482758 L 304,91.089654 C 304,91.593985 304.41471,91.999999 304.92985,92 L 334.07015,92 C 334.5853,92 335,91.593984 335,91.089654 L 335,70.910345 C 335,70.406014 334.5853,70 334.07015,70 L 304.92985,70 z" id="path3702" sodipodi:nodetypes="cccccccccc" style="opacity:0.81531528;fill:url(#radialGradient7559);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <path d="M 304.71875,68 L 304.03125,70.25 C 304.03158,70.260414 304.03158,70.270836 304.03125,70.28125 C 304.00957,70.33225 304,70.341241 304,70.4375 C 304.00033,70.447914 304.00033,70.458336 304,70.46875 C 304.00297,70.435653 303.99952,70.489668 304,70.5 C 304.00048,70.510332 304.0049,70.47686 304,70.53125 L 304,73 L 335,73 L 335,70.5625 L 335,70.5 L 334.96875,70.25 L 334.9375,70.15625 L 334.28125,68 L 304.71875,68 z" id="path3723" style="opacity:0.5;fill:url(#radialGradient7561);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <rect height="1" id="rect3746" style="opacity:0.62330568;fill:url(#radialGradient7563);fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.50000001;stroke-opacity:1;display:inline;enable-background:new" width="31" x="304" y="70"/>
+ <path d="M 307.9375,61.90625 C 307.82039,61.90625 307.7549,61.954412 307.65625,62.09375 C 307.55761,62.233088 307.47013,62.458403 307.40625,62.6875 L 305.28125,70.09375 L 323.5,70.09375 L 323.5,61.90625 L 307.9375,61.90625 z" id="path3558" style="opacity:0.36936939;fill:url(#linearGradient7565);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <rect height="1" id="rect3602" style="opacity:0.39639641;fill:#c17d11;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.50000001;stroke-opacity:1" width="23.340202" x="309" y="65"/>
+ <path d="M 317.875,59.09375 L 306.71875,62.03125 L 304.25,69.8125 L 317.75,66.875 L 317.875,59.09375 z" id="path3466" inkscape:original="M 318 58.9375 L 306.625 61.9375 L 304.0625 70 L 317.875 66.96875 L 318 58.9375 z " inkscape:radius="-0.11941363" sodipodi:type="inkscape:offset" style="fill:url(#radialGradient7567);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ <path d="M 317.875,58.5 L 306.5,61.5 C 306.35947,61.54996 306.25368,61.667506 306.21875,61.8125 L 303.65625,69.875 C 303.61173,70.021945 303.64677,70.181469 303.74877,70.296228 C 303.85078,70.410987 304.0051,70.464488 304.15625,70.4375 L 317.96875,67.40625 C 318.1724,67.360088 318.31583,67.177536 318.3125,66.96875 L 318.4375,58.9375 C 318.44168,58.796741 318.37845,58.662433 318.2673,58.575977 C 318.15614,58.489521 318.0104,58.461295 317.875,58.5 L 317.875,58.5 z" id="path3489" inkscape:original="M 318 58.9375 L 306.625 61.9375 L 304.0625 70 L 317.875 66.96875 L 318 58.9375 z " inkscape:radius="0.44133759" sodipodi:type="inkscape:offset" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient7569);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ <path d="M 323.65625,56.125 L 323.71875,61.46875 C 323.73018,61.77333 323.61637,62.069302 323.40382,62.287756 C 323.19127,62.506209 322.89853,62.628083 322.59375,62.625 L 318.40625,62.625 C 318.10702,62.627829 317.81923,62.510212 317.60763,62.298617 C 317.39604,62.087022 317.27842,61.799227 317.28125,61.5 L 317.3125,59.78125 L 307.1875,62.4375 C 307.18765,62.447916 307.18765,62.458334 307.1875,62.46875 C 307.1693,62.511683 307.14843,62.553432 307.125,62.59375 C 307.03546,62.749944 306.98033,62.837024 306.96875,62.875 C 306.9689,62.885416 306.9689,62.895834 306.96875,62.90625 L 304.625,70.4375 C 304.625,70.405959 304.63178,70.465233 304.625,70.5 L 304.625,70.53125 C 304.64058,70.357706 304.64449,70.377244 304.625,70.59375 L 304.625,90.84375 C 304.625,90.799502 304.68196,91.178267 304.78125,91.375 L 334.21875,91.375 C 334.31804,91.178267 334.375,90.799504 334.375,90.84375 L 334.375,70.5 L 334.375,70.4375 L 334.34375,70.34375 C 334.32611,70.272049 334.31564,70.198772 334.3125,70.125 L 331.9375,62.3125 L 323.65625,56.125 z" id="path3512" inkscape:original="M 322.875 54.40625 C 322.64926 54.465917 322.49407 54.672835 322.5 54.90625 L 322.59375 61.5 L 318.40625 61.5 L 318.4375 58.9375 C 318.44168 58.796741 318.39241 58.648956 318.28125 58.5625 C 318.17008 58.476044 318.0104 58.461295 317.875 58.5 L 306.5 61.5 C 306.35947 61.54996 306.25368 61.667506 306.21875 61.8125 L 306.15625 62.03125 C 306.05939 62.200213 305.96316 62.375835 305.90625 62.5625 L 303.5625 70.09375 C 303.51406 70.207689 303.5 70.305686 303.5 70.4375 C 303.4981 70.458668 303.5019 70.478889 303.5 70.5 L 303.5 90.84375 C 303.5 91.371002 303.93699 92.499999 304.46875 92.5 L 334.53125 92.5 C 335.06301 92.5 335.5 91.371004 335.5 90.84375 L 335.5 70.5 L 335.4375 70.09375 L 335.40625 70 C 335.40291 69.966972 335.41634 69.938782 335.40625 69.90625 L 332.9375 61.78125 C 332.9115 61.684204 332.85709 61.597145 332.78125 61.53125 L 323.3125 54.5 C 323.18929 54.402174 323.02747 54.367498 322.875 54.40625 z " inkscape:radius="-1.1144003" sodipodi:type="inkscape:offset" style="opacity:0.36936939;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient7571);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <path d="M 317.5625,59.5 L 306.96875,62.28125 L 304.6875,69.40625 L 317.4375,66.59375 L 317.5625,59.5 z" id="path3524" inkscape:original="M 318 58.9375 L 306.625 61.9375 L 304.0625 70 L 317.875 66.96875 L 318 58.9375 z " inkscape:radius="-0.43543664" sodipodi:type="inkscape:offset" style="opacity:0.53153154;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient7573);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ <path d="M 323.0625,61.90625 L 323.0625,70.09375 L 333.71875,70.09375 L 331.59375,62.5625 C 331.53244,62.342161 331.46293,62.136199 331.375,62.03125 C 331.28708,61.926301 331.20816,61.90625 331.0625,61.90625 L 323.0625,61.90625 z" id="path3593" style="opacity:0.36936939;fill:url(#linearGradient7575);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0.69999992;stroke-opacity:1;display:inline;enable-background:new"/>
+ <g id="g3562">
+ <path d="M 323,54.90625 L 323.125,63.03125 L 334.9375,70.0625 L 332.46875,61.9375 L 323,54.90625 z" id="path3468" inkscape:original="M 323 54.90625 L 323.125 63.03125 L 334.9375 70.0625 L 332.46875 61.9375 L 323 54.90625 z " inkscape:radius="-0.0019137046" sodipodi:type="inkscape:offset" style="fill:url(#linearGradient7577);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ <path d="M 322.875,54.40625 C 322.64926,54.465917 322.49407,54.672835 322.5,54.90625 L 322.625,63.03125 C 322.62421,63.211268 322.71951,63.378038 322.875,63.46875 L 334.6875,70.5 C 334.86776,70.596275 335.08831,70.574158 335.24586,70.444006 C 335.40342,70.313855 335.46677,70.101441 335.40625,69.90625 L 332.9375,61.78125 C 332.9115,61.684204 332.85709,61.597145 332.78125,61.53125 L 323.3125,54.5 C 323.18929,54.402174 323.02747,54.367498 322.875,54.40625 z" id="path3491" inkscape:original="M 323 54.90625 L 323.125 63.03125 L 334.9375 70.0625 L 332.46875 61.9375 L 323 54.90625 z " inkscape:radius="0.50389111" sodipodi:type="inkscape:offset" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient7579);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ <path d="M 323.46875,55.84375 L 323.59375,62.75 L 334.125,69.03125 L 332.03125,62.21875 L 323.46875,55.84375 z" id="path3526" inkscape:original="M 323 54.90625 L 323.125 63.03125 L 334.9375 70.0625 L 332.46875 61.9375 L 323 54.90625 z " inkscape:radius="-0.47827792" sodipodi:type="inkscape:offset" style="opacity:0.25225226;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient7581);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ <path d="M 323,54.90625 L 323.125,63.03125 L 334.9375,70.0625 L 324.0625,62.81342 L 324,56.49908 L 332.46875,61.9375 L 323,54.90625 z" id="path3548" sodipodi:nodetypes="ccccccc" style="opacity:0.36936939;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:new"/>
+ </g>
+ <rect height="1" id="rect3604" style="opacity:0.20720723;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.50000001;stroke-opacity:1;display:inline;enable-background:new" width="3" x="319" y="62"/>
+ </g>
+ <path d="M 26.5,38.700001 A 13.75,3.8 0 1 1 -1,38.700001 A 13.75,3.8 0 1 1 26.5,38.700001 z" id="path2858" sodipodi:cx="12.75" sodipodi:cy="38.700001" sodipodi:rx="13.75" sodipodi:ry="3.8" sodipodi:type="arc" style="opacity:0.5;fill:url(#radialGradient3042);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.80000001;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" transform="matrix(1.0181818,0,0,1.0526316,20.018182,3.2631562)"/>
+ <path d="M 47.500174,31.999486 C 47.500174,40.007969 41.007746,46.500175 33.000183,46.500175 C 24.991885,46.500175 18.499825,40.007895 18.499825,31.999486 C 18.499825,23.99137 24.991885,17.499825 33.000183,17.499825 C 41.007746,17.499825 47.500174,23.99137 47.500174,31.999486 L 47.500174,31.999486 z" id="path6495" style="fill:url(#radialGradient2707);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3927);stroke-width:0.9996503;stroke-miterlimit:4;stroke-dasharray:none"/>
+ <path d="M 46.500204,31.99952 C 46.500204,39.455718 40.455512,45.500205 33.00017,45.500205 C 25.544146,45.500205 19.499795,39.45565 19.499795,31.99952 C 19.499795,24.543666 25.544146,18.499795 33.00017,18.499795 C 40.455512,18.499795 46.500204,24.543666 46.500204,31.99952 L 46.500204,31.99952 z" id="path8655" style="opacity:0.6;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2704);stroke-width:0.99959099;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/>
+ <path d="M 23.5,32.5 L 33,22.5 L 42.5,32.5 L 36.75,32.5 L 36.75,41.5 L 29.25,41.5 L 29.25,32.5 L 23.5,32.5 z" id="path4236" sodipodi:nodetypes="cccccccc" style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"/>
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/console.svg b/resources/multimc/scalable/console.svg
new file mode 100644
index 00000000..ec14ab68
--- /dev/null
+++ b/resources/multimc/scalable/console.svg
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32px"
+ height="32px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="console.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/console.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs2987">
+ <linearGradient
+ id="linearGradient6244">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop6246" />
+ <stop
+ id="stop6254"
+ offset="0.4642857"
+ style="stop-color:#000000;stop-opacity:1" />
+ <stop
+ id="stop6252"
+ offset="0.53571427"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop6248" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6212">
+ <stop
+ style="stop-color:#959595;stop-opacity:1"
+ offset="0"
+ id="stop6214" />
+ <stop
+ id="stop6224"
+ offset="0.14849657"
+ style="stop-color:#b0b0b0;stop-opacity:1;" />
+ <stop
+ id="stop6220"
+ offset="0.41380492"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ style="stop-color:#cacaca;stop-opacity:1;"
+ offset="0.65110856"
+ id="stop6222" />
+ <stop
+ id="stop6228"
+ offset="0.87847149"
+ style="stop-color:#b0b0b0;stop-opacity:1;" />
+ <stop
+ style="stop-color:#969696;stop-opacity:1;"
+ offset="1"
+ id="stop6216" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6194"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#8e8e8e;stop-opacity:1;"
+ offset="0"
+ id="stop6196" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3050">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3052" />
+ <stop
+ id="stop3840"
+ offset="0.64285713"
+ style="stop-color:#164315;stop-opacity:1" />
+ <stop
+ id="stop3838"
+ offset="0.85714287"
+ style="stop-color:#24a91f;stop-opacity:1" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1"
+ offset="1"
+ id="stop3054" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3050"
+ id="linearGradient3056"
+ x1="15"
+ y1="16"
+ x2="15"
+ y2="2"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="reflect" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6212"
+ id="linearGradient6218"
+ x1="19.373737"
+ y1="18.689655"
+ x2="30.317204"
+ y2="31.204504"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="repeat" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6244"
+ id="linearGradient6250"
+ x1="2"
+ y1="2"
+ x2="30"
+ y2="30"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6244"
+ id="linearGradient6258"
+ gradientUnits="userSpaceOnUse"
+ x1="2"
+ y1="2"
+ x2="30"
+ y2="30"
+ gradientTransform="matrix(1.0666667,0,0,1.0666667,-33.066667,-33.066667)" />
+ <filter
+ inkscape:collect="always"
+ id="filter6272">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.32596875"
+ id="feGaussianBlur6274" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.5"
+ inkscape:cx="-151.66767"
+ inkscape:cy="-123.35228"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2995"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ id="rect2993"
+ width="32"
+ height="32"
+ x="-1.9984014e-15"
+ y="4.4408921e-16"
+ ry="2.6666667" />
+ <rect
+ style="fill:url(#linearGradient3056);fill-opacity:1;stroke:none"
+ id="rect2993-1"
+ width="28"
+ height="28"
+ x="2"
+ y="2"
+ ry="2.3333333" />
+ <g
+ style="font-size:85.93203735px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00ff00;fill-opacity:1;stroke:none;font-family:Sans"
+ id="text3001">
+ <path
+ style="font-size:45.2372551px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#008000;fill-opacity:1;stroke:none;filter:url(#filter6272);font-family:Sans"
+ d="m 7.4824597,10.427541 2.2812496,5.78125 0.2812497,-0.71875 -1.9999993,-5.0625 -0.5625,0 z m 8.4687493,0 0,11.34375 -1.875,0 0,0.65625 2.6875,0 0,-12 -0.8125,0 z m 11.625,0.3125 0,2.21875 c -0.5312,-0.3906 -1.046212,-0.693063 -1.5625,-0.875 -0.516305,-0.181918 -1.054102,-0.24999 -1.625,-0.25 -1.022671,10e-6 -1.820457,0.324952 -2.40625,1.03125 -0.585803,0.706316 -0.875004,1.696118 -0.875,2.9375 -4e-6,1.236043 0.289197,2.199946 0.875,2.90625 0.23333,0.281334 0.509858,0.486976 0.8125,0.65625 -0.585803,-0.706304 -0.875004,-1.670207 -0.875,-2.90625 -4e-6,-1.241382 0.289197,-2.231184 0.875,-2.9375 0.585793,-0.706298 1.383579,-1.06249 2.40625,-1.0625 0.570898,10e-6 1.139945,0.09933 1.65625,0.28125 0.516288,0.181937 1.0313,0.45315 1.5625,0.84375 l 0,-2.46875 c -0.271888,-0.152046 -0.55994,-0.261514 -0.84375,-0.375 z m -21.2812493,2.28125 0,8.75 -1.875,0 0,0.65625 2.71875,0 0,-7.28125 -0.84375,-2.125 z m 6.9374993,0 -2.5625,6.46875 -1.2187493,0 0.2499996,0.625 1.8124997,0 1.71875,-4.34375 0,-2.75 z m 15.1875,6.25 c -0.287318,0.211276 -0.560795,0.412277 -0.84375,0.5625 l 0,1.28125 c -0.526236,0.294295 -1.054104,0.537677 -1.625,0.6875 -0.570913,0.149823 -1.191958,0.21875 -1.8125,0.21875 -1.332472,0 -2.440972,-0.298702 -3.375,-0.875 1.063521,0.998245 2.465058,1.5 4.21875,1.5 0.620542,0 1.210337,-0.06893 1.78125,-0.21875 0.570896,-0.149823 1.130014,-0.361955 1.65625,-0.65625 l 0,-2.5 z"
+ id="text3022"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:45.2372551px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00ff00;fill-opacity:1;stroke:none;font-family:Sans"
+ x="2.7196128"
+ y="21.15748"
+ id="text3014"
+ sodipodi:linespacing="125%"
+ transform="scale(0.9632149,1.0381899)"><tspan
+ sodipodi:role="line"
+ id="tspan3016"
+ x="2.7196128"
+ y="21.15748"
+ style="font-size:15.83304024000000076px;font-weight:bold;-inkscape-font-specification:Bitstream Vera Sans Bold;font-family:Bitstream Vera Sans;font-style:normal;font-stretch:normal;font-variant:normal">MC</tspan></text>
+ </g>
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/console_error.svg b/resources/multimc/scalable/console_error.svg
new file mode 100644
index 00000000..a71c6b35
--- /dev/null
+++ b/resources/multimc/scalable/console_error.svg
@@ -0,0 +1,247 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32px"
+ height="32px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="console_error.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/console_error.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs2987">
+ <linearGradient
+ id="linearGradient6244">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop6246" />
+ <stop
+ id="stop6254"
+ offset="0.4642857"
+ style="stop-color:#000000;stop-opacity:1" />
+ <stop
+ id="stop6252"
+ offset="0.53571427"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop6248" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6212">
+ <stop
+ style="stop-color:#959595;stop-opacity:1"
+ offset="0"
+ id="stop6214" />
+ <stop
+ id="stop6224"
+ offset="0.14849657"
+ style="stop-color:#b0b0b0;stop-opacity:1;" />
+ <stop
+ id="stop6220"
+ offset="0.41380492"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ style="stop-color:#cacaca;stop-opacity:1;"
+ offset="0.65110856"
+ id="stop6222" />
+ <stop
+ id="stop6228"
+ offset="0.87847149"
+ style="stop-color:#b0b0b0;stop-opacity:1;" />
+ <stop
+ style="stop-color:#969696;stop-opacity:1;"
+ offset="1"
+ id="stop6216" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6194"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#8e8e8e;stop-opacity:1;"
+ offset="0"
+ id="stop6196" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3050">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3052" />
+ <stop
+ id="stop3840"
+ offset="0.64285713"
+ style="stop-color:#431515;stop-opacity:1;" />
+ <stop
+ id="stop3838"
+ offset="0.85714287"
+ style="stop-color:#a91f1f;stop-opacity:1;" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1"
+ offset="1"
+ id="stop3054" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3050"
+ id="linearGradient3056"
+ x1="15"
+ y1="16"
+ x2="15"
+ y2="2"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="reflect" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6244"
+ id="linearGradient6250"
+ x1="2"
+ y1="2"
+ x2="30"
+ y2="30"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6244"
+ id="linearGradient6258"
+ gradientUnits="userSpaceOnUse"
+ x1="2"
+ y1="2"
+ x2="30"
+ y2="30"
+ gradientTransform="matrix(1.0666667,0,0,1.0666667,-33.066667,-33.066667)" />
+ <filter
+ inkscape:collect="always"
+ id="filter6373">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.64"
+ id="feGaussianBlur6375" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4142136"
+ inkscape:cx="149.24645"
+ inkscape:cy="89.508966"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2995"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ id="rect2993"
+ width="32"
+ height="32"
+ x="-1.9984014e-15"
+ y="4.4408921e-16"
+ ry="2.6666667" />
+ <rect
+ style="fill:url(#linearGradient3056);fill-opacity:1;stroke:none"
+ id="rect2993-1"
+ width="28"
+ height="28"
+ x="2"
+ y="2"
+ ry="2.3333333" />
+ <g
+ style="font-size:85.93203735px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00ff00;fill-opacity:1;stroke:none;font-family:Sans"
+ id="text3001" />
+ <g
+ id="g6331"
+ transform="translate(6,-7)">
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter6373)"
+ id="rect6363"
+ width="16"
+ height="16"
+ x="8"
+ y="8"
+ transform="translate(-6,7)" />
+ <path
+ id="path6377"
+ d="m 4,17 0,4 4,0 0,-4 z"
+ style="fill:#800000;fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#800000;fill-opacity:1;stroke:none"
+ d="m 8,21 0,2 -2,0 0,6 2,0 0,-2 4,0 0,2 2,0 0,-6 -2,0 0,-2 z"
+ id="path6379"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ style="fill:#800000;fill-opacity:1;stroke:none"
+ d="m 12,17 0,4 4,0 0,-4 z"
+ id="path6381" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ style="fill:#ff0000;fill-opacity:1;stroke:none"
+ d="m 6,19 0,2 2,0 0,-2 z"
+ id="path6383" />
+ <path
+ id="path6385"
+ d="m 12,19 0,2 2,0 0,-2 z"
+ style="fill:#ff0000;fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path6387"
+ d="m 8,23 0,2 -2,0 0,4 2,0 0,-2 4,0 0,2 2,0 0,-4 -2,0 0,-2 z"
+ style="fill:#ff0000;fill-opacity:1;stroke:none" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/new.svg b/resources/multimc/scalable/new.svg
new file mode 100644
index 00000000..c9cff358
--- /dev/null
+++ b/resources/multimc/scalable/new.svg
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) --><svg height="48" id="svg249" inkscape:export-filename="/home/luca/Desktop/flare-16.png" inkscape:export-xdpi="30" inkscape:export-ydpi="30" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.45+0.46pre0" sodipodi:docbase="/home/dobey/Projects/gnome-icon-theme/scalable/actions" sodipodi:docname="document-new.svg" sodipodi:version="0.32" version="1.0" width="48" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <metadata>
+ <rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <cc:Work rdf:about="">
+ <dc:title>New Document</dc:title>
+ <dc:description></dc:description>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>regular</rdf:li>
+ <rdf:li>plaintext</rdf:li>
+ <rdf:li>text</rdf:li>
+ <rdf:li>document</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent rdf:about="http://www.openclipart.org/">
+ <dc:title>Source: GNOME Icon Theme, Source: GNOME Icon Theme, Source: GNOME Icon Theme, Source: GNOME Icon Theme, Source: GNOME Icon Theme</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Jakub Steiner</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>Jakub Steiner</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date></dc:date>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <cc:license rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"/>
+ <dc:language>en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs3">
+ <linearGradient id="linearGradient3656" inkscape:collect="always">
+ <stop id="stop3658" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3660" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient4253" inkscape:collect="always" x1="-26.753757" x2="-24.75" xlink:href="#linearGradient3656" y1="11.566258" y2="9.687501"/>
+ <linearGradient id="linearGradient3520" inkscape:collect="always">
+ <stop id="stop3522" offset="0" style="stop-color:#000000;stop-opacity:0.41295547"/>
+ <stop id="stop3524" offset="1" style="stop-color:#000000;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(0.9223058,0,0,0.9185751,-92.447368,1.3256997)" gradientUnits="userSpaceOnUse" id="linearGradient4273" inkscape:collect="always" x1="-18.588562" x2="-28.789402" xlink:href="#linearGradient3520" y1="11.052948" y2="14.069944"/>
+ <linearGradient id="linearGradient3671">
+ <stop id="stop3673" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3691" offset="0.47533694" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3675" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="-26.305403" cy="10.108011" fx="-26.305403" fy="10.108011" gradientTransform="matrix(0.4073362,-0.2798276,0.7510293,1.0932492,-115.18484,-8.4378699)" gradientUnits="userSpaceOnUse" id="radialGradient4276" inkscape:collect="always" r="7.0421038" xlink:href="#linearGradient3671"/>
+ <linearGradient id="linearGradient3741" inkscape:collect="always">
+ <stop id="stop3743" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop3745" offset="1" style="stop-color:#ffffff;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="4" cy="5.2999997" fx="4" fy="5.2999997" gradientTransform="matrix(1.8860258,0,0,1.1764706,-3.5441033,-4.2352941)" gradientUnits="userSpaceOnUse" id="radialGradient4247" inkscape:collect="always" r="17" xlink:href="#linearGradient3741"/>
+ <linearGradient id="linearGradient3613" inkscape:collect="always">
+ <stop id="stop3615" offset="0" style="stop-color:#888a85;stop-opacity:1"/>
+ <stop id="stop3617" offset="1" style="stop-color:#babdb6;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="translate(-90,0)" gradientUnits="userSpaceOnUse" id="linearGradient4282" inkscape:collect="always" x1="-47.5" x2="-62.75" xlink:href="#linearGradient3613" y1="49.020683" y2="-22.502075"/>
+ <linearGradient id="linearGradient3683">
+ <stop id="stop3685" offset="0" style="stop-color:#f6f6f5;stop-opacity:1;"/>
+ <stop id="stop3689" offset="1" style="stop-color:#d3d7cf;stop-opacity:1"/>
+ </linearGradient>
+ <radialGradient cx="-30.249996" cy="35.357208" fx="-30.249996" fy="35.357208" gradientTransform="matrix(3.9957492,0,0,1.9350367,0.62141,-31.167422)" gradientUnits="userSpaceOnUse" id="radialGradient4280" inkscape:collect="always" r="18.000002" xlink:href="#linearGradient3683"/>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient4241" inkscape:collect="always" x1="25.058096" x2="25.058096" xlink:href="#linearGradient3702" y1="47.027729" y2="39.999443"/>
+ <radialGradient cx="4.9929786" cy="43.5" fx="4.9929786" fy="43.5" gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)" gradientUnits="userSpaceOnUse" id="radialGradient4239" inkscape:collect="always" r="2.5" xlink:href="#linearGradient3688"/>
+ <radialGradient cx="4.9929786" cy="43.5" fx="4.9929786" fy="43.5" gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)" gradientUnits="userSpaceOnUse" id="radialGradient4237" inkscape:collect="always" r="2.5" xlink:href="#linearGradient3688"/>
+ <linearGradient id="linearGradient3702">
+ <stop id="stop3704" offset="0" style="stop-color:black;stop-opacity:0;"/>
+ <stop id="stop3710" offset="0.5" style="stop-color:black;stop-opacity:1;"/>
+ <stop id="stop3706" offset="1" style="stop-color:black;stop-opacity:0;"/>
+ </linearGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient4235" inkscape:collect="always" x1="25.058096" x2="25.058096" xlink:href="#linearGradient3702" y1="47.027729" y2="39.999443"/>
+ <radialGradient cx="4.9929786" cy="43.5" fx="4.9929786" fy="43.5" gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)" gradientUnits="userSpaceOnUse" id="radialGradient4233" inkscape:collect="always" r="2.5" xlink:href="#linearGradient3688"/>
+ <linearGradient id="linearGradient3688" inkscape:collect="always">
+ <stop id="stop3690" offset="0" style="stop-color:black;stop-opacity:1;"/>
+ <stop id="stop3692" offset="1" style="stop-color:black;stop-opacity:0;"/>
+ </linearGradient>
+ <radialGradient cx="4.9929786" cy="43.5" fx="4.9929786" fy="43.5" gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)" gradientUnits="userSpaceOnUse" id="radialGradient4231" inkscape:collect="always" r="2.5" xlink:href="#linearGradient3688"/>
+ <linearGradient id="linearGradient2378">
+ <stop id="stop2380" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
+ <stop id="stop4146" offset="0.25" style="stop-color:#fefede;stop-opacity:0.91836733;"/>
+ <stop id="stop2386" offset="0.5" style="stop-color:#f5f328;stop-opacity:1;"/>
+ <stop id="stop2382" offset="1" style="stop-color:#f5f32d;stop-opacity:0.12234043;"/>
+ </linearGradient>
+ <radialGradient cx="38.658855" cy="9.3411446" fx="38.658855" fy="9.3411446" gradientUnits="userSpaceOnUse" id="radialGradient3271" inkscape:collect="always" r="8.341651" xlink:href="#linearGradient2378"/>
+ </defs>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="0.3254902" id="base" inkscape:current-layer="layer5" inkscape:cx="40.26271" inkscape:cy="28.37649" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:showpageshadow="false" inkscape:window-height="939" inkscape:window-width="929" inkscape:window-x="751" inkscape:window-y="91" inkscape:zoom="1" pagecolor="#ffffff" showborder="true" showgrid="false"/>
+ <g id="layer6" inkscape:groupmode="layer" inkscape:label="Shadow"/>
+ <g id="layer1" inkscape:groupmode="layer" inkscape:label="Base" style="display:inline"/>
+ <g id="layer5" inkscape:groupmode="layer" inkscape:label="Text" style="display:inline">
+ <g id="g4425" style="display:inline" transform="translate(150,0)">
+ <rect height="48" id="rect2503" style="opacity:0;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;display:inline" width="48" x="-150" y="0"/>
+ <g id="g4408">
+ <g id="g3697" inkscape:label="Shadow" style="opacity:0.65587045;display:inline" transform="matrix(1.0464281,0,0,0.8888889,-151.18571,5.7222396)">
+ <g id="g3699" style="opacity:0.4" transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+ <rect height="7" id="rect3701" style="opacity:1;fill:url(#radialGradient4231);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" width="5" x="38" y="40"/>
+ <rect height="7" id="rect3703" style="opacity:1;fill:url(#radialGradient4233);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" transform="scale(-1,-1)" width="5" x="-10" y="-47"/>
+ <rect height="7.0000005" id="rect3705" style="opacity:1;fill:url(#linearGradient4235);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" width="28" x="10" y="40"/>
+ </g>
+ </g>
+ <g id="g3713" inkscape:label="Shadow" style="display:inline" transform="matrix(0.9548466,0,0,0.5555562,-148.98776,19.888875)">
+ <g id="g3715" style="opacity:0.4" transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
+ <rect height="7" id="rect3717" style="opacity:1;fill:url(#radialGradient4237);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" width="5" x="38" y="40"/>
+ <rect height="7" id="rect3719" style="opacity:1;fill:url(#radialGradient4239);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" transform="scale(-1,-1)" width="5" x="-10" y="-47"/>
+ <rect height="7.0000005" id="rect3721" style="opacity:1;fill:url(#linearGradient4241);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" width="28" x="10" y="40"/>
+ </g>
+ </g>
+ <path d="M -141.47614,3.5 C -141.47614,3.5 -124,3.5 -122.5,3.5 C -118.62295,3.5729425 -116,6 -113.5,8.5 C -111,11 -108.89232,13.752625 -108.5,17.5 C -108.5,19 -108.5,42.476142 -108.5,42.476142 C -108.5,43.597359 -109.40264,44.5 -110.52385,44.5 L -141.47614,44.5 C -142.59736,44.5 -143.5,43.597359 -143.5,42.476142 L -143.5,5.523858 C -143.5,4.402641 -142.59736,3.5 -141.47614,3.5 z" id="path3499" sodipodi:nodetypes="ccsccccccc" style="fill:url(#radialGradient4280);fill-opacity:1;stroke:url(#linearGradient4282);stroke-width:1;stroke-miterlimit:4;display:inline"/>
+ <path d="M 8.53125,4 C 7.6730803,4 7,4.6730802 7,5.53125 L 7,42.46875 C 7,43.32692 7.6730802,44 8.53125,44 L 39.46875,44 C 40.326919,44 41,43.326918 41,42.46875 C 41,42.46875 41,19 41,17.5 C 41,16.10803 40.513021,13.200521 38.65625,11.34375 C 36.65625,9.34375 35.65625,8.34375 33.65625,6.34375 C 31.799479,4.4869792 28.89197,4 27.5,4 C 26,4 8.53125,4 8.53125,4 z" id="path3735" inkscape:original="M 8.53125 3.5 C 7.410033 3.5 6.5 4.4100329 6.5 5.53125 L 6.5 42.46875 C 6.5 43.589967 7.4100329 44.5 8.53125 44.5 L 39.46875 44.5 C 40.589967 44.5 41.5 43.589966 41.5 42.46875 C 41.5 42.46875 41.5 19 41.5 17.5 C 41.5 16 41 13 39 11 C 37 9 36 8 34 6 C 32 4 29 3.5 27.5 3.5 C 26 3.5 8.5312499 3.5 8.53125 3.5 z " inkscape:radius="-0.4861359" sodipodi:type="inkscape:offset" style="opacity:0.68016196;fill:url(#radialGradient4247);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;display:inline" transform="translate(-150,0)"/>
+ <path d="M -122.5,4 C -123.88889,4 -122.54207,4.4970883 -121.15625,5.125 C -119.77043,5.7529117 -116.18337,8.3400517 -117,12 C -112.67669,11.569417 -110.32087,15.122378 -110,16.28125 C -109.67913,17.440122 -109,18.888889 -109,17.5 C -108.97167,13.694419 -111.84543,11.068299 -113.84375,8.84375 C -115.84207,6.6192012 -118.84621,4.4767615 -122.5,4 z" id="path3666" sodipodi:nodetypes="ccccczc" style="fill:url(#radialGradient4276);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;display:inline"/>
+ <path d="M -121.39912,5.0143528 C -120.47682,5.0143528 -118.39068,11.210015 -119.31298,15.343603 C -115.01802,14.915844 -110.4596,15.43178 -110,16.28125 C -110.32087,15.122378 -112.67669,11.569417 -117,12 C -116.13534,8.124761 -120.18657,5.3827023 -121.39912,5.0143528 z" id="path3625" sodipodi:nodetypes="ccccc" style="opacity:0.87854249;fill:url(#linearGradient4273);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;display:inline"/>
+ <path d="M -51.46875,4.5 C -52.051916,4.5 -52.5,4.9480842 -52.5,5.53125 L -52.5,42.46875 C -52.5,43.051915 -52.051914,43.5 -51.46875,43.5 L -20.53125,43.5 C -19.948085,43.5 -19.5,43.051914 -19.5,42.46875 C -19.5,42.46875 -19.5,19 -19.5,17.5 C -19.5,16.220971 -19.980469,13.394531 -21.6875,11.6875 C -23.6875,9.6875 -24.6875,8.6875 -26.6875,6.6875 C -28.394531,4.9804687 -31.220971,4.5 -32.5,4.5 C -34,4.5 -51.46875,4.5 -51.46875,4.5 z" id="path3650" inkscape:original="M -51.46875 3.5 C -52.589967 3.5 -53.5 4.4100329 -53.5 5.53125 L -53.5 42.46875 C -53.5 43.589967 -52.589966 44.5 -51.46875 44.5 L -20.53125 44.5 C -19.410033 44.5 -18.5 43.589966 -18.5 42.46875 C -18.5 42.46875 -18.5 19 -18.5 17.5 C -18.5 16 -19 13 -21 11 C -23 9 -24 8 -26 6 C -28 4 -31 3.5 -32.5 3.5 C -34 3.5 -51.468749 3.5 -51.46875 3.5 z " inkscape:radius="-0.99436891" sodipodi:type="inkscape:offset" style="fill:none;fill-opacity:1;stroke:url(#linearGradient4253);stroke-width:1;stroke-miterlimit:4;display:inline" transform="translate(-90,0)"/>
+ </g>
+ </g>
+ <g id="g14103">
+ <path d="M 47.000506 9.3411446 A 8.341651 8.341651 0 1 1 30.317204,9.3411446 A 8.341651 8.341651 0 1 1 47.000506 9.3411446 z" id="path2388" sodipodi:cx="38.658855" sodipodi:cy="9.3411446" sodipodi:rx="8.341651" sodipodi:ry="8.341651" sodipodi:type="arc" style="fill:url(#radialGradient3271);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="matrix(1.14985,0,0,1.14985,-7.595328,0.490895)"/>
+ <path d="M 44.520054,15.50279 C 44.012883,16.381236 39.925351,15.341967 38.998703,15.754538 C 38.072055,16.167108 36.109289,19.900142 35.117113,19.689249 C 34.124936,19.478355 33.850222,15.26973 33.171495,14.515926 C 32.492767,13.762123 28.335913,13.048993 28.229885,12.040207 C 28.123857,11.031421 32.041607,9.4696164 32.548778,8.5911701 C 33.055949,7.7127238 32.449637,3.5389508 33.376285,3.1263806 C 34.302933,2.7138103 36.998949,5.957187 37.991126,6.1680807 C 38.983302,6.3789743 42.765436,4.5125708 43.444163,5.2663741 C 44.122891,6.0201775 41.871371,9.5864995 41.977399,10.595285 C 42.083426,11.604071 45.027225,14.624343 44.520054,15.50279 z " id="path1345" inkscape:flatsided="false" inkscape:randomized="0" inkscape:rounded="0.18352206" sodipodi:arg1="0.52359878" sodipodi:arg2="1.1519173" sodipodi:cx="36.9375" sodipodi:cy="11.125" sodipodi:r1="8.755579" sodipodi:r2="5.0676599" sodipodi:sides="5" sodipodi:type="star" style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" transform="matrix(0.674116,0.299577,-0.299577,0.674116,15.46413,-7.192469)"/>
+ </g>
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/news.svg b/resources/multimc/scalable/news.svg
new file mode 100644
index 00000000..67a370df
--- /dev/null
+++ b/resources/multimc/scalable/news.svg
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128"
+ height="128"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="news.svg">
+ <defs
+ id="defs2987">
+ <linearGradient
+ id="linearGradient4095">
+ <stop
+ style="stop-color:#fff7d8;stop-opacity:1;"
+ offset="0"
+ id="stop4097" />
+ <stop
+ style="stop-color:#ffeca0;stop-opacity:1;"
+ offset="1"
+ id="stop4099" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3898"
+ x="-0.11333333"
+ width="1.2266667"
+ y="-0.10074074"
+ height="1.2014815">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.5111111"
+ id="feGaussianBlur3900" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4091">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="3.6378601"
+ id="feGaussianBlur4093" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4095"
+ id="linearGradient4101"
+ x1="88.388351"
+ y1="94.942757"
+ x2="99.525276"
+ y2="103.95837"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.25"
+ inkscape:cx="-700.46253"
+ inkscape:cy="-25.005652"
+ inkscape:current-layer="text3832"
+ showgrid="false"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1"
+ showguides="false"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2993"
+ empspacing="8"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <inkscape:grid
+ type="xygrid"
+ id="grid2995"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ color="#00ff0b"
+ opacity="0.08235294"
+ empcolor="#00ff22"
+ empopacity="0.23137255" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="41,100"
+ id="guide4165" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="68,106"
+ id="guide4167" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="73,109"
+ id="guide4169" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="80,106"
+ id="guide4171" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="83,102"
+ id="guide4173" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="104,104"
+ id="guide4175" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ transform="translate(0,64)">
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path3778"
+ d="m 16,8 96,0 0,76 c -9.14072,13.804136 -19.955033,25.58254 -32,36 l -64,0 z"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;filter:url(#filter4091)"
+ transform="translate(0,-64)" />
+ <path
+ transform="translate(0,-64)"
+ style="color:#000000;fill:#fff6d3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 16,8 96,0 0,76 c -9.14072,13.804136 -19.955033,25.58254 -32,36 l -64,0 z"
+ id="rect2997"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ style="color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 68,-28 36,0 0,56 -16,20 -20,0 z"
+ id="rect4044"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <g
+ id="g4129">
+ <path
+ id="rect4103"
+ d="m 24,36 36,0 0,36 -36,0 z"
+ style="color:#000000;fill:#fff6d3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ transform="translate(0,-64)" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="rect4107"
+ d="m 68,36 36,0 0,57 -16,19 -20,0 z"
+ style="color:#000000;fill:#fff6d3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ transform="translate(0,-64)" />
+ <flowRoot
+ id="flowRoot4115"
+ style="font-size:8px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><flowRegion
+ id="flowRegion4117"><use
+ height="128"
+ width="128"
+ id="use4119"
+ xlink:href="#rect4103"
+ y="0"
+ x="0" /><use
+ height="128"
+ width="128"
+ id="use4121"
+ xlink:href="#rect4107"
+ y="0"
+ x="0" /></flowRegion><flowPara
+ style="font-size:2px;font-weight:bold;text-align:justify;text-anchor:start;-inkscape-font-specification:Sans Bold"
+ id="flowPara4123">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce convallis mauris ullamcorper mauris viverra molestie. Donec ultricies faucibus laoreet. Donec convallis congue neque consequat vehicula. Morbi condimentum tempor nulla et rhoncus. Etiam auctor, augue eu pharetra congue, elit justo lacinia risus, non lacinia est justo sed erat. Ut risus urna, viverra id interdum in, molestie non sem. Morbi leo orci, gravida auctor tempor vel, varius et enim. Nulla sem enim, ultricies vel laoreet ac, semper vel mauris. Ut adipiscing sapien sed leo pretium id vulputate erat gravida. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras tempor leo sit amet velit molestie commodo eget tincidunt leo. Cras dictum metus non ante pulvinar pellentesque. Morbi id elit ullamcorper mi vulputate lobortis. Cras ac vehicula felis. Phasellus dictum, tellus at molestie pellentesque, purus purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce convallis mauris ullamcorper mauris viverra molestie. Donec ultricies faucibus laoreet. Donec convallis congue neque consequat vehicula. Morbi condimentum tempor nulla et rhoncus. Etiam auctor, augue eu pharetra congue, elit justo lacinia risus, non lacinia est justo sed erat. Ut risus urna, </flowPara></flowRoot> </g>
+ <path
+ style="opacity:0.41176471;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3898);enable-background:accumulate"
+ d="M 85.522922,28.087287 C 96.299051,25.849792 101.98214,24.118305 110.44998,20.924851 101.30926,34.728987 91.732381,44.562847 79.687414,54.980307 83.49938,42.627824 86.087749,33.764885 85.522922,28.087287 z"
+ id="path3848"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <g
+ transform="scale(1.3146517,0.76065775)"
+ style="font-size:26.48733711px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#008000;fill-opacity:1;stroke:none;font-family:Sans"
+ id="text3832">
+ <path
+ d="m 30.25786,-47.327461 -2.845319,0 -6.874782,-17.090472 0,17.090472 -2.281973,0 0,-21.034427 3.042631,0 6.84592,17.090472 0,-17.090472 2.281973,0 0,21.034427"
+ style="font-variant:normal;font-stretch:normal;fill:#008000;font-family:Oxygen Mono;-inkscape-font-specification:Oxygen Mono"
+ id="path4157"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ d="m 37.27223,-56.530023 0,6.573259 7.606577,0 0.818953,2.629303 -10.707504,0 0,-21.034427 10.649209,0 -0.760658,2.629303 -7.606577,0 0.0038,6.512419 7.602816,0 -3.9e-5,2.690143"
+ style="font-variant:normal;font-stretch:normal;fill:#008000;font-family:Oxygen Mono;-inkscape-font-specification:Oxygen Mono"
+ id="path4159"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccc" />
+ <path
+ d="m 60.091962,-50.290057 0.760658,-18.071831 2.281973,0 -1.521316,21.034427 -3.04263,0 -2.281974,-7.88791 -2.277041,7.88791 -3.047563,0 -1.61189,-21.034427 2.368129,0 0.765077,18.405124 3.042631,-9.202562 1.521315,0 3.042631,8.869269"
+ style="font-variant:normal;font-stretch:normal;fill:#008000;font-family:Oxygen Mono;-inkscape-font-specification:Oxygen Mono"
+ id="path4161"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccc" />
+ <path
+ d="m 67.201805,-51.228789 c 1.498704,1.191317 3.283103,1.786974 5.3532,1.786972 1.124022,2e-6 1.990463,-0.331929 2.599324,-0.995793 0.608839,-0.672953 0.913263,-1.586899 0.913275,-2.741842 -1.2e-5,-0.336472 -0.04684,-0.650214 -0.140504,-0.941229 -0.08431,-0.291 -0.192033,-0.545633 -0.323158,-0.763896 -0.13115,-0.218249 -0.318487,-0.431957 -0.562017,-0.641127 -0.234183,-0.209154 -0.449623,-0.377393 -0.646317,-0.504718 -0.196716,-0.1364 -0.45899,-0.281905 -0.786822,-0.436511 -0.318486,-0.15459 -0.585443,-0.272812 -0.800874,-0.354666 -0.206079,-0.08183 -0.487089,-0.190965 -0.843023,-0.327385 -0.346583,-0.1364 -0.604173,-0.240981 -0.772772,-0.313742 -0.580755,-0.236435 -1.067835,-0.445597 -1.461242,-0.627488 -0.384048,-0.190962 -0.810244,-0.450141 -1.278584,-0.777536 -0.468351,-0.327373 -0.843028,-0.668397 -1.124032,-1.023075 -0.28101,-0.354653 -0.519866,-0.795713 -0.716571,-1.323177 -0.187339,-0.536533 -0.281009,-1.127643 -0.281007,-1.773331 -2e-6,-1.700561 0.594798,-3.019191 1.784399,-3.955891 1.189595,-0.945755 2.721087,-1.418643 4.594479,-1.418664 2.360456,2.1e-5 4.201056,0.504737 5.521806,1.514151 l -0.983527,1.964305 c -1.217713,-0.918476 -2.683637,-1.377724 -4.397774,-1.377741 -1.105305,1.7e-5 -1.985796,0.295573 -2.641474,0.886664 -0.646325,0.582034 -0.969483,1.386852 -0.969477,2.414458 -6e-6,0.400151 0.08429,0.773005 0.252905,1.118562 0.168601,0.336492 0.369989,0.618405 0.604168,0.845743 0.234168,0.218269 0.543277,0.436525 0.927327,0.654768 0.393404,0.218268 0.735296,0.386507 1.025679,0.504717 0.290365,0.109139 0.66036,0.250096 1.10998,0.422871 0.449605,0.163703 0.777446,0.291019 0.983528,0.381948 0.562005,0.245549 1.044402,0.472898 1.44719,0.682049 0.402767,0.20008 0.833645,0.472899 1.292638,0.81846 0.468332,0.336489 0.847692,0.691155 1.13808,1.064 0.290362,0.372862 0.529218,0.832109 0.716571,1.377741 0.196691,0.536553 0.295042,1.123115 0.295058,1.75969 -1.6e-5,1.955213 -0.585447,3.446628 -1.756299,4.474248 -1.161511,1.018529 -2.753887,1.527792 -4.777134,1.527793 -2.519709,-10e-7 -4.6179,-0.541094 -6.294577,-1.623281 l 0.997578,-2.278047"
+ style="font-variant:normal;font-stretch:normal;fill:#008000;font-family:Oxygen Mono;-inkscape-font-specification:Oxygen Mono"
+ id="path4163"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path3838"
+ d="M 88.181818,93.090909 C 100.37216,91.737298 104.94638,87.547007 112,84 c -9.14072,13.804136 -19.955033,25.58254 -32,36 4.69585,-10.93827 8.746645,-21.231493 8.181818,-26.909091 z"
+ style="color:#000000;fill:url(#linearGradient4101);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:1"
+ transform="translate(0,-64)" />
+ <rect
+ style="color:#000000;fill:#accc74;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect4138"
+ width="36"
+ height="36"
+ x="24"
+ y="76"
+ transform="translate(0,-64)" />
+ <g
+ id="g4000"
+ transform="matrix(0.60097005,0,0,0.60097012,3.5088114,25.221343)"
+ style="fill:#333333">
+ <g
+ id="g3937"
+ style="fill:#333333">
+ <rect
+ style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3906"
+ width="16"
+ height="16"
+ x="40"
+ y="-16" />
+ <rect
+ style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3908"
+ width="16"
+ height="16"
+ x="72"
+ y="-16" />
+ <path
+ style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 56,64 0,8 -8,0 0,24 8,0 0,-8 16,0 0,8 8,0 0,-24 -8,0 0,-8 -16,0 z"
+ transform="translate(0,-64)"
+ id="rect3910"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 24,32 80,0"
+ id="path3998"
+ inkscape:connector-curvature="0"
+ transform="translate(0,-64)" />
+ <rect
+ style="color:#000000;fill:none;stroke:#666666;stroke-width:0.9014551;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect4007"
+ width="36.058205"
+ height="36.058208"
+ x="23.941793"
+ y="12" />
+ <rect
+ style="color:#000000;fill:none;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect4034"
+ width="36"
+ height="32"
+ x="24"
+ y="-28" />
+ </g>
+</svg>
diff --git a/resources/multimc/scalable/viewfolder.svg b/resources/multimc/scalable/viewfolder.svg
new file mode 100644
index 00000000..4ba0ed0a
--- /dev/null
+++ b/resources/multimc/scalable/viewfolder.svg
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) --><svg height="128" id="svg2811" inkscape:export-filename="/home/david/document-open.png" inkscape:export-xdpi="22.5" inkscape:export-ydpi="22.5" inkscape:output_extension="org.inkscape.output.svgz.inkscape" inkscape:version="0.45.1" sodipodi:docbase="/home/jakob/dev/kde/src/kdebase/runtime/pics/oxygen/scalable/actions" sodipodi:docname="document-open-folder.svgz" sodipodi:version="0.32" version="1.0" width="128" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <metadata>
+ <rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <cc:Work rdf:about="">
+ <dc:title></dc:title>
+ <dc:description></dc:description>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>unsorted</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title></dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title></dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date></dc:date>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/ or http://creativecommons.org/licenses/LGPL/2.1/"/>
+ <dc:language>en</dc:language>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" gridempspacing="2" gridspacingx="4px" gridspacingy="4px" gridtolerance="10000" guidetolerance="10.0" id="base" inkscape:current-layer="svg2811" inkscape:cx="64" inkscape:cy="64" inkscape:grid-bbox="true" inkscape:grid-points="true" inkscape:guide-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="736" inkscape:window-width="825" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="1" objecttolerance="10.0" pagecolor="#ffffff" showborder="false" showgrid="false" showguides="true"/>
+ <defs id="defs2813">
+ <linearGradient gradientTransform="matrix(1.0033808,0,0,1,-8.2378002,8)" gradientUnits="userSpaceOnUse" id="linearGradient2937" x1="122.74438" x2="122.39215" y1="96.721588" y2="20.043535">
+ <stop id="stop2939" offset="0" style="stop-color:#72b4f4;stop-opacity:1"/>
+ <stop id="stop2941" offset="0.13053299" style="stop-color:#b3d9ff;stop-opacity:1"/>
+ <stop id="stop2943" offset="0.34621301" style="stop-color:#b3d9ff;stop-opacity:1"/>
+ <stop id="stop2945" offset="0.72006166" style="stop-color:#71a8f5;stop-opacity:1"/>
+ <stop id="stop2947" offset="1" style="stop-color:#508ed9;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="translate(242.00093,332.5)" gradientUnits="userSpaceOnUse" id="linearGradient2927" x1="-178" x2="-178" y1="-228.3945" y2="-304.61469">
+ <stop id="stop2929" offset="0" style="stop-color:#cfe7ff;stop-opacity:1"/>
+ <stop id="stop2931" offset="0.1" style="stop-color:#71a8f5;stop-opacity:1"/>
+ <stop id="stop2933" offset="1" style="stop-color:#2c72c7;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient2822">
+ <stop id="stop2824" offset="0" style="stop-color:#ffffff;stop-opacity:1"/>
+ <stop id="stop2826" offset="1" style="stop-color:#ffffff;stop-opacity:0"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.0033404,0,0,1,-8.2374684,8)" gradientUnits="userSpaceOnUse" id="XMLID_9_" x1="71.999496" x2="71.999496" y1="14.2578" y2="19.9583">
+ <stop id="stop46" offset="0.25" style="stop-color:#71a8f5;stop-opacity:0"/>
+ <stop id="stop48" offset="1" style="stop-color:#0057ae;stop-opacity:1"/>
+ </linearGradient>
+ <filter height="1.768" id="filter2807" width="1.0512" x="-0.025599999" y="-0.384">
+ <feGaussianBlur id="feGaussianBlur2809" inkscape:collect="always" stdDeviation="1.28"/>
+ </filter>
+ <linearGradient gradientTransform="translate(-6.999995,8)" gradientUnits="userSpaceOnUse" id="XMLID_6_" x1="72.000504" x2="72.000504" y1="96" y2="0.00048828119">
+ <stop id="stop7" offset="0" style="stop-color:#00479e;stop-opacity:1"/>
+ <stop id="stop9" offset="0.0769" style="stop-color:#2c72c7;stop-opacity:1"/>
+ <stop id="stop11" offset="0.58579999" style="stop-color:#6ea1df;stop-opacity:1"/>
+ <stop id="stop13" offset="0.96450001" style="stop-color:#adcbee;stop-opacity:1"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.0033808,0,0,1,-8.2378,8)" gradientUnits="userSpaceOnUse" id="linearGradient3109" x1="122.74438" x2="122.74438" xlink:href="#linearGradient2937" y1="96" y2="20"/>
+ <linearGradient gradientTransform="translate(242.00093,332.5)" gradientUnits="userSpaceOnUse" id="linearGradient2923" x1="-168.99216" x2="-168.99216" xlink:href="#linearGradient2822" y1="-300.5" y2="-296.48441"/>
+ <linearGradient gradientTransform="translate(242.00093,332.5)" gradientUnits="userSpaceOnUse" id="linearGradient2925" x1="-178" x2="-178" xlink:href="#linearGradient2927" y1="-228.5" y2="-304.61469"/>
+ <linearGradient gradientTransform="translate(242.00093,364.5)" gradientUnits="userSpaceOnUse" id="linearGradient2197" inkscape:collect="always" x1="-168.99216" x2="-168.99216" xlink:href="#linearGradient2822" y1="-300.5" y2="-296.48441"/>
+ <linearGradient gradientTransform="matrix(1,0,0,0.7368421,242.00093,284.36842)" gradientUnits="userSpaceOnUse" id="linearGradient2201" inkscape:collect="always" x1="-178" x2="-178" xlink:href="#linearGradient2927" y1="-228.5" y2="-304.61469"/>
+ <linearGradient gradientTransform="matrix(1.0033404,0,0,1,-7.2374684,40)" gradientUnits="userSpaceOnUse" id="linearGradient2204" inkscape:collect="always" x1="71.999496" x2="71.999496" xlink:href="#XMLID_9_" y1="14.2578" y2="19.9583"/>
+ <linearGradient gradientTransform="matrix(1.0033808,0,0,0.7368421,-8.2378,45.263158)" gradientUnits="userSpaceOnUse" id="linearGradient2207" inkscape:collect="always" x1="122.74438" x2="122.74438" xlink:href="#linearGradient2937" y1="96" y2="20"/>
+ <linearGradient gradientTransform="translate(-6.999995,20)" gradientUnits="userSpaceOnUse" id="linearGradient2212" inkscape:collect="always" x1="72.000504" x2="72.000504" xlink:href="#XMLID_6_" y1="96" y2="0.00048828119"/>
+ <filter id="filter2770" inkscape:collect="always">
+ <feGaussianBlur id="feGaussianBlur2772" inkscape:collect="always" stdDeviation="2.0786429"/>
+ </filter>
+ <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient10213" inkscape:collect="always" x1="98.617439" x2="91.228737" xlink:href="#linearGradient10207" y1="106.41443" y2="99.254974"/>
+ <radialGradient cx="102" cy="112.3047" fx="102" fy="112.3047" gradientTransform="matrix(1.295034,1.3831431e-7,-1.3627884e-7,1.2946006,-30.093452,-33.119615)" gradientUnits="userSpaceOnUse" id="radialGradient9437" inkscape:collect="always" r="139.55859" xlink:href="#XMLID_8_"/>
+ <clipPath clipPathUnits="userSpaceOnUse" id="clipPath7084">
+ <path d="M 72,88 L 40,120 L 32,120 L 32,80 L 72,80 L 72,88 z" id="path7086" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/>
+ </clipPath>
+ <filter height="1.3839999" id="filter6697" inkscape:collect="always" width="1.3839999" x="-0.19200002" y="-0.19199999">
+ <feGaussianBlur id="feGaussianBlur6699" inkscape:collect="always" stdDeviation="1.9447689"/>
+ </filter>
+ <radialGradient cx="102" cy="112.3047" gradientUnits="userSpaceOnUse" id="XMLID_8_" r="139.55859">
+ <stop id="stop41" offset="0" style="stop-color:#b7b8b9;stop-opacity:1;"/>
+ <stop id="stop47" offset="0.18851049" style="stop-color:#ECECEC"/>
+ <stop id="stop49" offset="0.25718147" style="stop-color:#FAFAFA"/>
+ <stop id="stop51" offset="0.30111277" style="stop-color:#FFFFFF"/>
+ <stop id="stop53" offset="0.5313" style="stop-color:#FAFAFA"/>
+ <stop id="stop55" offset="0.8449" style="stop-color:#EBECEC"/>
+ <stop id="stop57" offset="1" style="stop-color:#E1E2E3"/>
+ </radialGradient>
+ <linearGradient gradientUnits="userSpaceOnUse" id="XMLID_12_" x1="96" x2="88.000198" y1="104" y2="96.000198">
+ <stop id="stop83" offset="0" style="stop-color:#888A85"/>
+ <stop id="stop85" offset="0.0072" style="stop-color:#8C8E89"/>
+ <stop id="stop87" offset="0.0673" style="stop-color:#ABACA9"/>
+ <stop id="stop89" offset="0.1347" style="stop-color:#C5C6C4"/>
+ <stop id="stop91" offset="0.2652576" style="stop-color:#DBDBDA"/>
+ <stop id="stop93" offset="0.37646064" style="stop-color:#EBEBEB"/>
+ <stop id="stop95" offset="0.48740286" style="stop-color:#F7F7F6"/>
+ <stop id="stop97" offset="0.6324091" style="stop-color:#FDFDFD"/>
+ <stop id="stop99" offset="1" style="stop-color:#FFFFFF"/>
+ </linearGradient>
+ <linearGradient id="linearGradient10207">
+ <stop id="stop10209" offset="0" style="stop-color:#a2a2a2;stop-opacity:1;"/>
+ <stop id="stop10211" offset="1" style="stop-color:#ffffff;stop-opacity:1;"/>
+ </linearGradient>
+ <linearGradient gradientTransform="matrix(1.0172054,0,0,1.5,246.03226,514.75)" gradientUnits="userSpaceOnUse" id="linearGradient3385" inkscape:collect="always" x1="-168.99216" x2="-168.99216" xlink:href="#linearGradient2822" y1="-300.5" y2="-296.48441"/>
+ <linearGradient gradientTransform="matrix(1,0,0,0.7368421,242.00093,284.36842)" gradientUnits="userSpaceOnUse" id="linearGradient3387" inkscape:collect="always" x1="-178" x2="-178" xlink:href="#linearGradient2927" y1="-232.84966" y2="-304.61469"/>
+ </defs>
+ <path d="M 118.983,31 C 118.992,29.35 117.64999,28 115.99999,28 L 40.961007,28 C 40.961007,28 32.061006,20 30.961,20 L 14.999998,20 C 12.799996,20 10.999999,21.8 10.999999,24 L 10.999999,31 C 10.999999,31 11.999999,116 8,116 L 122,116 C 117.99999,116 118.983,31 118.983,31 z " id="path15" style="fill:url(#linearGradient2212)"/>
+ <g id="g17" style="opacity:0.6;filter:url(#filter2807)" transform="matrix(1.0033404,0,0,1,-8.2374684,20)">
+ <path d="M 132,96 C 132,98.2 128.4,100 124,100 L 20,100 C 15.6,100 12,98.2 12,96 C 12,93.8 15.6,92 20,92 L 124,92 C 128.4,92 132,93.8 132,96 z " id="path19"/>
+ </g>
+ <path d="M 10.884862,54 C 10.893892,55.75 10.902922,57.755 10.910952,60 L 119.09511,60 C 119.10414,57.755 119.11317,55.75 119.1212,54 L 10.884862,54 z " id="path50" style="opacity:0.5;fill:url(#linearGradient2204)"/>
+ <path d="M 119.99722,31 C 120.00622,29.35 118.66422,28 117.01422,28 L 42.975222,28 L 36.389222,21.414 C 35.611222,20.636 34.075222,20 32.975222,20 L 12.014222,20 C 9.8142222,20 8.0142222,21.8 8.0142222,24 C 8.0142222,24 7.9822222,54.499299 8.0142222,60.031299 L 12.014222,60.031299 C 12.014222,53.222299 12.014222,24 12.014222,24 L 32.901222,23.997 C 33.083222,24.019 33.470222,24.179 33.560222,24.243 L 41.318222,32 C 41.318222,32 114.02722,32 115.99922,32 C 115.99922,32.435 116.00022,56.400299 116.00222,60.031299 L 120.01422,60.031299 C 120.04522,54.499299 119.99722,31 119.99722,31 z " id="path2896" sodipodi:nodetypes="ccccccccccccccccc" style="fill:#5e95e3;fill-opacity:1"/>
+ <path d="M 124.36598,113.79242 C 124.27969,115.00674 122.85389,116 121.19831,116 L 6.812906,116 C 5.157329,116 3.731522,115.00674 3.644228,113.79242 L 0.007982,62.204632 C -0.112423,60.992526 1.143808,60 2.799384,60 L 125.21183,60 C 126.86741,60 128.11762,60.991789 127.9912,62.203895 L 124.36598,113.79242 z " id="path30" style="opacity:0.9;fill:url(#linearGradient2207);fill-opacity:1"/>
+ <path d="M 125.21293,60 L 2.7999261,60 C 1.1449261,60 -0.11207393,60.992526 0.0079260701,62.204632 L 3.6439261,113.79242 C 3.7309261,115.00674 5.1569261,116 6.8129261,116 L 121.19793,116 C 122.85393,116 124.27993,115.00674 124.36593,113.79242 L 127.99093,62.203895 C 128.11893,60.991789 126.86793,60 125.21293,60 z M 120.41393,113.05263 C 118.87493,113.05263 9.1349261,113.05263 7.5979261,113.05263 C 7.2299261,107.83726 4.5229261,70.627562 4.0659261,64.149246 C 6.5189261,64.149246 121.45793,64.149246 123.93493,64.149246 C 123.81393,65.85872 120.49293,111.92821 120.41393,113.05263 z " id="path2894" sodipodi:nodetypes="cccccccccccccc" style="fill:url(#linearGradient3387)"/>
+ <path d="M 4,64 C 4.0273488,64.775875 4.1802721,68.801119 4.2225137,70 C 7.123925,70 122.78934,70 125.71499,70 C 125.74343,69.191222 125.93026,64.204735 125.9375,64 C 123.41788,64 6.4952049,64 4,64 z " id="path2908" style="fill:url(#linearGradient3385);fill-opacity:1;opacity:0.835"/>
+</svg>
diff --git a/resources/sources/clucker.svg b/resources/sources/clucker.svg
new file mode 100644
index 00000000..0c1727eb
--- /dev/null
+++ b/resources/sources/clucker.svg
@@ -0,0 +1,404 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="clucker.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/chicken128.png"
+ inkscape:export-xdpi="360"
+ inkscape:export-ydpi="360">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4008">
+ <stop
+ id="stop4010"
+ offset="0"
+ style="stop-color:#4a0000;stop-opacity:1;" />
+ <stop
+ id="stop4012"
+ offset="1"
+ style="stop-color:#360000;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3945">
+ <stop
+ id="stop3947"
+ offset="0"
+ style="stop-color:#4e300d;stop-opacity:1;" />
+ <stop
+ id="stop3949"
+ offset="1"
+ style="stop-color:#604000;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3939">
+ <stop
+ id="stop3941"
+ offset="0"
+ style="stop-color:#5b2400;stop-opacity:1;" />
+ <stop
+ id="stop3943"
+ offset="1"
+ style="stop-color:#8d3800;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3838">
+ <stop
+ style="stop-color:#af6c1d;stop-opacity:1;"
+ offset="0"
+ id="stop3840" />
+ <stop
+ style="stop-color:#604000;stop-opacity:1;"
+ offset="1"
+ id="stop3842" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3830">
+ <stop
+ style="stop-color:#ff7f2a;stop-opacity:1;"
+ offset="0"
+ id="stop3832" />
+ <stop
+ style="stop-color:#8d3800;stop-opacity:1"
+ offset="1"
+ id="stop3834" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3820">
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0"
+ id="stop3822" />
+ <stop
+ style="stop-color:#360000;stop-opacity:1"
+ offset="1"
+ id="stop3824" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3816"
+ x="-0.22469135"
+ width="1.4493827"
+ y="-0.16851852"
+ height="1.337037">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.6851852"
+ id="feGaussianBlur3818" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3820"
+ id="linearGradient3828"
+ x1="12.904698"
+ y1="1043.2582"
+ x2="19.445436"
+ y2="1052.4506"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-1,5.2165626e-5)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3820"
+ id="linearGradient3909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.125,-2,911.19195)"
+ x1="12.904698"
+ y1="1043.2582"
+ x2="19.445436"
+ y2="1052.4506" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3820"
+ id="linearGradient3911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-2,1.0000522)"
+ x1="12.904698"
+ y1="1043.2582"
+ x2="19.445436"
+ y2="1052.4506" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4008"
+ id="linearGradient3915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.125,0,0,1,16.5,1.0000522)"
+ x1="12.904698"
+ y1="1043.2582"
+ x2="19.445436"
+ y2="1052.4506" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3820"
+ id="linearGradient4032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-25,-2.9999652)"
+ x1="12.904698"
+ y1="1043.2582"
+ x2="19.445436"
+ y2="1052.4506" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4008"
+ id="linearGradient4034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.125,0,0,1,-6.5,-2.9999652)"
+ x1="12.904698"
+ y1="1043.2582"
+ x2="19.445436"
+ y2="1052.4506" />
+ <filter
+ inkscape:collect="always"
+ id="filter4053"
+ x="-0.12098765"
+ width="1.2419753"
+ y="-0.090740741"
+ height="1.1814815">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.058642"
+ id="feGaussianBlur4055" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3830"
+ id="linearGradient3840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.70710371,1032.3713,730.70724)"
+ x1="13.258252"
+ y1="1033.889"
+ x2="17.500893"
+ y2="1041.7556" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3830"
+ id="linearGradient3846"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-4,3.0000194)"
+ x1="13.258252"
+ y1="1033.889"
+ x2="17.500893"
+ y2="1041.7556" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3838"
+ id="linearGradient3849"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-4,3.0000194)"
+ x1="14.142136"
+ y1="1038.0433"
+ x2="18.119612"
+ y2="1047.1473" />
+ <filter
+ inkscape:collect="always"
+ id="filter3879">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.42040957"
+ id="feGaussianBlur3881" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3850">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.04"
+ id="feGaussianBlur3852" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3854">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.04"
+ id="feGaussianBlur3856" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.313708"
+ inkscape:cx="2.6058272"
+ inkscape:cy="11.408405"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1614"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <path
+ style="opacity:0.87866109;fill:#070707;fill-opacity:1;stroke:none;filter:url(#filter4053)"
+ d="m 10,1023.3622 -3,3 0,20 4,0 0,5 8,0 1,-1 0,-4 5,0 3,-3 0,-20 -18,0 z"
+ id="path4022"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3806"
+ d="m 6.1703915,1025.53 0,4 0,16 5.0000005,0 0,4 8,0 0,-4 5,0 0,-16 0,-4 -18.0000005,0 z"
+ style="opacity:0.75313804;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3816)" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path3861"
+ d="m 23,1025.3622 4,-3 0,20 -3,3 -1,-1 z"
+ style="fill:#6c6c6c;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#e2e2e2;fill-opacity:1;stroke:none"
+ d="m 6,1025.3622 3,-3 18,0 -3,3 0,20 -18,0 z"
+ id="rect3858"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ id="rect3759"
+ width="4"
+ height="4"
+ x="7"
+ y="1029.3622" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none"
+ id="rect3761"
+ width="4"
+ height="4"
+ x="19"
+ y="1029.3622" />
+ <rect
+ style="fill:url(#linearGradient3828);fill-opacity:1;stroke:none"
+ id="rect3769"
+ width="8"
+ height="8"
+ x="11"
+ y="1041.3622" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3854)"
+ id="rect3773"
+ width="1"
+ height="1"
+ x="9"
+ y="1030.3622" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3850)"
+ id="rect3775"
+ width="1"
+ height="1"
+ x="21"
+ y="1030.3622" />
+ <path
+ style="fill:url(#linearGradient3911);fill-opacity:1;stroke:none"
+ d="m 10,1042.3622 8,0 0,8 -8,0 z"
+ id="rect3902"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3907"
+ d="m 11,1041.3622 8,0 -1,1 -8,0 z"
+ style="fill:url(#linearGradient3909);fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3913"
+ d="m 18,1042.3622 1,-1 0,8 -1,1 z"
+ style="fill:url(#linearGradient3915);fill-opacity:1;stroke:none"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ d="m 9,1022.3622 18,0 -3,3 -18,0 z"
+ id="rect4019"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:url(#linearGradient3846);fill-opacity:1;stroke:none"
+ d="m 3.9999995,1036.3622 3.0000005,-3 16,0 0,8 -3,3 -16,0 z"
+ id="rect3815"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="fill:#b34f0e;fill-opacity:1;stroke:none;opacity:1"
+ id="rect3851"
+ width="4.2426286"
+ height="4.0000043"
+ x="28.284193"
+ y="1056.3621"
+ transform="matrix(0.70710873,-0.70710483,0,1,0,0)" />
+ <rect
+ style="fill:#8d5910;fill-opacity:1;stroke:none;opacity:1"
+ id="rect3855"
+ width="4.2426286"
+ height="4"
+ x="28.284189"
+ y="1060.3621"
+ transform="matrix(0.70710883,-0.70710473,0,1,0,0)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3970"
+ d="m 20,1036.3622 c 0,1 0,6 0,7 -3,0 -11.0351165,-0.059 -15.5,0.7502 l 1.230393,1.1835 15.5625,-0.094 2.707107,-2.8397 0,-4 0,-4 -1,-1 z"
+ style="opacity:0.63598326;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3879)"
+ sodipodi:nodetypes="cccccccccc" />
+ <rect
+ y="1040.3622"
+ x="3.9999995"
+ height="4"
+ width="16"
+ id="rect3917"
+ style="fill:url(#linearGradient3849);fill-opacity:1;stroke:none" />
+ <rect
+ y="1036.3622"
+ x="3.9999995"
+ height="4"
+ width="16"
+ id="rect3923"
+ style="fill:url(#linearGradient3846);fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:url(#linearGradient3840);fill-opacity:1;stroke:none"
+ id="rect3931"
+ width="16.000004"
+ height="4.242579"
+ x="1040.3712"
+ y="1461.4012"
+ transform="matrix(1,0,-0.70710986,0.7071037,0,0)" />
+ </g>
+</svg>
diff --git a/resources/sources/creeper.svg b/resources/sources/creeper.svg
new file mode 100644
index 00000000..2a2e39b6
--- /dev/null
+++ b/resources/sources/creeper.svg
@@ -0,0 +1,775 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="creeper.svg">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3917">
+ <stop
+ id="stop3919"
+ offset="0"
+ style="stop-color:#115008;stop-opacity:1;" />
+ <stop
+ style="stop-color:#39c228;stop-opacity:1;"
+ offset="0.49999952"
+ id="stop3921" />
+ <stop
+ id="stop3923"
+ offset="1"
+ style="stop-color:#c7f7c2;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3892">
+ <stop
+ style="stop-color:#1a1a1a;stop-opacity:1;"
+ offset="0"
+ id="stop3894" />
+ <stop
+ style="stop-color:#1a1a1a;stop-opacity:0;"
+ offset="1"
+ id="stop3896" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3871">
+ <stop
+ style="stop-color:#115008;stop-opacity:1;"
+ offset="0"
+ id="stop3873" />
+ <stop
+ id="stop3900"
+ offset="0.5"
+ style="stop-color:#39c228;stop-opacity:1;" />
+ <stop
+ style="stop-color:#c7f7c2;stop-opacity:1;"
+ offset="1"
+ id="stop3875" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3867">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.792"
+ id="feGaussianBlur3869" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3892"
+ id="linearGradient3898"
+ x1="16"
+ y1="31"
+ x2="16"
+ y2="26"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3871"
+ id="linearGradient5605"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,1020.3622)"
+ x1="27"
+ y1="26"
+ x2="5"
+ y2="3" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3871-3"
+ id="linearGradient5605-2"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,1020.3622)"
+ x1="27"
+ y1="26"
+ x2="5"
+ y2="3" />
+ <linearGradient
+ id="linearGradient3871-3">
+ <stop
+ style="stop-color:#115008;stop-opacity:1;"
+ offset="0"
+ id="stop3873-5" />
+ <stop
+ id="stop3900-7"
+ offset="0.5"
+ style="stop-color:#39c228;stop-opacity:1;" />
+ <stop
+ style="stop-color:#c7f7c2;stop-opacity:1;"
+ offset="1"
+ id="stop3875-9" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3917"
+ id="linearGradient3147"
+ x1="31"
+ y1="1052.3622"
+ x2="4"
+ y2="1024.3622"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="reflect" />
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4028"
+ x="-0.14708571"
+ width="1.2941715"
+ y="-0.25740001"
+ height="1.5148">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.429"
+ id="feGaussianBlur4030" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4056">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.676"
+ id="feGaussianBlur4058" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter5711"
+ x="-0.1728"
+ width="1.3456"
+ y="-0.3456"
+ height="1.6912">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.144"
+ id="feGaussianBlur5713" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter5719"
+ x="-0.18"
+ width="1.36"
+ y="-0.36"
+ height="1.72">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.15"
+ id="feGaussianBlur5721" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter5723"
+ x="-0.21"
+ width="1.42"
+ y="-0.28"
+ height="1.56">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.35"
+ id="feGaussianBlur5725" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter5727"
+ x="-0.21"
+ width="1.42"
+ y="-0.28"
+ height="1.56">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.35"
+ id="feGaussianBlur5729" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4"
+ inkscape:cx="57.618613"
+ inkscape:cy="29.528242"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <rect
+ style="fill:#2b1100;fill-opacity:1;stroke:none"
+ id="rect3063"
+ width="22"
+ height="22"
+ x="5"
+ y="5"
+ transform="translate(0,1020.3622)"
+ rx="5"
+ ry="5" />
+ <path
+ id="path3065"
+ d="m 6,1026.3622 0,22 22,0 0,-22 z m 2,2 6,0 0,6 6,0 0,-6 6,0 0,6 -6,0 0,3 3,0 0,9 -3,0 0,-3 -6,0 0,3 -3,0 0,-9 3,0 0,-3 -6,0 z"
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3867)"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccc" />
+ <path
+ style="fill:url(#linearGradient5605);fill-opacity:1;stroke:none"
+ d="m 7,1022.3622 c -2.77,0 -5,2.23 -5,5 l 0,25 28,0 0,-25 c 0,-2.77 -2.23,-5 -5,-5 z m 0,5 6,0 0,6 6,0 0,-6 6,0 0,6 -6,0 0,3 3,0 0,9 -3,0 0,-3 -6,0 0,3 -3,0 0,-9 3,0 0,-3 -6,0 z"
+ id="rect3045"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccsssccccccccccccccccccccc" />
+ <g
+ id="g5466"
+ style="opacity:0.3"
+ transform="translate(0,-1)">
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3902"
+ d="m 3.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3904"
+ d="m 6.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3906"
+ d="m 3.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3908"
+ d="m 0.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3914"
+ d="m 3.999974,1028.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3916"
+ d="m 0.999974,1028.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3918"
+ d="m 6.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3924"
+ d="m 9.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3926"
+ d="m 12.999974,1028.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3930"
+ d="m 12.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3932"
+ d="m 9.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3936"
+ d="m 12.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3999"
+ d="m 18.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4007"
+ d="m 15.99998,1031.3622 0,-3 3,0 0,3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4009"
+ d="m 24.99998,1031.3622 0,-3 3,0 0,3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4011"
+ d="m 24.99998,1034.3622 0,-3 3,0 0,3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4013"
+ d="m 21.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4015"
+ d="m 24.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4017"
+ d="m 15.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4019"
+ d="m 21.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4023"
+ d="m 24.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4027"
+ d="m 18.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4029"
+ d="m 15.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4035"
+ d="m 15.99998,1034.3622 0,-3 3,0 0,3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4039"
+ d="m 6.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4041"
+ d="m 6.99998,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4043"
+ d="m 9.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4049"
+ d="m 3.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4053"
+ d="m 12.99998,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4059"
+ d="m 3.99998,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4063"
+ d="m 12.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4069"
+ d="m 6.99998,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4071"
+ d="m 3.99998,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4073"
+ d="m 6.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4075"
+ d="m 3.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4077"
+ d="m 3.99998,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4133"
+ d="m 28,1028.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4143"
+ d="m 28,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4155"
+ d="m 28,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4159"
+ d="m 28,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4161"
+ d="m 28,1025.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4175"
+ d="m 28,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4185"
+ d="m 28,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4197"
+ d="m 28,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4203"
+ d="m 28,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4207"
+ d="m 6.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4211"
+ d="m 9.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4213"
+ d="m 9.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4215"
+ d="m 6.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4217"
+ d="m 3.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4219"
+ d="m 12.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4221"
+ d="m 12.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4245"
+ d="m 3.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4261"
+ d="m 0.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4263"
+ d="m 0.99998,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4267"
+ d="m 0.99998,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4273"
+ d="m 0.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4275"
+ d="m 0.99998,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4303"
+ d="m 0.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4305"
+ d="m 0.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4093"
+ d="m 12.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4099"
+ d="m 15.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4105"
+ d="m 21.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4109"
+ d="m 21.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4113"
+ d="m 18.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4115"
+ d="m 21.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4117"
+ d="m 21.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4119"
+ d="m 9.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4377"
+ d="m 21.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4379"
+ d="m 18.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4381"
+ d="m 15.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4383"
+ d="m 15.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4387"
+ d="m 18.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4389"
+ d="m 15.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4391"
+ d="m 21.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4393"
+ d="m 21.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4395"
+ d="m 21.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4397"
+ d="m 24.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4399"
+ d="m 27.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4401"
+ d="m 24.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4403"
+ d="m 27.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4405"
+ d="m 24.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4407"
+ d="m 24.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4409"
+ d="m 27.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4411"
+ d="m 27.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4417"
+ d="m 18.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4419"
+ d="m 21.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4433"
+ d="m 21.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4435"
+ d="m 21.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4439"
+ d="m 24.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4441"
+ d="m 27.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4443"
+ d="m 24.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4445"
+ d="m 27.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4447"
+ d="m 24.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4449"
+ d="m 24.999974,1031.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4451"
+ d="m 27.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4453"
+ d="m 27.999974,1031.3622 3,0 0,3 -3,0 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4455"
+ d="m 15.999974,1031.3622 3,0 0,3 -3,0 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ </g>
+ <path
+ style="fill:#000000;stroke:none"
+ d="m 7,1020.3622 c -3.878,0 -7,3.122 -7,7 l 0,25 2,0 0,-25 c 0,-2.77 2.23,-5 5,-5 l 18,0 c 2.77,0 5,2.23 5,5 l 0,25 2,0 0,-25 c 0,-3.878 -3.122,-7 -7,-7 z"
+ id="rect3887"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccssssccsss" />
+ <path
+ style="opacity:0.34999999999999998;fill:url(#linearGradient3147);fill-opacity:1;stroke:none"
+ d="m 7,1021.3622 c -3.324,0 -6,2.676 -6,6 l 0,25 1,0 0,-25 c 0,-2.77 2.23,-5 5,-5 l 18,0 c 2.77,0 5,2.23 5,5 l 0,25 1,0 0,-25 c 0,-3.324 -2.676,-6 -6,-6 l -18,0 z"
+ id="rect3098"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="fill:url(#linearGradient3898);fill-opacity:1;stroke:none"
+ id="rect3890"
+ width="32"
+ height="6"
+ x="0"
+ y="26"
+ transform="translate(0,1020.3622)" />
+ </g>
+</svg>
diff --git a/resources/sources/enderpearl.svg b/resources/sources/enderpearl.svg
new file mode 100644
index 00000000..ac9378f5
--- /dev/null
+++ b/resources/sources/enderpearl.svg
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="enderpearl.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/enderpearl.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4030">
+ <stop
+ id="stop4032"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4034"
+ offset="1"
+ style="stop-color:#0c493f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3884">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.505"
+ id="feGaussianBlur3886" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3898"
+ x="-0.20110182"
+ width="1.4022036"
+ y="-0.20210065"
+ height="1.4042013">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0604979"
+ id="feGaussianBlur3900" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3916"
+ x="-0.11491533"
+ width="1.2298307"
+ y="-0.11548609"
+ height="1.2309722">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6059988"
+ id="feGaussianBlur3918" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3992">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.44"
+ id="feGaussianBlur3994" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4024"
+ gradientUnits="userSpaceOnUse"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)" />
+ <filter
+ inkscape:collect="always"
+ id="filter4082"
+ x="-0.13465964"
+ width="1.2693193"
+ y="-0.12490681"
+ height="1.2498136">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.763901"
+ id="feGaussianBlur4084" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4110"
+ x="-0.17716113"
+ width="1.3543223"
+ y="-0.17804105"
+ height="1.3560821">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.93424815"
+ id="feGaussianBlur4112" />
+ </filter>
+ <linearGradient
+ id="linearGradient3954-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-4" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-2" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="-241.15414"
+ inkscape:cy="194.64078"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="11.25,96.5"
+ id="guide4252" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#032620;fill-opacity:1;stroke:none;filter:url(#filter3992)"
+ id="path3005"
+ sodipodi:cx="32"
+ sodipodi:cy="32"
+ sodipodi:rx="24"
+ sodipodi:ry="24"
+ d="M 56,32 A 24,24 0 1 1 8,32 24,24 0 1 1 56,32 z"
+ transform="matrix(1.1666667,0,0,1.1666667,-5.3333344,983.02885)" />
+ <g
+ id="g4116"
+ transform="matrix(1.0195929,0,0,1.0195936,-0.15674338,-19.992546)">
+ <path
+ transform="matrix(1.1769403,0,0,1.1769403,-6.1232836,982.70009)"
+ d="M 52,32 C 52,43.045695 43.045695,52 32,52 20.954305,52 12,43.045695 12,32 12,20.954305 20.954305,12 32,12 c 11.045695,0 20,8.954305 20,20 z"
+ sodipodi:ry="20"
+ sodipodi:rx="20"
+ sodipodi:cy="32"
+ sodipodi:cx="32"
+ id="path3014"
+ style="fill:url(#radialGradient4024);fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3866"
+ transform="matrix(1.1769403,0,0,1.1769403,-5.6620909,982.68843)"
+ d="m 28,16 c -6.627417,0 -12,5.372583 -12,12 0,0.20181 0.0214,0.394383 0.03125,0.59375 C 20,20 22,22 28.65625,16.03125 28.436252,16.019242 28.222996,16 28,16 z"
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3884)" />
+ <path
+ transform="matrix(1.487885,0,0,1.487885,-10.597031,-493.22036)"
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3898)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3888"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3902"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ transform="matrix(-1.8359746,0,0,-1.8359746,81.432966,2885.3572)" />
+ <path
+ transform="matrix(-1.1914115,0,0,-1.1914115,69.087304,2235.4229)"
+ style="fill:#d7f4d7;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3920"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="csccsc"
+ inkscape:connector-curvature="0"
+ id="path3964"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 5.570868,-4.4816 8.905966,-4.4575 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4110)"
+ transform="matrix(0.58516297,0,0,0.58516297,6.7391969,416.10681)" />
+ <path
+ transform="translate(0,988.36218)"
+ sodipodi:nodetypes="ccccccccccccccccccssssscsscsscccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path4036"
+ d="M 30,29 30.450431,19.407328 32.447263,28.5316 35,27 l 1,-6 -1,-4 1,-2 0,3 1.125,3.375 -0.07418,2.744617 L 44.0828,21.873859 37,26 l -1,4 2,2 3.375,0 5.0625,-3.0625 -4.3125,4.375 L 36,34 c 0,0 2.686048,7.256632 1.846439,8.096462 -0.326542,0.326628 -1.289256,-3.475594 -2.437243,-4.77335 C 34.192758,35.947974 32.79132,37.0625 32.5625,37.0625 c -0.602669,0 -2.136064,2.519769 -3.25945,4.522134 -0.205342,0.36601 0.728015,9.714733 0.308259,6.715119 C 29.482708,47.380759 28,44 28,44 c 0,0 0,-2 0,-3 0,-1 2,-3 2,-4 0,-1 0,-3 0,-3 0,0 -4.25,-1.125 -5.25,-1.125 -0.47674,0 -1.893857,0.89205 -3.377184,1.040709 C 19.744745,34.078874 18.0625,33.5 18.0625,33.5 L 16.774038,34.754808 17,39 l -2,-4 2,-3 3.8125,-0.0625 1.170423,-6.693425 3.092068,-2.055016 -2.122126,3.038537 -0.913529,5.350612 L 28,31 z"
+ style="opacity:0.86178864;fill:#000000;fill-opacity:0.86666667;stroke:none;filter:url(#filter4082)" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/ftb-glow.svg b/resources/sources/ftb-glow.svg
new file mode 100644
index 00000000..be78c78e
--- /dev/null
+++ b/resources/sources/ftb-glow.svg
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32px"
+ height="32px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="ftb-glow.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/ftb_glow128.png"
+ inkscape:export-xdpi="360"
+ inkscape:export-ydpi="360">
+ <defs
+ id="defs2987">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4462">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop4464" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop4466" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3981">
+ <stop
+ style="stop-color:#000083;stop-opacity:1;"
+ offset="0"
+ id="stop3983" />
+ <stop
+ id="stop3985"
+ offset="0.25"
+ style="stop-color:#1919ff;stop-opacity:1;" />
+ <stop
+ id="stop3987"
+ offset="0.5"
+ style="stop-color:#1919ff;stop-opacity:1;" />
+ <stop
+ style="stop-color:#1919ff;stop-opacity:1;"
+ offset="0.75"
+ id="stop3989" />
+ <stop
+ style="stop-color:#00007d;stop-opacity:1;"
+ offset="1"
+ id="stop3991" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3941">
+ <stop
+ id="stop3943"
+ offset="0"
+ style="stop-color:#00830e;stop-opacity:1;" />
+ <stop
+ style="stop-color:#00c017;stop-opacity:1;"
+ offset="0.25"
+ id="stop3945" />
+ <stop
+ style="stop-color:#00ff1e;stop-opacity:1;"
+ offset="0.5"
+ id="stop3947" />
+ <stop
+ id="stop3949"
+ offset="0.75"
+ style="stop-color:#00ff1e;stop-opacity:1;" />
+ <stop
+ id="stop3951"
+ offset="1"
+ style="stop-color:#007d0f;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3801">
+ <stop
+ style="stop-color:#830000;stop-opacity:1"
+ offset="0"
+ id="stop3803" />
+ <stop
+ id="stop3811"
+ offset="0.25"
+ style="stop-color:#ff0000;stop-opacity:1;" />
+ <stop
+ id="stop3809"
+ offset="0.5"
+ style="stop-color:#ff0000;stop-opacity:1;" />
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0.75"
+ id="stop3813" />
+ <stop
+ style="stop-color:#7d0000;stop-opacity:1"
+ offset="1"
+ id="stop3805" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter4107"
+ x="-0.14444444"
+ width="1.2888889"
+ y="-0.12380952"
+ height="1.247619">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.4444444"
+ id="feGaussianBlur4109" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4452">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.85596708"
+ id="feGaussianBlur4454" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4462"
+ id="linearGradient4468"
+ x1="-34.75"
+ y1="14"
+ x2="72.5"
+ y2="18.5"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(4.228145,0,0,1,111.61491,355.31802)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3801"
+ id="linearGradient4476"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="2"
+ x2="-4"
+ y2="6" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3941"
+ id="linearGradient4478"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="2"
+ x2="-4"
+ y2="6" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3981"
+ id="linearGradient4480"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="2"
+ x2="-4"
+ y2="6" />
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4107-6"
+ x="-0.14444444"
+ width="1.2888889"
+ y="-0.12380952"
+ height="1.247619">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.4444444"
+ id="feGaussianBlur4109-1" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3941"
+ id="linearGradient3336"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="6"
+ x2="4"
+ y2="17" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3981"
+ id="linearGradient3338"
+ gradientUnits="userSpaceOnUse"
+ x1="-16"
+ y1="2"
+ x2="-4"
+ y2="14" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3801"
+ id="linearGradient3340"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="6"
+ x2="4"
+ y2="18" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3801"
+ id="linearGradient3342"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="2"
+ x2="-4"
+ y2="6" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3801"
+ id="linearGradient3344"
+ gradientUnits="userSpaceOnUse"
+ x1="-8"
+ y1="2"
+ x2="-4"
+ y2="6" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6568542"
+ inkscape:cx="6.8684247"
+ inkscape:cy="24.209238"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2993"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="4,48"
+ id="guide3346" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="-29,35"
+ id="guide3348" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="-62,42"
+ id="guide3350" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="-58,69"
+ id="guide3352" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <rect
+ style="fill:url(#linearGradient4468);fill-opacity:1;stroke:none"
+ id="rect4460"
+ width="495.75"
+ height="96.25"
+ x="-76.537521"
+ y="319.06802" />
+ <g
+ id="g3280"
+ transform="translate(2,53)"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/vectors/ftb.png"
+ inkscape:export-xdpi="360"
+ inkscape:export-ydpi="360">
+ <g
+ transform="translate(-2,-53)"
+ id="g8144"
+ style="filter:url(#filter4107)">
+ <path
+ style="fill:#5353ff;fill-opacity:1;stroke:none"
+ d="m 4,2 12,0 0,4 -8,0 0,4 4,0 0,4 -4,0 0,8 -4,0 z"
+ id="path8146"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ style="fill:#009a00;fill-opacity:1;stroke:none"
+ d="m 8,6 12,0 0,4 -4,0 0,16 -4,0 0,-16 -4,0 z"
+ id="path8148"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ style="fill:#ac0000;fill-opacity:1;stroke:none"
+ d="m 16,10 0,20 8,0 0,-4 -4,0 0,-4 4,0 0,-4 -4,0 0,-4 4,0 0,-4 -8,0 z m 8,4 0,4 4,0 0,-4 -4,0 z m 0,8 0,4 4,0 0,-4 -4,0 z"
+ id="path8150"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g8256"
+ transform="translate(32,-53)">
+ <g
+ id="g8258"
+ transform="translate(-18,4)">
+ <path
+ sodipodi:nodetypes="ccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path8260"
+ d="m -8,2 4,0 8,0 0,4 -4,0 0,16 -4,0 0,-16 0,0 -4,0 z"
+ style="fill:url(#linearGradient3336);fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.50196078;stroke:none"
+ d="m -8,2 1,1 0,2 -1,1 z"
+ id="path8262"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8264"
+ d="M 4,2 3,3 -7,3 -8,2 z"
+ style="fill:#ffffff;fill-opacity:0.50196078;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8266"
+ d="m -4,22 1,-1 2,0 1,1 z"
+ style="fill:#000000;fill-opacity:0.3466667;stroke:none" />
+ <path
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none"
+ d="M 4,6 3,5 3,3 4,2 z"
+ id="path8268"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#000000;fill-opacity:0.3466667;stroke:none"
+ d="m -8,6 1,-1 4,0 -1,1 z"
+ id="path8270"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8272"
+ d="M 0,6 -1,5 3,5 4,6 z"
+ style="fill:#000000;fill-opacity:0.3466667;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8274"
+ d="m -4,6 1,-1 0,16 -1,1 z"
+ style="fill:#ffffff;fill-opacity:0.50196078;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8276"
+ d="M 0,22 -1,21 -1,5 0,6 z"
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none" />
+ </g>
+ <g
+ transform="translate(-14,0)"
+ id="g8278">
+ <path
+ sodipodi:nodetypes="ccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path8280"
+ d="m -16,2 12,0 0,4 -8,0 0,4 4,0 0,4 -4,0 0,8 -4,0 z"
+ style="fill:url(#linearGradient3338);fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8282"
+ d="m -16,2 1,1 0,18 -1,1 z"
+ style="fill:#ffffff;fill-opacity:0.50222222;stroke:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.40888887;stroke:none"
+ d="M -4,2 -5,3 -15,3 -16,2 z"
+ id="path8284"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.3466667;stroke:none"
+ d="m -16,22 1,-1 2,0 1,1 z"
+ id="path8286"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8288"
+ d="M -4,6 -5,5 -5,3 -4,2 z"
+ style="fill:#ffffff;fill-opacity:0.25777779;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8290"
+ d="m -12,14 -1,-1 4,0 1,1 z"
+ style="fill:#ffffff;fill-opacity:0.3466667;stroke:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.3466667;stroke:none"
+ d="m -12,6 -1,-1 8,0 1,1 z"
+ id="path8292"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.25777779;stroke:none"
+ d="m -8,14 -1,-1 0,-2 1,-1 z"
+ id="path8294"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8296"
+ d="m -8,10 -1,1 -4,0 1,-1 z"
+ style="fill:#ffffff;fill-opacity:0.40888887;stroke:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.25777779;stroke:none"
+ d="m -12,10 -1,1 0,-6 1,1 z"
+ id="path8298"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8300"
+ d="m -12,22 -1,-1 0,-8 1,1 z"
+ style="fill:#ffffff;fill-opacity:0.25777779;stroke:none" />
+ </g>
+ <g
+ id="g8302">
+ <g
+ id="g8304"
+ transform="translate(-10,8)">
+ <path
+ style="fill:url(#linearGradient3340);fill-opacity:1;stroke:none"
+ d="m -8,2 8,0 0,4 -4,0 0,4 4,0 0,4 -4,0 0,4 4,0 0,4 -8,0 z"
+ id="path8306"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccc" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.39111113;stroke:none"
+ d="m -8,2 1,1 0,18 -1,1 z"
+ id="path8308"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8310"
+ d="M 0,2 -1,3 -7,3 -8,2 z"
+ style="fill:#ffffff;fill-opacity:0.23999999;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8312"
+ d="m -8,22 1,-1 6,0 1,1 z"
+ style="fill:#000000;fill-opacity:0.43111112;stroke:none" />
+ <path
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none"
+ d="M 0,6 -1,5 -1,3 0,2 z"
+ id="path8314"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#000000;fill-opacity:0.43111112;stroke:none"
+ d="M -4,6 -5,5 -1,5 0,6 z"
+ id="path8316"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8318"
+ d="m -4,14 -1,-1 4,0 1,1 z"
+ style="fill:#000000;fill-opacity:0.43111112;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8320"
+ d="m -4,10 -1,1 0,-6 1,1 z"
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none" />
+ <path
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none"
+ d="m -4,18 -1,1 0,-6 1,1 z"
+ id="path8322"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8324"
+ d="m 0,22 -1,-1 0,-2 1,-1 z"
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.23999999;stroke:none"
+ d="m 0,10 -1,1 -4,0 1,-1 z"
+ id="path8326"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8328"
+ d="m 0,18 -1,1 -4,0 1,-1 z"
+ style="fill:#ffffff;fill-opacity:0.23999999;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8330"
+ d="m 0,14 -1,-1 0,-2 1,-1 z"
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none" />
+ </g>
+ <g
+ transform="translate(-2,12)"
+ id="g8332">
+ <rect
+ style="fill:url(#linearGradient3342);fill-opacity:1;stroke:none"
+ id="rect8334"
+ width="4"
+ height="4"
+ x="-8"
+ y="2" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.39111113;stroke:none"
+ d="m -8,2 1,1 0,2 -1,1 z"
+ id="path8336"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8338"
+ d="M -4,2 -5,3 -7,3 -8,2 z"
+ style="fill:#ffffff;fill-opacity:0.23999999;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8340"
+ d="m -8,6 1,-1 2,0 1,1 z"
+ style="fill:#000000;fill-opacity:0.43111112;stroke:none" />
+ <path
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none"
+ d="M -4,6 -5,5 -5,3 -4,2 z"
+ id="path8342"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ <g
+ id="g8344"
+ transform="translate(-2,20)">
+ <rect
+ y="2"
+ x="-8"
+ height="4"
+ width="4"
+ id="rect8346"
+ style="fill:url(#linearGradient3344);fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8348"
+ d="m -8,2 1,1 0,2 -1,1 z"
+ style="fill:#ffffff;fill-opacity:0.39111113;stroke:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:0.23999999;stroke:none"
+ d="M -4,2 -5,3 -7,3 -8,2 z"
+ id="path8350"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#000000;fill-opacity:0.43111112;stroke:none"
+ d="m -8,6 1,-1 2,0 1,1 z"
+ id="path8352"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path8354"
+ d="M -4,6 -5,5 -5,3 -4,2 z"
+ style="fill:#000000;fill-opacity:0.25777779;stroke:none" />
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/ftb-logo.svg b/resources/sources/ftb-logo.svg
new file mode 100644
index 00000000..8cf73567
--- /dev/null
+++ b/resources/sources/ftb-logo.svg
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ width="128"
+ height="128"
+ sodipodi:docname="ftb-logo.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/ftb_logo128.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3027">
+ <stop
+ style="stop-color:#418cc6;stop-opacity:1"
+ offset="0"
+ id="stop3029" />
+ <stop
+ style="stop-color:#014b85;stop-opacity:1"
+ offset="1"
+ id="stop3031" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3019">
+ <stop
+ style="stop-color:#41cd55;stop-opacity:1"
+ offset="0"
+ id="stop3021" />
+ <stop
+ style="stop-color:#018d15;stop-opacity:1"
+ offset="1"
+ id="stop3023" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3011">
+ <stop
+ style="stop-color:#d24141;stop-opacity:1"
+ offset="0"
+ id="stop3013" />
+ <stop
+ style="stop-color:#910101;stop-opacity:1"
+ offset="1"
+ id="stop3015" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3826"
+ color-interpolation-filters="sRGB">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="3.4237769"
+ id="feGaussianBlur3828" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3019"
+ id="linearGradient3830"
+ gradientUnits="userSpaceOnUse"
+ x1="567.10803"
+ y1="231.67372"
+ x2="567.10803"
+ y2="300.68848" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3027"
+ id="linearGradient3832"
+ gradientUnits="userSpaceOnUse"
+ x1="548.19916"
+ y1="255.50847"
+ x2="548.19916"
+ y2="324.71759" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3011"
+ id="linearGradient3834"
+ gradientUnits="userSpaceOnUse"
+ x1="600.95337"
+ y1="257.41525"
+ x2="600.95337"
+ y2="324.16464" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3019"
+ id="linearGradient3848"
+ gradientUnits="userSpaceOnUse"
+ x1="567.10803"
+ y1="231.67372"
+ x2="567.10803"
+ y2="300.68848" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3027"
+ id="linearGradient3850"
+ gradientUnits="userSpaceOnUse"
+ x1="548.19916"
+ y1="255.50847"
+ x2="548.19916"
+ y2="324.71759" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3011"
+ id="linearGradient3852"
+ gradientUnits="userSpaceOnUse"
+ x1="600.95337"
+ y1="257.41525"
+ x2="600.95337"
+ y2="324.16464" />
+ </defs>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ id="namedview4"
+ showgrid="false"
+ showguides="false"
+ inkscape:guide-bbox="true"
+ inkscape:zoom="4"
+ inkscape:cx="32.725294"
+ inkscape:cy="54.630313"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g3838">
+ <sodipodi:guide
+ orientation="1,0"
+ position="352.46726,316.96211"
+ id="guide2991" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="347.41115,278.08622"
+ id="guide2993" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="431.31454,337.77645"
+ id="guide3001" />
+ <inkscape:grid
+ type="xygrid"
+ id="grid3005"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="431.28645,312"
+ id="guide3007" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="73.25,82.125"
+ id="guide3856" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="75.5,79.25"
+ id="guide3858" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="35.125,49.75"
+ id="guide3860" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="39.686368,60.45763"
+ id="guide3862" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="41.9375,48.59375"
+ id="guide3864" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="28.416854,59.142853"
+ id="guide3866" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="28.416854,56.623785"
+ id="guide3868" />
+ </sodipodi:namedview>
+ <g
+ id="g3838"
+ transform="matrix(1,0,0,1.018245,3.8570626,-2.6546202)">
+ <g
+ style="opacity:0.33054397;fill:#000000;fill-opacity:1;filter:url(#filter3826)"
+ transform="matrix(0.83116569,0,0,0.83116569,-409.72999,-161.96543)"
+ id="g3040">
+ <path
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path3042"
+ d="m 547.55325,231.83317 c -0.0986,2.38736 42.68285,2.06721 42.92078,0 l 3.59546,21.34804 -2.92131,0 c -3.03427,-7.13555 -5.22493,-14.772 -17.52786,-16.40428 l 0,59.32506 5.6179,1.34829 0,3.37075 -20.67388,0 0,-3.14603 5.73213,-1.53592 0,-59.58687 c -6.03051,0.84659 -12.06101,1.92591 -18.09152,15.95485 l -2.69659,0 z"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="cccccccccccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path3044"
+ d="m 505.54497,324.79984 20.44916,0 0,-3.14603 -5.73026,-1.0674 0,-28.9322 9.10101,-0.11236 c 3.72116,2.12145 5.47848,6.20675 6.51676,11.01109 l 2.6966,-0.22472 0,-24.26934 -3.14603,0 c -1.29037,3.73961 -0.27178,8.24887 -6.51677,10.33694 l -8.65157,-0.11236 0,-27.13447 c 15.85546,-0.55543 21.31445,0.2909 29.77489,16.46046 l 2.92131,0 -4.71904,-22.02218 c -0.99909,3.14752 -12.86953,2.24716 -42.69606,2.24716 l 0.0281,2.72468 c 3.6912,0.29962 3.59931,0.57116 5.14038,0.87078 l -0.0562,59.2127 -5.05612,1.51683 z"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3046"
+ d="m 612.29661,257.60381 -27.75,0.125 0,3.125 5.15625,0.90625 0,58.875 -5.40625,1.46875 0,2.46875 27.65625,0 c 32.83223,-0.42026 25.38759,-33.26589 5.15625,-36.40625 19.87788,-2.96053 19.96965,-30.36986 -4.8125,-30.5625 z m -2.375,3.34375 c 16.40273,0 18.2699,25.53125 -1.6875,25.53125 l -9.21875,0 0,-25.5 10.90625,-0.0312 z m -1.1875,28.84375 c 10.10368,0.81978 14.43973,6.63609 15.71875,15.6875 2.50148,17.70261 -22.21516,16.36814 -25.375,15.15625 l 0,-30.78125 9.65625,-0.0625 z"
+ style="fill:#000000;fill-opacity:1;stroke:none" />
+ </g>
+ <g
+ transform="matrix(0.83116569,0,0,0.83116569,-412.777,-169.25441)"
+ id="g3035">
+ <path
+ style="fill:url(#linearGradient3848);fill-opacity:1;stroke:none"
+ d="m 547.55325,231.83317 c -0.0986,2.38736 42.68285,2.06721 42.92078,0 l 3.59546,21.34804 -2.92131,0 c -3.03427,-7.13555 -5.22493,-14.772 -17.52786,-16.40428 l 0,59.32506 5.6179,1.34829 0,3.37075 -20.67388,0 0,-3.14603 5.73213,-1.53592 0,-59.58687 c -6.03051,0.84659 -12.06101,1.92591 -18.09152,15.95485 l -2.69659,0 z"
+ id="path2987"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccc" />
+ <path
+ style="fill:url(#linearGradient3850);fill-opacity:1;stroke:none"
+ d="m 505.54497,324.79984 20.44916,0 0,-3.14603 -5.73026,-1.0674 0,-28.9322 9.0706,-0.0112 c 7.23229,-0.009 6.17144,5.6893 6.17144,10.66981 l 3.07233,0 0,-24.25397 -3.14603,0 c 0.0737,4.503 1.04842,10.24675 -6.09774,10.23698 l -9.0706,-0.0124 0,-27.13447 c 15.85546,-0.55543 21.31445,0.2909 29.77489,16.46046 l 2.92131,0 -4.71904,-22.02218 c -0.99909,3.14752 -12.86953,2.24716 -42.69606,2.24716 l 0.0281,2.72468 c 3.6912,0.29962 3.59931,0.57116 5.14038,0.87078 l -0.0562,59.2127 -5.05612,1.51683 z"
+ id="path2989"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccsccccsccccccccccc" />
+ <path
+ style="fill:url(#linearGradient3852);fill-opacity:1;stroke:none"
+ d="m 612.29661,257.60381 -27.75,0.125 0,3.125 5.15625,0.90625 0,58.875 -5.40625,1.46875 0,2.46875 27.65625,0 c 32.83223,-0.42026 25.38759,-33.26589 5.15625,-36.40625 19.87788,-2.96053 19.96965,-30.36986 -4.8125,-30.5625 z m -2.375,3.34375 c 16.40273,0 18.2699,25.53125 -1.6875,25.53125 l -9.21875,0 0,-25.5 10.90625,-0.0312 z m -1.1875,28.84375 c 10.10368,0.81978 14.43973,6.63609 15.71875,15.6875 2.50148,17.70261 -22.21516,16.36814 -25.375,15.15625 l 0,-30.78125 9.65625,-0.0625 z"
+ id="path2995"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/gear.svg b/resources/sources/gear.svg
new file mode 100644
index 00000000..c0169aec
--- /dev/null
+++ b/resources/sources/gear.svg
@@ -0,0 +1,434 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="gear.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/gear128.png"
+ inkscape:export-xdpi="180"
+ inkscape:export-ydpi="180">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4030">
+ <stop
+ id="stop4032"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4034"
+ offset="1"
+ style="stop-color:#0c493f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-4" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-2" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient3069"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient3917"
+ x1="43.78125"
+ y1="42.687496"
+ x2="15.453125"
+ y2="21.578121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1428571,0,0,1.1428571,-2.2857143,-2.28571)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3911-5"
+ id="linearGradient3929-9"
+ gradientUnits="userSpaceOnUse"
+ x1="40.5"
+ y1="36.999996"
+ x2="9"
+ y2="19.499996" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3911-5">
+ <stop
+ style="stop-color:#8c3800;stop-opacity:1"
+ offset="0"
+ id="stop3913-6" />
+ <stop
+ style="stop-color:#ff6701;stop-opacity:1"
+ offset="1"
+ id="stop3915-3" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient3986"
+ gradientUnits="userSpaceOnUse"
+ x1="28"
+ y1="28"
+ x2="44"
+ y2="32" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient3925-4"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3919-9">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop3921-2" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop3923-3" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-12,-12)"
+ y2="48"
+ x2="48"
+ y1="16"
+ x1="16"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4025"
+ xlink:href="#linearGradient3919-9"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3919-9-3">
+ <stop
+ style="stop-color:#ff9e06;stop-opacity:1"
+ offset="0"
+ id="stop3921-2-9" />
+ <stop
+ style="stop-color:#af4600;stop-opacity:1"
+ offset="1"
+ id="stop3923-3-4" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-8,-8)"
+ y2="48"
+ x2="48"
+ y1="16"
+ x1="16"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4065"
+ xlink:href="#linearGradient3919-9-3"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3919-9-35">
+ <stop
+ style="stop-color:#ff9e06;stop-opacity:1"
+ offset="0"
+ id="stop3921-2-3" />
+ <stop
+ style="stop-color:#af4600;stop-opacity:1"
+ offset="1"
+ id="stop3923-3-5" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-8,-8)"
+ y2="48"
+ x2="48"
+ y1="16"
+ x1="16"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4098"
+ xlink:href="#linearGradient3919-9-35"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(92.25,1040.3622)"
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient3986-9"
+ gradientUnits="userSpaceOnUse"
+ x1="28"
+ y1="28"
+ x2="44"
+ y2="32" />
+ <linearGradient
+ id="linearGradient3919-9-7">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop3921-2-93" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop3923-3-0" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient3917-1"
+ x1="43.78125"
+ y1="42.687496"
+ x2="15.453125"
+ y2="21.578121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1428571,0,0,1.1428571,89.96429,1038.0765)" />
+ <linearGradient
+ id="linearGradient4172">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop4174" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop4176" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(92.25,1040.3622)"
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient3925-4-8"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient4179">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop4181" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop4183" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5178-5"
+ id="linearGradient5184-3"
+ x1="-58.570175"
+ y1="1003.1377"
+ x2="-18.635777"
+ y2="1034.4441"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient5178-5">
+ <stop
+ style="stop-color:#13daba;stop-opacity:0.50196081;"
+ offset="0"
+ id="stop5180-8" />
+ <stop
+ style="stop-color:#03251f;stop-opacity:1"
+ offset="1"
+ id="stop5182-3" />
+ </linearGradient>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4462-8">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.68"
+ id="feGaussianBlur4464-6" />
+ </filter>
+ <linearGradient
+ gradientTransform="translate(164.98654,52.0355)"
+ y2="1034.4441"
+ x2="-18.635777"
+ y1="1003.1377"
+ x1="-58.570175"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4197"
+ xlink:href="#linearGradient5178-5"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5178-5"
+ id="linearGradient4308"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(164.98654,52.0355)"
+ x1="-58.570175"
+ y1="1003.1377"
+ x2="-18.635777"
+ y2="1034.4441" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient4310"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(92.25,1040.3622)"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient4312"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1428571,0,0,1.1428571,89.96429,1038.0765)"
+ x1="43.78125"
+ y1="42.687496"
+ x2="15.453125"
+ y2="21.578121" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient4314"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(92.25,1040.3622)"
+ x1="28"
+ y1="28"
+ x2="44"
+ y2="32" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="63.31906"
+ inkscape:cy="60.240415"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4300"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ spacingx="1px"
+ spacingy="1px" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="11.25,96.5"
+ id="guide4252" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <g
+ id="g4300"
+ transform="translate(-92.25,-52.00002)">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="rect4162-6"
+ d="m 120.25,1044.3622 -4,4 0,4 -3,-3 -6,0 -6,6 0,6 3,3 -4,0 -4,4 0,8 4,4 4,0 -3,3 0,6 6,6 6,0 3,-3 0,4 4,4 8,0 4,-4 0,-4 3,3 6,0 6,-6 0,-6 -3,-3 4,0 4,-4 0,-8 -4,-4 -4,0 3,-3 0,-6 -6,-6 -6,0 -3,3 0,-4 -4,-4 z"
+ style="color:#000000;fill:url(#linearGradient4308);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4462-8);enable-background:accumulate" />
+ <path
+ id="rect3071-3"
+ d="m 120.25,1048.3622 0,4.4062 c -0.22274,0.045 -0.43627,0.1037 -0.65625,0.1563 -0.4105,0.098 -0.81848,0.19 -1.21875,0.3125 -0.0218,0.01 -0.0408,0.024 -0.0625,0.031 -0.59186,0.1833 -1.18309,0.3898 -1.75,0.625 -0.50056,0.2088 -0.98977,0.4401 -1.46875,0.6875 -0.0827,0.043 -0.16797,0.081 -0.25,0.125 -0.55648,0.2973 -1.10149,0.6219 -1.625,0.9687 l -3.125,-3.125 -5.65625,5.6562 3.125,3.125 c -0.34167,0.5158 -0.67512,1.046 -0.96875,1.5938 -0.048,0.09 -0.0783,0.1905 -0.125,0.2812 -0.23239,0.45 -0.45783,0.9059 -0.65625,1.375 -0.0127,0.03 -0.0187,0.063 -0.0312,0.094 -0.23753,0.5694 -0.44005,1.1553 -0.625,1.75 -0.13065,0.4218 -0.24046,0.848 -0.34375,1.2812 -0.0526,0.22 -0.11102,0.4335 -0.15625,0.6563 l -4.40625,0 0,8 4.40625,0 c 0.0452,0.2227 0.10369,0.4363 0.15625,0.6562 0.0979,0.4105 0.19004,0.8185 0.3125,1.2188 0.007,0.022 0.0245,0.041 0.0312,0.062 0.18332,0.5918 0.38983,1.1831 0.625,1.75 0.2088,0.5005 0.44015,0.9897 0.6875,1.4687 0.0467,0.091 0.077,0.1914 0.125,0.2813 0.0175,0.033 0.0448,0.061 0.0625,0.094 0.27878,0.5148 0.58417,1.0139 0.90625,1.5 l -3.125,3.125 5.65625,5.6563 3.125,-3.125 c 0.52351,0.3468 1.06852,0.6714 1.625,0.9687 0.082,0.044 0.16729,0.082 0.25,0.125 0.45001,0.2324 0.90589,0.4579 1.375,0.6563 0.0303,0.013 0.0634,0.019 0.0937,0.031 0.56941,0.2376 1.15536,0.4401 1.75,0.625 0.42179,0.1307 0.84805,0.2405 1.28125,0.3438 0.21027,0.05 0.41221,0.1127 0.625,0.1562 0.01,0 0.0212,0 0.0312,0 l 0,4.4063 8,0 0,-4.4063 c 0.22283,-0.045 0.43616,-0.1037 0.65625,-0.1562 0.92356,-0.2207 1.81865,-0.4999 2.6875,-0.8438 0.61879,-0.2435 1.22575,-0.5106 1.8125,-0.8125 0.0825,-0.043 0.16813,-0.081 0.25,-0.125 0.55807,-0.2971 1.10017,-0.621 1.625,-0.9687 l 3.125,3.125 5.65625,-5.6563 -3.125,-3.125 c 0.34276,-0.5174 0.6751,-1.044 0.96875,-1.5937 0.0483,-0.09 0.0781,-0.1904 0.125,-0.2813 0.30187,-0.5867 0.56896,-1.1937 0.8125,-1.8125 0.34385,-0.8688 0.62307,-1.7639 0.84375,-2.6875 0.0525,-0.2201 0.111,-0.4334 0.15625,-0.6562 l 4.40625,0 0,-8 -4.40625,0 c -0.002,-0.01 0.002,-0.021 0,-0.031 -0.0435,-0.2127 -0.10613,-0.4148 -0.15625,-0.625 -0.22068,-0.9235 -0.4999,-1.8186 -0.84375,-2.6875 -0.24354,-0.6188 -0.51063,-1.2257 -0.8125,-1.8125 -0.0469,-0.091 -0.0767,-0.1912 -0.125,-0.2812 -0.29365,-0.5497 -0.62599,-1.0764 -0.96875,-1.5938 l 3.125,-3.125 -5.65625,-5.6562 -3.125,3.125 c -0.48499,-0.3213 -0.98661,-0.6273 -1.5,-0.9062 -0.0409,-0.022 -0.0839,-0.041 -0.125,-0.062 -0.0819,-0.044 -0.16745,-0.082 -0.25,-0.125 -0.58675,-0.3019 -1.19371,-0.569 -1.8125,-0.8125 -0.86885,-0.3439 -1.76394,-0.6231 -2.6875,-0.8438 -0.21019,-0.05 -0.4123,-0.1127 -0.625,-0.1562 -0.01,0 -0.0214,0 -0.0312,0 l 0,-4.4063 -8,0 z m 4,20 c 2.20914,0 4,1.7908 4,4 0,2.2091 -1.79086,4 -4,4 -0.27614,0 -0.55211,-0.041 -0.8125,-0.094 -1.82273,-0.373 -3.1875,-1.9732 -3.1875,-3.9062 0,-0.2762 0.0405,-0.5521 0.0937,-0.8125 0.37299,-1.8228 1.97325,-3.1875 3.90625,-3.1875 z"
+ style="color:#000000;fill:url(#linearGradient4310);fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3874-0"
+ d="m 124.25,1056.3622 c -8.83656,0 -16,7.1634 -16,16 0,8.8365 7.16344,16 16,16 8.83656,0 16,-7.1635 16,-16 0,-8.8366 -7.16344,-16 -16,-16 z m 0,12 c 2.20914,0 4,1.7908 4,4 0,2.2091 -1.79086,4 -4,4 -2.20914,0 -4,-1.7909 -4,-4 0,-2.2092 1.79086,-4 4,-4 z"
+ style="fill:url(#linearGradient4312);fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3876-7"
+ d="m 124.25,1064.3622 c -4.41828,0 -8,3.5817 -8,8 0,4.4183 3.58172,8 8,8 4.41828,0 8,-3.5817 8,-8 0,-4.4183 -3.58172,-8 -8,-8 z m 0,4 c 2.20914,0 4,1.7908 4,4 0,2.2091 -1.79086,4 -4,4 -2.20914,0 -4,-1.7909 -4,-4 0,-2.2092 1.79086,-4 4,-4 z"
+ style="fill:url(#linearGradient4314);fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3927-9"
+ d="m 124.25,1056.3622 c -8.83656,0 -16,7.1634 -16,16 0,4.8907 2.19848,9.2526 5.65625,12.1875 -2.28249,-2.7658 -3.65625,-6.3215 -3.65625,-10.1875 0,-8.8366 7.16344,-16 16,-16 3.86599,0 7.42176,1.3737 10.1875,3.6562 -2.93487,-3.4577 -7.29683,-5.6562 -12.1875,-5.6562 z"
+ style="fill:#000000;fill-opacity:0.39130435;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3970-3"
+ d="m 131.8125,1069.7997 c 0.27256,0.8054 0.4375,1.665 0.4375,2.5625 0,4.4183 -3.58172,8 -8,8 -2.72065,0 -5.11721,-1.3623 -6.5625,-3.4375 1.06926,3.1597 4.04169,5.4375 7.5625,5.4375 4.41828,0 8,-3.5817 8,-8 0,-1.6976 -0.53567,-3.2676 -1.4375,-4.5625 z"
+ style="fill:#000000;fill-opacity:0.39130435;stroke:none" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/herobrine.svg b/resources/sources/herobrine.svg
new file mode 100644
index 00000000..7392ba36
--- /dev/null
+++ b/resources/sources/herobrine.svg
@@ -0,0 +1,583 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="herobrine.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/herobrine.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4">
+ <filter
+ inkscape:collect="always"
+ id="filter3927"
+ x="-0.10864197"
+ width="1.2172839"
+ y="-0.10864198"
+ height="1.217284">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.1769547"
+ id="feGaussianBlur3929" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4035"
+ x="-0.066666667"
+ width="1.1333333"
+ y="-0.13333333"
+ height="1.2666667">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.16666667"
+ id="feGaussianBlur4037" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#484848"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="-64.2584"
+ inkscape:cy="64.238479"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2996"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <path
+ style="fill:#170b28;fill-opacity:1;stroke:none;filter:url(#filter3927)"
+ d="m 5.0606609,1024.2461 -1,1 0,24 1,1 24.0000001,0 1,-1 0,-24 -1,-1 z"
+ id="rect3223-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none"
+ d="m 6,1024.3622 20,0 c 1,0 2,1 2,2 l 0,7 -3,0 0,-3 -18,0 0,3 -3,0 0,-7.005 c 0,-0.995 1,-1.995 2,-1.995 z"
+ id="rect3978"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ transform="translate(0,1020.3622)"
+ style="fill:#421d0a;fill-opacity:1;stroke:none"
+ d="m 10,22 3,0 0,3 6,0 0,-3 3,0 0,6 -12,0 z"
+ id="rect3981"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ transform="translate(0,1020.3622)"
+ style="fill:#9c694c;fill-opacity:1;stroke:none"
+ d="m 7,10 18,0 0,3 3,0 0,13 c 0,1 -1,2 -2,2 l -4,0 0,-6 -3,0 0,-3 6,0 0,-3 -6,0 0,3 -6,0 0,-3 -6,0 0,3 6,0 0,3 -3,0 0,6 -4,0 C 5,28 4,27 4,26 l 0,-10 0,-3 3,0 z"
+ id="rect3941"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccc" />
+ <rect
+ style="fill:#bb8972;fill-opacity:1;stroke:none"
+ id="rect3939"
+ width="24"
+ height="24"
+ x="42"
+ y="1052.6122" />
+ <rect
+ y="1024.3622"
+ x="6.9999995"
+ height="3"
+ width="3"
+ id="rect3011-9"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#2f1f0f;fill-opacity:1;stroke:none"
+ id="rect3013-0"
+ width="3"
+ height="3"
+ x="10"
+ y="1024.3622" />
+ <rect
+ style="fill:#281c0b;fill-opacity:1;stroke:none"
+ id="rect3017-9"
+ width="3"
+ height="3"
+ x="13"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="16"
+ height="3"
+ width="3"
+ id="rect3019-6"
+ style="fill:#241808;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#261a0a;fill-opacity:1;stroke:none"
+ id="rect3021-8"
+ width="3"
+ height="3"
+ x="19"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="22"
+ height="3"
+ width="3"
+ id="rect3023-6"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ y="1027.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3027-7"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none"
+ id="rect3029-6"
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1027.3622" />
+ <rect
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none"
+ id="rect3033-4"
+ width="3"
+ height="3"
+ x="10"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3035-4"
+ style="fill:#332411;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#422a12;fill-opacity:1;stroke:none"
+ id="rect3037-2"
+ width="3"
+ height="3"
+ x="16"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3039-4"
+ style="fill:#3f2a15;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#2c1e0e;fill-opacity:1;stroke:none"
+ id="rect3041-2"
+ width="3"
+ height="3"
+ x="22"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3043-1"
+ style="fill:#281c0b;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3045-3"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3061-7"
+ style="fill:#342512;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#6a4030;fill-opacity:1;stroke:none"
+ id="rect3177-4"
+ width="6"
+ height="3"
+ x="13"
+ y="1039.3622" />
+ <rect
+ style="fill:#41210c;fill-opacity:1;stroke:none"
+ id="rect3085"
+ width="3"
+ height="3"
+ x="10"
+ y="1042.3622" />
+ <rect
+ style="fill:#8a4c3d;fill-opacity:1;stroke:none"
+ id="rect3089"
+ width="6"
+ height="3"
+ x="13"
+ y="1042.3622" />
+ <rect
+ y="1042.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3091"
+ style="fill:#41210c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#421d0a;fill-opacity:1;stroke:none"
+ id="rect3093"
+ width="3"
+ height="3"
+ x="13"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3095"
+ style="fill:#45220e;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#45220e;fill-opacity:1;stroke:none"
+ id="rect3097"
+ width="3"
+ height="3"
+ x="10"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="16"
+ height="3"
+ width="3"
+ id="rect3099"
+ style="fill:#45220e;fill-opacity:1;stroke:none" />
+ <g
+ id="g3944"
+ transform="translate(-28,1.7382813e-5)">
+ <rect
+ y="1030.3622"
+ x="35"
+ height="3"
+ width="3"
+ id="rect3047-1"
+ style="fill:#b6896c;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="38"
+ height="3"
+ width="3"
+ id="rect3051-9"
+ style="fill:#bd8e72;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#c69680;fill-opacity:1;stroke:none"
+ id="rect3053-2"
+ width="3"
+ height="3"
+ x="41"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="44"
+ height="3"
+ width="3"
+ id="rect3055-9"
+ style="fill:#bd8b72;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#bd8e74;fill-opacity:1;stroke:none"
+ id="rect3057-7"
+ width="3"
+ height="3"
+ x="47"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="50"
+ height="3"
+ width="3"
+ id="rect3059-2"
+ style="fill:#ac765a;fill-opacity:1;stroke:none" />
+ <rect
+ y="1033.3622"
+ x="32"
+ height="3"
+ width="3"
+ id="rect3063-8"
+ style="fill:#aa7d66;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b4846d;fill-opacity:1;stroke:none"
+ id="rect3065-6"
+ width="3"
+ height="3"
+ x="35"
+ y="1033.3622" />
+ <rect
+ style="fill:#aa7d66;fill-opacity:1;stroke:none"
+ id="rect3069-9"
+ width="3"
+ height="3"
+ x="38"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="41"
+ height="3"
+ width="3"
+ id="rect3071-7"
+ style="fill:#ad806d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9c725c;fill-opacity:1;stroke:none"
+ id="rect3073-1"
+ width="3"
+ height="3"
+ x="44"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="47"
+ height="3"
+ width="3"
+ id="rect3075-8"
+ style="fill:#bb8972;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9c694c;fill-opacity:1;stroke:none"
+ id="rect3077-8"
+ width="3"
+ height="3"
+ x="50"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="53"
+ height="3"
+ width="3"
+ id="rect3079-2"
+ style="fill:#9c694c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b4846d;fill-opacity:1;stroke:none"
+ id="rect3153-6"
+ width="3"
+ height="3"
+ x="32"
+ y="1036.3622" />
+ <rect
+ style="fill:#b57b67;fill-opacity:1;stroke:none"
+ id="rect3159-7"
+ width="3"
+ height="3"
+ x="41"
+ y="1036.3622" />
+ <rect
+ y="1036.3622"
+ x="44"
+ height="3"
+ width="3"
+ id="rect3161-0"
+ style="fill:#bb8972;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#aa7d66;fill-opacity:1;stroke:none"
+ id="rect3167-3"
+ width="3"
+ height="3"
+ x="53"
+ y="1036.3622" />
+ <rect
+ y="1039.3622"
+ x="32"
+ height="3"
+ width="3"
+ id="rect3169-4"
+ style="fill:#9c6346;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b37b62;fill-opacity:1;stroke:none"
+ id="rect3171-8"
+ width="3"
+ height="3"
+ x="35"
+ y="1039.3622" />
+ <rect
+ style="fill:#b78272;fill-opacity:1;stroke:none"
+ id="rect3175-0"
+ width="3"
+ height="3"
+ x="38"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="47"
+ height="3"
+ width="3"
+ id="rect3181-9"
+ style="fill:#be886c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a26a47;fill-opacity:1;stroke:none"
+ id="rect3183-7"
+ width="3"
+ height="3"
+ x="50"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="53"
+ height="3"
+ width="3"
+ id="rect3185-6"
+ style="fill:#805334;fill-opacity:1;stroke:none" />
+ <rect
+ y="1042.3622"
+ x="32"
+ height="3"
+ width="3"
+ id="rect3187-1"
+ style="fill:#905e43;fill-opacity:1;stroke:none" />
+ <rect
+ y="1042.3622"
+ x="53"
+ height="3"
+ width="3"
+ id="rect3203-8"
+ style="fill:#815339;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#6f452c;fill-opacity:1;stroke:none"
+ d="m 32,1045.3622 3,0 0,3 -1,0 c -1,0 -2,-1 -2,-2 z"
+ id="rect3205-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ y="1045.3622"
+ x="35"
+ height="3"
+ width="3"
+ id="rect3207-2"
+ style="fill:#6d432a;fill-opacity:1;stroke:none" />
+ <rect
+ y="1045.3622"
+ x="50"
+ height="3"
+ width="3"
+ id="rect3219-4"
+ style="fill:#83553b;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#7a4e33;fill-opacity:1;stroke:none"
+ d="m 53,1045.3622 3,0 0,1 c 0,1 -1,2 -2,2 l -1,0 z"
+ id="rect3221-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ style="fill:#965f40;fill-opacity:1;stroke:none"
+ id="rect3083"
+ width="3"
+ height="3"
+ x="35"
+ y="1042.3622" />
+ <rect
+ y="1042.3622"
+ x="50"
+ height="3"
+ width="3"
+ id="rect3101"
+ style="fill:#8f5e3e;fill-opacity:1;stroke:none" />
+ </g>
+ <g
+ id="g4045"
+ transform="matrix(1.05,0,0,1.05,14.2,-51.893092)">
+ <rect
+ y="1036.3622"
+ x="-7"
+ height="3"
+ width="6"
+ id="rect4011"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)"
+ id="rect4039"
+ width="6"
+ height="3"
+ x="-7"
+ y="1036.3622" />
+ <rect
+ y="1036.3622"
+ x="-7"
+ height="3"
+ width="6"
+ id="rect4041"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)"
+ id="rect4043"
+ width="6"
+ height="3"
+ x="-7"
+ y="1036.3622" />
+ </g>
+ <g
+ transform="matrix(1.05,0,0,1.05,26.2,-51.893092)"
+ id="g4051">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)"
+ id="rect4053"
+ width="6"
+ height="3"
+ x="-7"
+ y="1036.3622" />
+ <rect
+ y="1036.3622"
+ x="-7"
+ height="3"
+ width="6"
+ id="rect4055"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)"
+ id="rect4057"
+ width="6"
+ height="3"
+ x="-7"
+ y="1036.3622" />
+ <rect
+ y="1036.3622"
+ x="-7"
+ height="3"
+ width="6"
+ id="rect4059"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4035)" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/magitech.svg b/resources/sources/magitech.svg
new file mode 100644
index 00000000..c6dd6bc0
--- /dev/null
+++ b/resources/sources/magitech.svg
@@ -0,0 +1,886 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="magitech.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/magitech128.png"
+ inkscape:export-xdpi="180"
+ inkscape:export-ydpi="180">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4053">
+ <stop
+ id="stop4055"
+ offset="0"
+ style="stop-color:#19c6a1;stop-opacity:0.75294119;" />
+ <stop
+ id="stop4057"
+ offset="0.30864197"
+ style="stop-color:#a4f4e3;stop-opacity:1;" />
+ <stop
+ style="stop-color:#d9f7f1;stop-opacity:1;"
+ offset="0.65432096"
+ id="stop4059" />
+ <stop
+ id="stop4061"
+ offset="1"
+ style="stop-color:#009b78;stop-opacity:0.75294119;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4001">
+ <stop
+ id="stop4003"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4005"
+ offset="1"
+ style="stop-color:#0f5f52;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3882">
+ <stop
+ style="stop-color:#19c6a1;stop-opacity:1;"
+ offset="0"
+ id="stop3884" />
+ <stop
+ style="stop-color:#a4f4e3;stop-opacity:1;"
+ offset="0.30864197"
+ id="stop3894" />
+ <stop
+ id="stop3935"
+ offset="0.65432096"
+ style="stop-color:#d9f7f1;stop-opacity:1;" />
+ <stop
+ style="stop-color:#009b7a;stop-opacity:1;"
+ offset="1"
+ id="stop3886" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5178">
+ <stop
+ style="stop-color:#13daba;stop-opacity:0.50196081;"
+ offset="0"
+ id="stop5180" />
+ <stop
+ style="stop-color:#03251f;stop-opacity:1"
+ offset="1"
+ id="stop5182" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4030">
+ <stop
+ id="stop4032"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4034"
+ offset="1"
+ style="stop-color:#0c493f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-4" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-2" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient3917"
+ x1="43.78125"
+ y1="42.687496"
+ x2="15.453125"
+ y2="21.578121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1428571,0,0,1.1428571,-2.2857143,-2.28571)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3911-5"
+ id="linearGradient3929-9"
+ gradientUnits="userSpaceOnUse"
+ x1="40.5"
+ y1="36.999996"
+ x2="9"
+ y2="19.499996" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3911-5">
+ <stop
+ style="stop-color:#8c3800;stop-opacity:1"
+ offset="0"
+ id="stop3913-6" />
+ <stop
+ style="stop-color:#ff6701;stop-opacity:1"
+ offset="1"
+ id="stop3915-3" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient3986"
+ gradientUnits="userSpaceOnUse"
+ x1="28"
+ y1="28"
+ x2="44"
+ y2="32" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient3925-4"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3919-9">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop3921-2" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop3923-3" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-12,-12)"
+ y2="48"
+ x2="48"
+ y1="16"
+ x1="16"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4025"
+ xlink:href="#linearGradient3919-9"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3919-9-3">
+ <stop
+ style="stop-color:#ff9e06;stop-opacity:1"
+ offset="0"
+ id="stop3921-2-9" />
+ <stop
+ style="stop-color:#af4600;stop-opacity:1"
+ offset="1"
+ id="stop3923-3-4" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-8,-8)"
+ y2="48"
+ x2="48"
+ y1="16"
+ x1="16"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4065"
+ xlink:href="#linearGradient3919-9-3"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3919-9-35">
+ <stop
+ style="stop-color:#ff9e06;stop-opacity:1"
+ offset="0"
+ id="stop3921-2-3" />
+ <stop
+ style="stop-color:#af4600;stop-opacity:1"
+ offset="1"
+ id="stop3923-3-5" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-8,-8)"
+ y2="48"
+ x2="48"
+ y1="16"
+ x1="16"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4098"
+ xlink:href="#linearGradient3919-9-35"
+ inkscape:collect="always" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954-5"
+ id="radialGradient4024"
+ gradientUnits="userSpaceOnUse"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)" />
+ <linearGradient
+ id="linearGradient3954-5">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-5" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-3" />
+ </linearGradient>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter3916-4"
+ x="-0.11491533"
+ width="1.2298307"
+ y="-0.11548609"
+ height="1.2309722">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6059988"
+ id="feGaussianBlur3918-4" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4082-1"
+ x="-0.13465963"
+ width="1.2693193"
+ y="-0.12490681"
+ height="1.2498136">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.763901"
+ id="feGaussianBlur4084-4" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient4440"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1428571,0,0,1.1428571,-2.2857143,-2.28571)"
+ x1="43.78125"
+ y1="42.687496"
+ x2="15.453125"
+ y2="21.578121" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient4442"
+ gradientUnits="userSpaceOnUse"
+ x1="28"
+ y1="28"
+ x2="44"
+ y2="32" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954-5"
+ id="radialGradient4453"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.3784287,1.5357628,-0.89303843,0.80155051,16.46751,-42.794012)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <filter
+ inkscape:collect="always"
+ id="filter4462">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.68"
+ id="feGaussianBlur4464" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-1"
+ id="linearGradient4438-1"
+ gradientUnits="userSpaceOnUse"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48" />
+ <linearGradient
+ id="linearGradient3919-9-1">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop3921-2-92" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop3923-3-6" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9"
+ id="linearGradient5119"
+ gradientUnits="userSpaceOnUse"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5178"
+ id="linearGradient5184"
+ x1="10.429825"
+ y1="1007.6377"
+ x2="38.114223"
+ y2="1024.6941"
+ gradientUnits="userSpaceOnUse" />
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter3992">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.44"
+ id="feGaussianBlur3994" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954-1"
+ id="radialGradient4024-7"
+ gradientUnits="userSpaceOnUse"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)" />
+ <linearGradient
+ id="linearGradient3954-1">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-2" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-9" />
+ </linearGradient>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter3884">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.505"
+ id="feGaussianBlur3886" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter3898"
+ x="-0.20110182"
+ width="1.4022036"
+ y="-0.20210065"
+ height="1.4042013">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0604979"
+ id="feGaussianBlur3900" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter3916"
+ x="-0.11491533"
+ width="1.2298307"
+ y="-0.11548609"
+ height="1.2309722">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6059988"
+ id="feGaussianBlur3918" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4110"
+ x="-0.17716113"
+ width="1.3543223"
+ y="-0.17804106"
+ height="1.3560821">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.93424815"
+ id="feGaussianBlur4112" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4082"
+ x="-0.13465963"
+ width="1.2693193"
+ y="-0.12490681"
+ height="1.2498136">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.763901"
+ id="feGaussianBlur4084" />
+ </filter>
+ <linearGradient
+ gradientTransform="translate(-72.736544,988.32667)"
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient3986-9"
+ gradientUnits="userSpaceOnUse"
+ x1="28"
+ y1="28"
+ x2="44"
+ y2="32" />
+ <linearGradient
+ id="linearGradient3919-9-7">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop3921-2-93" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop3923-3-0" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient3917-1"
+ x1="43.78125"
+ y1="42.687496"
+ x2="15.453125"
+ y2="21.578121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1428571,0,0,1.1428571,-75.022254,986.04097)" />
+ <linearGradient
+ id="linearGradient5362">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop5364" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop5366" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-72.736544,988.32667)"
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919-9-7"
+ id="linearGradient3925-4-8"
+ x1="16"
+ y1="16"
+ x2="48"
+ y2="48"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient5369">
+ <stop
+ style="stop-color:#fff306;stop-opacity:1;"
+ offset="0"
+ id="stop5371" />
+ <stop
+ style="stop-color:#ff7f01;stop-opacity:1;"
+ offset="1"
+ id="stop5373" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3882"
+ id="linearGradient3888"
+ x1="42.125"
+ y1="10.375"
+ x2="24.75"
+ y2="56.125"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="pad" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5178-5"
+ id="linearGradient5184-3"
+ x1="-58.570175"
+ y1="1003.1377"
+ x2="-18.635777"
+ y2="1034.4441"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5178-5">
+ <stop
+ style="stop-color:#13daba;stop-opacity:1"
+ offset="0"
+ id="stop5180-8" />
+ <stop
+ style="stop-color:#03251f;stop-opacity:1"
+ offset="1"
+ id="stop5182-3" />
+ </linearGradient>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4462-8">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.68"
+ id="feGaussianBlur4464-6" />
+ </filter>
+ <linearGradient
+ gradientTransform="translate(0.03199987,-988.33014)"
+ y2="1024.6941"
+ x2="38.114223"
+ y1="1007.6377"
+ x1="10.429825"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3913"
+ xlink:href="#linearGradient5178-5"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4001"
+ id="linearGradient3999"
+ x1="38.625706"
+ y1="34.124741"
+ x2="31.201086"
+ y2="28.82144"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4053"
+ id="linearGradient4023"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="pad"
+ x1="42.125"
+ y1="10.375"
+ x2="24.75"
+ y2="56.125"
+ gradientTransform="translate(0.375,988.42468)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4001"
+ id="linearGradient4081"
+ gradientUnits="userSpaceOnUse"
+ x1="38.183765"
+ y1="33.064079"
+ x2="31.201086"
+ y2="28.82144" />
+ <filter
+ inkscape:collect="always"
+ id="filter4101"
+ x="-0.2597809"
+ width="1.5195618"
+ y="-0.34475598"
+ height="1.689512">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.3585616"
+ id="feGaussianBlur4103" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4153"
+ x="-0.10485404"
+ width="1.2097081"
+ y="-0.054822425"
+ height="1.1096448">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0071355"
+ id="feGaussianBlur4155" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="20"
+ inkscape:cx="33.36206"
+ inkscape:cy="39.612969"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ spacingx="1px"
+ spacingy="1px" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="11.25,96.5"
+ id="guide4252" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <g
+ id="g4430">
+ <path
+ style="color:#000000;fill:url(#linearGradient5184);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4462);enable-background:accumulate;opacity:1"
+ d="m 28,992.36214 -4,4.00004 0,4.00002 -3,-3.00002 -6,0 -6,6.00002 0,6 3,3 -4,0 -4,4 0,8 4,4 4,0 -3,3 0,6 6,6 6,0 c 1.147043,0.435 1.437261,0.5242 1.5,3.3125 2.236628,0.5754 7.083754,1.6875 9.5,1.6875 15.46397,0 28,-12.536 28,-28 0,-11.5626 -6.99928,-21.50902 -17,-25.78131 -0.666614,1.59048 -2.504535,2.24015 -3,1.78129 l -4,-4.00004 z"
+ id="rect4162"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccsscccc" />
+ <path
+ id="rect3071"
+ d="m 28,8 0,4.40625 c -0.222736,0.04523 -0.43627,0.103686 -0.65625,0.15625 -0.410499,0.09787 -0.818477,0.190041 -1.21875,0.3125 -0.02176,0.0067 -0.04077,0.02449 -0.0625,0.03125 -0.591863,0.183324 -1.183088,0.389835 -1.75,0.625 -0.500562,0.208804 -0.989768,0.440151 -1.46875,0.6875 -0.08271,0.04255 -0.167971,0.08132 -0.25,0.125 -0.556479,0.297288 -1.101494,0.621939 -1.625,0.96875 l -3.125,-3.125 -5.65625,5.65625 3.125,3.125 c -0.341672,0.515748 -0.675122,1.045976 -0.96875,1.59375 -0.04801,0.08987 -0.07834,0.190559 -0.125,0.28125 -0.232389,0.450012 -0.457832,0.905888 -0.65625,1.375 -0.01273,0.03027 -0.01866,0.0634 -0.03125,0.09375 -0.237526,0.569414 -0.440047,1.155357 -0.625,1.75 -0.130645,0.421788 -0.240463,0.848047 -0.34375,1.28125 C 12.509936,27.56373 12.451477,27.777264 12.40625,28 L 8,28 l 0,8 4.40625,0 c 0.04523,0.222736 0.103686,0.43627 0.15625,0.65625 0.09787,0.410499 0.190041,0.818477 0.3125,1.21875 0.0067,0.02176 0.02449,0.04077 0.03125,0.0625 0.183324,0.591863 0.389835,1.183088 0.625,1.75 0.208804,0.500562 0.440151,0.989768 0.6875,1.46875 0.04666,0.09069 0.07699,0.191375 0.125,0.28125 0.01751,0.03266 0.04481,0.06119 0.0625,0.09375 0.278784,0.514793 0.584173,1.01383 0.90625,1.5 l -3.125,3.125 5.65625,5.65625 3.125,-3.125 c 0.523506,0.346811 1.068521,0.671462 1.625,0.96875 0.08203,0.04368 0.167292,0.08245 0.25,0.125 0.450012,0.232389 0.905888,0.457832 1.375,0.65625 0.03027,0.01273 0.0634,0.01866 0.09375,0.03125 0.569414,0.237526 1.155357,0.440047 1.75,0.625 0.421788,0.130645 0.848047,0.240463 1.28125,0.34375 0.21027,0.05024 0.412209,0.112707 0.625,0.15625 0.01001,0.002 0.02124,-0.002 0.03125,0 l 8,0 c 0.22283,-0.04525 0.436163,-0.103776 0.65625,-0.15625 0.923563,-0.220682 1.81865,-0.499897 2.6875,-0.84375 0.618794,-0.243545 1.225752,-0.510633 1.8125,-0.8125 0.08255,-0.04263 0.168126,-0.08126 0.25,-0.125 0.558072,-0.297141 1.100165,-0.621059 1.625,-0.96875 l 5.65625,-5.65625 c 0.342758,-0.517388 0.6751,-1.044039 0.96875,-1.59375 0.04827,-0.09005 0.07808,-0.190386 0.125,-0.28125 0.301867,-0.586748 0.568955,-1.193706 0.8125,-1.8125 0.343853,-0.86885 0.623068,-1.763937 0.84375,-2.6875 0.05247,-0.220087 0.111003,-0.43342 0.15625,-0.65625 l 0,-8 c -0.002,-0.0098 0.002,-0.02142 0,-0.03125 -0.04353,-0.2127 -0.106134,-0.414805 -0.15625,-0.625 -0.220682,-0.923563 -0.499897,-1.81865 -0.84375,-2.6875 -0.243545,-0.618794 -0.510633,-1.225752 -0.8125,-1.8125 -0.04692,-0.09086 -0.07673,-0.191205 -0.125,-0.28125 C 49.3626,22.012789 49.030258,21.486138 48.6875,20.96875 L 43.03125,15.3125 c -0.484994,-0.321298 -0.986607,-0.627358 -1.5,-0.90625 -0.04094,-0.02217 -0.08389,-0.04061 -0.125,-0.0625 -0.08187,-0.04374 -0.167451,-0.08237 -0.25,-0.125 -0.586748,-0.301867 -1.193706,-0.568955 -1.8125,-0.8125 -0.86885,-0.343853 -1.763937,-0.623068 -2.6875,-0.84375 -0.210195,-0.05012 -0.4123,-0.112724 -0.625,-0.15625 -0.0098,-0.002 -0.02142,0.002 -0.03125,0 L 36,8 z m 4,20 c 2.209139,0 4,1.790861 4,4 0,2.209139 -1.790861,4 -4,4 -0.276142,0 -0.55211,-0.04047 -0.8125,-0.09375 C 29.364767,35.533265 28,33.932997 28,32 28,31.723858 28.040466,31.44789 28.09375,31.1875 28.466735,29.364767 30.067003,28 32,28 z"
+ style="color:#000000;fill:url(#linearGradient5119);fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccssscsss"
+ transform="translate(0,988.36218)" />
+ <path
+ id="path3874"
+ transform="translate(0,988.36218)"
+ d="m 32,16 c -8.836556,0 -16,7.163444 -16,16 0,8.836556 7.163444,16 16,16 8.836556,0 16,-7.163444 16,-16 0,-8.836556 -7.163444,-16 -16,-16 z m 0,12 c 2.209139,0 4,1.790861 4,4 0,2.209139 -1.790861,4 -4,4 -2.209139,0 -4,-1.790861 -4,-4 0,-2.209139 1.790861,-4 4,-4 z"
+ style="fill:url(#linearGradient4440);fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3876"
+ transform="translate(0,988.36218)"
+ d="m 32,24 c -4.418278,0 -8,3.581722 -8,8 0,4.418278 3.581722,8 8,8 4.418278,0 8,-3.581722 8,-8 0,-4.418278 -3.581722,-8 -8,-8 z m 0,4 c 2.209139,0 4,1.790861 4,4 0,2.209139 -1.790861,4 -4,4 -2.209139,0 -4,-1.790861 -4,-4 0,-2.209139 1.790861,-4 4,-4 z"
+ style="fill:url(#linearGradient4442);fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3927"
+ d="m 32,1004.3622 c -8.836556,0 -16,7.1634 -16,16 0,4.8907 2.198478,9.2526 5.65625,12.1875 C 19.373756,1029.7839 18,1026.2282 18,1022.3622 c 0,-8.8366 7.163444,-16 16,-16 3.86599,0 7.42176,1.3737 10.1875,3.6562 -2.93487,-3.4577 -7.29683,-5.6562 -12.1875,-5.6562 z"
+ style="fill:#000000;fill-opacity:0.39130435;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3970"
+ d="m 39.5625,1017.7997 c 0.272555,0.8054 0.4375,1.665 0.4375,2.5625 0,4.4183 -3.581722,8 -8,8 -2.720651,0 -5.117207,-1.3623 -6.5625,-3.4375 1.069255,3.1597 4.041685,5.4375 7.5625,5.4375 4.418278,0 8,-3.5817 8,-8 0,-1.6976 -0.535669,-3.2676 -1.4375,-4.5625 z"
+ style="fill:#000000;fill-opacity:0.39130435;stroke:none" />
+ </g>
+ <g
+ id="g5168">
+ <path
+ style="fill:url(#linearGradient4081);fill-opacity:1;stroke:none"
+ d="M 42.3125,10.34375 C 42.036647,17.156583 44.403627,25.298186 33,31 18.729736,38.135058 22.534188,45.86375 26.144531,55.257813 c 0.677562,0.217822 -0.12077,-0.0069 0.580514,0.151895 C 28.421984,55.79405 30.185284,56 32,56 45.254834,56 56,45.254843 56,32 56,22.438466 50.403908,14.202051 42.3125,10.34375 z"
+ id="path3997"
+ transform="translate(0,988.36218)"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscsssc" />
+ <path
+ sodipodi:type="arc"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4101);enable-background:accumulate"
+ id="path4083"
+ sodipodi:cx="13.611806"
+ sodipodi:cy="37.881245"
+ sodipodi:rx="6.2755728"
+ sodipodi:ry="4.7287765"
+ d="m 19.887379,37.881245 c 0,2.611631 -2.80967,4.728776 -6.275573,4.728776 -3.465903,0 -6.2755729,-2.117145 -6.2755729,-4.728776 0,-2.611631 2.8096699,-4.728777 6.2755729,-4.728777 3.465903,0 6.275573,2.117146 6.275573,4.728777 z"
+ transform="matrix(1.338311,0,0,1.4370006,15.945267,978.21621)" />
+ <path
+ id="path3014"
+ d="m 42.3125,10.34375 c 1.626929,3.407127 2.260167,8.676072 -0.03685,13.414814 -1.556024,3.210082 -3.304624,6.220765 -7.658776,8.522607 -4.247767,2.245602 -8.002291,2.8045 -9.732287,4.512181 -4.014531,3.96275 -2.278905,11.00222 1.274261,18.448146 C 28.475952,55.986401 29.435328,56 32,56 45.254834,56 56,45.254843 56,32 56,22.438466 50.403908,14.202051 42.3125,10.34375 z"
+ style="opacity:0.87029275;fill:url(#radialGradient4453);fill-opacity:1;stroke:none"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssscssc"
+ transform="translate(0,988.36218)" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3902"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3916-4);opacity:1"
+ transform="matrix(-1.8719467,0,0,-1.871948,82.871731,2921.8992)" />
+ <path
+ transform="matrix(-1.2147547,0,0,-1.2147555,70.284181,2259.2303)"
+ style="fill:#d7f4d7;fill-opacity:1;stroke:none;filter:url(#filter3916-4);opacity:1"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3920"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ transform="translate(0,988.36218)"
+ id="path4036"
+ d="M 44.78125 21.6875 L 41.28125 22.8125 C 41.121938 23.226574 40.928384 23.623793 40.71875 24.03125 L 44.78125 21.6875 z M 36.90625 28.53125 C 35.824388 29.387261 34.550485 30.224758 33 31 C 31.216966 31.891508 29.738803 32.739507 28.5 33.5625 C 29.458327 33.796351 30.4375 34.03125 30.4375 34.03125 L 30.4375 37.09375 C 30.4375 38.113344 28.40625 40.167906 28.40625 41.1875 L 28.40625 44.25 C 28.40625 44.25 29.900129 47.688 30.03125 48.625 C 30.45923 51.683387 29.509385 42.154431 29.71875 41.78125 C 30.864146 39.739651 32.416773 37.15625 33.03125 37.15625 C 33.264553 37.15625 34.697228 36.035418 35.9375 37.4375 C 37.107979 38.760684 38.10456 42.614278 38.4375 42.28125 C 39.293559 41.424965 36.5625 34.03125 36.5625 34.03125 L 42.78125 33.34375 L 47.1875 28.875 L 42.03125 32 L 38.59375 32 L 36.5625 29.96875 L 36.90625 28.53125 z "
+ style="opacity:0.86178864;fill:#000000;fill-opacity:0.86666667;stroke:none;filter:url(#filter4082-1)" />
+ <path
+ style="opacity:0.9497908;fill:url(#linearGradient3888);fill-opacity:1;stroke:none"
+ d="m 42.327724,10.343784 c -0.548738,-0.247754 -2.642269,-0.03831 -5.109835,2.376588 l 2.056042,0.657478 c 1.127203,-0.71102 1.815028,-0.50787 1.967315,-0.459692 1.494171,0.472705 0.645754,4.388833 0.1693,6.47919 -1.552718,8.361211 -9.002523,11.324205 -15.159208,14.152866 -3.345171,1.559801 -4.737296,4.36486 -5.23858,8.881728 -0.501284,4.516868 2.508904,8.819746 5.132693,12.829082 0.669966,0.210667 -0.142632,-0.006 0.54783,0.143209 0,0 -4.830399,-7.064617 -3.406092,-13.736155 1.213309,-5.683211 5.53111,-6.436498 9.626097,-8.496005 7.176567,-3.609337 10.985289,-9.424678 11.4425,-16.679509 0.19205,-3.226247 -1.020059,-5.693669 -2.028062,-6.14878 z"
+ id="path3112"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sccscczccsscs"
+ transform="translate(0,988.36218)" />
+ <path
+ sodipodi:nodetypes="ccczccsscc"
+ inkscape:connector-curvature="0"
+ id="path4021"
+ d="m 42.985537,999.28252 c 0.436543,5.62048 0.126695,2.81738 -1.199991,8.53948 -1.552718,8.3612 -6.315023,11.7617 -12.471708,14.5904 -3.345171,1.5598 -6.976573,2.6274 -7.477857,7.1442 -0.501284,4.5169 1.758904,9.6635 4.382693,13.6728 0.669966,0.2107 -0.142632,-0.01 0.54783,0.1432 0,0 -4.450497,-6.5927 -3.02619,-13.2642 1.213309,-5.6833 5.46861,-6.3427 9.563597,-8.4022 7.176567,-3.6094 11.032164,-9.3934 11.489375,-16.6483 0.19205,-3.2262 -0.760684,-4.5078 -1.807749,-5.77538 z"
+ style="opacity:1;fill:url(#linearGradient4023);fill-opacity:1;stroke:none;filter:url(#filter4153)" />
+ </g>
+ <g
+ transform="translate(72.5,-1.500004)"
+ id="g4268">
+ <path
+ transform="matrix(1.1666667,0,0,1.1666667,-5.3333344,983.02885)"
+ d="M 56,32 C 56,45.254834 45.254834,56 32,56 18.745166,56 8,45.254834 8,32 8,18.745166 18.745166,8 32,8 45.254834,8 56,18.745166 56,32 z"
+ sodipodi:ry="24"
+ sodipodi:rx="24"
+ sodipodi:cy="32"
+ sodipodi:cx="32"
+ id="path3005"
+ style="fill:#032620;fill-opacity:1;stroke:none;filter:url(#filter3992)"
+ sodipodi:type="arc" />
+ <g
+ transform="matrix(1.0195929,0,0,1.0195936,-0.15674338,-19.992546)"
+ id="g4116">
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient4024-7);fill-opacity:1;stroke:none"
+ id="path3014-0"
+ sodipodi:cx="32"
+ sodipodi:cy="32"
+ sodipodi:rx="20"
+ sodipodi:ry="20"
+ d="M 52,32 C 52,43.045695 43.045695,52 32,52 20.954305,52 12,43.045695 12,32 12,20.954305 20.954305,12 32,12 c 11.045695,0 20,8.954305 20,20 z"
+ transform="matrix(1.1769403,0,0,1.1769403,-6.1232836,982.70009)" />
+ <path
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3884)"
+ d="m 28,16 c -6.627417,0 -12,5.372583 -12,12 0,0.20181 0.0214,0.394383 0.03125,0.59375 C 20,20 22,22 28.65625,16.03125 28.436252,16.019242 28.222996,16 28,16 z"
+ transform="matrix(1.1769403,0,0,1.1769403,-5.6620909,982.68843)"
+ id="path3866"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3888"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3898)"
+ transform="matrix(1.487885,0,0,1.487885,-10.597031,-493.22036)" />
+ <path
+ transform="matrix(-1.8359746,0,0,-1.8359746,81.432966,2885.3572)"
+ style="fill:#00ffcc;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3902-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3920-6"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#d7f4d7;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ transform="matrix(-1.1914115,0,0,-1.1914115,69.087304,2235.4229)" />
+ <path
+ transform="matrix(0.58516297,0,0,0.58516297,6.7391969,416.10681)"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4110)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 5.570868,-4.4816 8.905966,-4.4575 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3964"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csccsc" />
+ <path
+ style="opacity:0.86178864;fill:#000000;fill-opacity:0.86666667;stroke:none;filter:url(#filter4082)"
+ d="M 30,29 30.450431,19.407328 32.447263,28.5316 35,27 l 1,-6 -1,-4 1,-2 0,3 1.125,3.375 -0.07418,2.744617 L 44.0828,21.873859 37,26 l -1,4 2,2 3.375,0 5.0625,-3.0625 -4.3125,4.375 L 36,34 c 0,0 2.686048,7.256632 1.846439,8.096462 -0.326542,0.326628 -1.289256,-3.475594 -2.437243,-4.77335 C 34.192758,35.947974 32.79132,37.0625 32.5625,37.0625 c -0.602669,0 -2.136064,2.519769 -3.25945,4.522134 -0.205342,0.36601 0.728015,9.714733 0.308259,6.715119 C 29.482708,47.380759 28,44 28,44 c 0,0 0,-2 0,-3 0,-1 2,-3 2,-4 0,-1 0,-3 0,-3 0,0 -4.25,-1.125 -5.25,-1.125 -0.47674,0 -1.893857,0.89205 -3.377184,1.040709 C 19.744745,34.078874 18.0625,33.5 18.0625,33.5 L 16.774038,34.754808 17,39 l -2,-4 2,-3 3.8125,-0.0625 1.170423,-6.693425 3.092068,-2.055016 -2.122126,3.038537 -0.913529,5.350612 L 28,31 z"
+ id="path4036-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccssssscsscsscccccccccccc"
+ transform="translate(0,988.36218)" />
+ </g>
+ </g>
+ <path
+ style="color:#000000;fill:url(#linearGradient5184-3);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4462-8);enable-background:accumulate"
+ d="m -44.736544,992.32667 -4,4 0,4.00003 -3,-3.00003 -6,0 -6,6.00003 0,6 3,3 -4,0 -4,4 0,8 4,4 4,0 -3,3 0,6 6,6 6,0 3,-3 0,4 4,4 8,0 4,-4 0,-4 3,3 6,0 6,-6 0,-6 -3,-3 4,0 4,-4 0,-8 -4,-4 -4,0 3,-3 0,-6 -6,-6.00003 -6,0 -3,3.00003 0,-4.00003 -4,-4 z"
+ id="rect4162-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;fill:url(#linearGradient3925-4-8);fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m -44.736544,996.32667 0,4.40623 c -0.222736,0.045 -0.43627,0.1037 -0.65625,0.1563 -0.410499,0.098 -0.818477,0.19 -1.21875,0.3125 -0.0218,0.01 -0.0408,0.024 -0.0625,0.031 -0.59186,0.1833 -1.18309,0.3898 -1.75,0.625 -0.50056,0.2088 -0.98977,0.4401 -1.46875,0.6875 -0.0827,0.043 -0.16797,0.081 -0.25,0.125 -0.55648,0.2973 -1.10149,0.6219 -1.625,0.9687 l -3.125,-3.125 -5.65625,5.6562 3.125,3.125 c -0.34167,0.5158 -0.67512,1.046 -0.96875,1.5938 -0.048,0.09 -0.0783,0.1905 -0.125,0.2812 -0.23239,0.45 -0.45783,0.9059 -0.65625,1.375 -0.0127,0.03 -0.0187,0.063 -0.0312,0.094 -0.23753,0.5694 -0.44005,1.1553 -0.625,1.75 -0.13065,0.4218 -0.24046,0.848 -0.34375,1.2812 -0.0526,0.22 -0.11102,0.4335 -0.15625,0.6563 l -4.40625,0 0,8 4.40625,0 c 0.0452,0.2227 0.10369,0.4363 0.15625,0.6562 0.0979,0.4105 0.19004,0.8185 0.3125,1.2188 0.007,0.022 0.0245,0.041 0.0312,0.062 0.18332,0.5918 0.38983,1.1831 0.625,1.75 0.2088,0.5005 0.44015,0.9897 0.6875,1.4687 0.0467,0.091 0.077,0.1914 0.125,0.2813 0.0175,0.033 0.0448,0.061 0.0625,0.094 0.27878,0.5148 0.58417,1.0139 0.90625,1.5 l -3.125,3.125 5.65625,5.6563 3.125,-3.125 c 0.52351,0.3468 1.06852,0.6714 1.625,0.9687 0.082,0.044 0.16729,0.082 0.25,0.125 0.45001,0.2324 0.90589,0.4579 1.375,0.6563 0.0303,0.013 0.0634,0.019 0.0937,0.031 0.56941,0.2376 1.15536,0.4401 1.75,0.625 0.42179,0.1307 0.848047,0.2405 1.28125,0.3438 0.21027,0.05 0.412209,0.1127 0.625,0.1562 0.01001,0 0.02124,0 0.03125,0 l 0,4.4063 8,0 0,-4.4063 c 0.22283,-0.045 0.436163,-0.1037 0.65625,-0.1562 0.923563,-0.2207 1.81865,-0.4999 2.6875,-0.8438 0.618794,-0.2435 1.225752,-0.5106 1.8125,-0.8125 0.08255,-0.043 0.168126,-0.081 0.25,-0.125 0.558072,-0.2971 1.100165,-0.621 1.625,-0.9687 l 3.125,3.125 5.65625,-5.6563 -3.125,-3.125 c 0.342758,-0.5174 0.6751,-1.044 0.96875,-1.5937 0.04827,-0.09 0.07808,-0.1904 0.125,-0.2813 0.301867,-0.5867 0.568955,-1.1937 0.8125,-1.8125 0.343853,-0.8688 0.623068,-1.7639 0.84375,-2.6875 0.05247,-0.2201 0.111003,-0.4334 0.15625,-0.6562 l 4.40625,0 0,-8 -4.40625,0 c -0.002,-0.01 0.002,-0.021 0,-0.031 -0.04353,-0.2127 -0.106134,-0.4148 -0.15625,-0.625 -0.220682,-0.9235 -0.499897,-1.8186 -0.84375,-2.6875 -0.243545,-0.6188 -0.510633,-1.2257 -0.8125,-1.8125 -0.04692,-0.091 -0.07673,-0.1912 -0.125,-0.2812 -0.29365,-0.5497 -0.625992,-1.0764 -0.96875,-1.5938 l 3.125,-3.125 -5.65625,-5.6562 -3.125,3.125 c -0.484994,-0.3213 -0.986607,-0.6273 -1.5,-0.9062 -0.04094,-0.022 -0.08389,-0.041 -0.125,-0.062 -0.08187,-0.044 -0.167451,-0.082 -0.25,-0.125 -0.586748,-0.3019 -1.193706,-0.569 -1.8125,-0.8125 -0.86885,-0.3439 -1.763937,-0.6231 -2.6875,-0.8438 -0.210195,-0.05 -0.4123,-0.1127 -0.625,-0.1562 -0.0098,0 -0.02142,0 -0.03125,0 l 0,-4.40633 -8,0 z m 4,20.00003 c 2.209139,0 4,1.7908 4,4 0,2.2091 -1.790861,4 -4,4 -0.276142,0 -0.55211,-0.041 -0.8125,-0.094 -1.822733,-0.373 -3.1875,-1.9732 -3.1875,-3.9062 0,-0.2762 0.04047,-0.5521 0.09375,-0.8125 0.372985,-1.8228 1.973253,-3.1875 3.90625,-3.1875 z"
+ id="rect3071-3" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3917-1);fill-opacity:1;stroke:none"
+ d="m -40.736544,1004.3267 c -8.83656,0 -16,7.1634 -16,16 0,8.8365 7.16344,16 16,16 8.836556,0 16,-7.1635 16,-16 0,-8.8366 -7.163444,-16 -16,-16 z m 0,12 c 2.209139,0 4,1.7908 4,4 0,2.2091 -1.790861,4 -4,4 -2.209139,0 -4,-1.7909 -4,-4 0,-2.2092 1.790861,-4 4,-4 z"
+ id="path3874-0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3986-9);fill-opacity:1;stroke:none"
+ d="m -40.736544,1012.3267 c -4.418278,0 -8,3.5817 -8,8 0,4.4183 3.581722,8 8,8 4.418278,0 8,-3.5817 8,-8 0,-4.4183 -3.581722,-8 -8,-8 z m 0,4 c 2.209139,0 4,1.7908 4,4 0,2.2091 -1.790861,4 -4,4 -2.209139,0 -4,-1.7909 -4,-4 0,-2.2092 1.790861,-4 4,-4 z"
+ id="path3876-7" />
+ <path
+ style="fill:#000000;fill-opacity:0.39130435;stroke:none"
+ d="m -40.736544,1004.3267 c -8.83656,0 -16,7.1634 -16,16 0,4.8907 2.19848,9.2526 5.65625,12.1875 -2.28249,-2.7658 -3.65625,-6.3215 -3.65625,-10.1875 0,-8.8366 7.16344,-16 16,-16 3.86599,0 7.42176,1.3737 10.1875,3.6562 -2.93487,-3.4577 -7.29683,-5.6562 -12.1875,-5.6562 z"
+ id="path3927-9"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:0.39130435;stroke:none"
+ d="m -33.174044,1017.7642 c 0.272555,0.8054 0.4375,1.665 0.4375,2.5625 0,4.4183 -3.581722,8 -8,8 -2.720651,0 -5.117207,-1.3623 -6.5625,-3.4375 1.069255,3.1597 4.041685,5.4375 7.5625,5.4375 4.418278,0 8,-3.5817 8,-8 0,-1.6976 -0.535669,-3.2676 -1.4375,-4.5625 z"
+ id="path3970-3"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/resources/sources/meat.svg b/resources/sources/meat.svg
new file mode 100644
index 00000000..69a20073
--- /dev/null
+++ b/resources/sources/meat.svg
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32px"
+ height="32px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="meat.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/meat128.png"
+ inkscape:export-xdpi="360"
+ inkscape:export-ydpi="360">
+ <defs
+ id="defs2987">
+ <filter
+ inkscape:collect="always"
+ id="filter4454"
+ x="-0.16296296"
+ width="1.3259259"
+ y="-0.16296296"
+ height="1.3259259">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.2222222"
+ id="feGaussianBlur4456" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4863">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.53333333"
+ id="feGaussianBlur4865" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4974"
+ x="-0.18666667"
+ width="1.3733333"
+ y="-0.18666667"
+ height="1.3733333">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.8666667"
+ id="feGaussianBlur4976" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="16"
+ inkscape:cx="9.6045985"
+ inkscape:cy="9.7795413"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3004"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ style="fill:#ffcc00;stroke:none;filter:url(#filter4974)"
+ d="m 4,10 6,-6 4,0 10,10 0,4 2,0 2,2 0,4 -4,4 -6,0 0,-4 -4,0 -2,-2 -2,0 -6,-6 z"
+ id="path4952"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccc" />
+ <g
+ transform="translate(-68,24)"
+ id="g4869" />
+ <g
+ id="g3473"
+ transform="translate(4,4)">
+ <rect
+ y="2"
+ x="6"
+ height="2"
+ width="4"
+ id="rect3006"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3014"
+ width="2"
+ height="2"
+ x="4"
+ y="4" />
+ <rect
+ y="6"
+ x="2"
+ height="6"
+ width="2"
+ id="rect3016"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ y="4"
+ x="10"
+ height="2"
+ width="2"
+ id="rect3018"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3020"
+ width="2"
+ height="2"
+ x="4"
+ y="12" />
+ <rect
+ y="14"
+ x="6"
+ height="2"
+ width="2"
+ id="rect3022"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3024"
+ width="2"
+ height="2"
+ x="12"
+ y="6" />
+ <rect
+ y="8"
+ x="14"
+ height="2"
+ width="2"
+ id="rect3026"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3028"
+ width="2"
+ height="2"
+ x="8"
+ y="14" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3030"
+ width="4"
+ height="2"
+ x="18"
+ y="16" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3034"
+ width="2"
+ height="4"
+ x="16"
+ y="18" />
+ <rect
+ y="18"
+ x="20"
+ height="2"
+ width="2"
+ id="rect3036"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect3038"
+ width="2"
+ height="2"
+ x="18"
+ y="20" />
+ <rect
+ y="18"
+ x="18"
+ height="2"
+ width="2"
+ id="rect3040"
+ style="fill:#fff7dc;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#e2d5aa;fill-opacity:1;stroke:none"
+ id="rect3042"
+ width="2"
+ height="2"
+ x="16"
+ y="16" />
+ <rect
+ y="12"
+ x="10"
+ height="2"
+ width="4"
+ id="rect3044"
+ style="fill:#7b512d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#613c1b;fill-opacity:1;stroke:none"
+ id="rect3046"
+ width="2"
+ height="2"
+ x="8"
+ y="12" />
+ <rect
+ y="10"
+ x="6"
+ height="2"
+ width="4"
+ id="rect3048"
+ style="fill:#7b512d;fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ inkscape:connector-curvature="0"
+ id="rect3050"
+ d="m 10,8 2,0 0,2 2,0 0,2 -4,0 z"
+ style="fill:#9d6d43;fill-opacity:1;stroke:none" />
+ <rect
+ y="8"
+ x="8"
+ height="2"
+ width="2"
+ id="rect3053"
+ style="fill:#b88458;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b88458;fill-opacity:1;stroke:none"
+ id="rect3055"
+ width="2"
+ height="2"
+ x="10"
+ y="6" />
+ <rect
+ y="8"
+ x="12"
+ height="2"
+ width="2"
+ id="rect3057"
+ style="fill:#b88458;fill-opacity:1;stroke:none" />
+ <rect
+ y="12"
+ x="6"
+ height="2"
+ width="2"
+ id="rect3059"
+ style="fill:#613c1b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#dfb18f;fill-opacity:1;stroke:none"
+ id="rect3061"
+ width="2"
+ height="2"
+ x="6"
+ y="6" />
+ <rect
+ y="4"
+ x="8"
+ height="2"
+ width="2"
+ id="rect3063"
+ style="fill:#b21818;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b21818;fill-opacity:1;stroke:none"
+ id="rect3065"
+ width="2"
+ height="2"
+ x="4"
+ y="8" />
+ <rect
+ y="6"
+ x="4"
+ height="2"
+ width="2"
+ id="rect3067"
+ style="fill:#d42a2a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#d42a2a;fill-opacity:1;stroke:none"
+ id="rect3069"
+ width="2"
+ height="2"
+ x="6"
+ y="4" />
+ <rect
+ y="6"
+ x="8"
+ height="2"
+ width="2"
+ id="rect3071"
+ style="fill:#d42a2a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#d42a2a;fill-opacity:1;stroke:none"
+ id="rect3073"
+ width="2"
+ height="2"
+ x="6"
+ y="8" />
+ <rect
+ y="16"
+ x="10"
+ height="2"
+ width="6"
+ id="rect4470"
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#0c0d0b;fill-opacity:1;stroke:none"
+ id="rect4472"
+ width="2"
+ height="6"
+ x="16"
+ y="10" />
+ <rect
+ y="14"
+ x="12"
+ height="2"
+ width="4"
+ id="rect4474"
+ style="fill:#613c1b;fill-opacity:1;stroke:none" />
+ <rect
+ y="12"
+ x="14"
+ height="2"
+ width="2"
+ id="rect4476"
+ style="fill:#9d6d43;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#502d16;fill-opacity:1;stroke:none"
+ id="rect4478"
+ width="2"
+ height="2"
+ x="10"
+ y="14" />
+ <rect
+ style="fill:#b88458;fill-opacity:1;stroke:none"
+ id="rect4480"
+ width="2"
+ height="2"
+ x="14"
+ y="10" />
+ <rect
+ style="fill:#613c1b;fill-opacity:1;stroke:none"
+ id="rect4978"
+ width="2"
+ height="2"
+ x="4"
+ y="10" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/netherstar.svg b/resources/sources/netherstar.svg
new file mode 100644
index 00000000..4046e4ec
--- /dev/null
+++ b/resources/sources/netherstar.svg
@@ -0,0 +1,342 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="netherstar.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/netherstar.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4065">
+ <stop
+ id="stop4067"
+ offset="0"
+ style="stop-color:#556b6b;stop-opacity:1" />
+ <stop
+ style="stop-color:#3b8585;stop-opacity:0.49803922;"
+ offset="0.5"
+ id="stop4069" />
+ <stop
+ id="stop4071"
+ offset="1"
+ style="stop-color:#fafbfb;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4053">
+ <stop
+ id="stop4055"
+ offset="0"
+ style="stop-color:#556b6b;stop-opacity:1" />
+ <stop
+ style="stop-color:#3b8585;stop-opacity:0.49803922;"
+ offset="0.5"
+ id="stop4057" />
+ <stop
+ id="stop4059"
+ offset="1"
+ style="stop-color:#fafbfb;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3802">
+ <stop
+ style="stop-color:#cbd6d6;stop-opacity:1;"
+ offset="0"
+ id="stop3804" />
+ <stop
+ style="stop-color:#637e7e;stop-opacity:1"
+ offset="1"
+ id="stop3806" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3006">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3008" />
+ <stop
+ id="stop3955"
+ offset="0.14285807"
+ style="stop-color:#fff656;stop-opacity:1;" />
+ <stop
+ id="stop3949"
+ offset="0.28571615"
+ style="stop-color:#fdd300;stop-opacity:1;" />
+ <stop
+ style="stop-color:#f6d01a;stop-opacity:1;"
+ offset="0.50000137"
+ id="stop3951" />
+ <stop
+ style="stop-color:#fafd00;stop-opacity:0;"
+ offset="1"
+ id="stop3010" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3893">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.3"
+ id="feGaussianBlur3895" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3901">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.3"
+ id="feGaussianBlur3903" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3905">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.3000003"
+ id="feGaussianBlur3907" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3913">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.3"
+ id="feGaussianBlur3915" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3945">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.74074074"
+ id="feGaussianBlur3947" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3967">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.4979424"
+ id="feGaussianBlur3969" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4053"
+ id="linearGradient3985"
+ gradientUnits="userSpaceOnUse"
+ x1="51"
+ y1="1014.3622"
+ x2="35"
+ y2="997.36218" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4065"
+ id="linearGradient3987"
+ gradientUnits="userSpaceOnUse"
+ x1="25"
+ y1="1039.3622"
+ x2="9"
+ y2="1023.3622" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3802"
+ id="linearGradient3989"
+ gradientUnits="userSpaceOnUse"
+ x1="28"
+ y1="28"
+ x2="36"
+ y2="36" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3006"
+ id="radialGradient3991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1034483,-2.1791858e-6,2.1791909e-6,1.103451,-3.1035149,985.25875)"
+ cx="30"
+ cy="30"
+ fx="30"
+ fy="30"
+ r="14.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3802"
+ id="linearGradient3993"
+ gradientUnits="userSpaceOnUse"
+ x1="24"
+ y1="48"
+ x2="36"
+ y2="48" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3802"
+ id="linearGradient3995"
+ gradientUnits="userSpaceOnUse"
+ x1="24"
+ y1="1000.3622"
+ x2="36"
+ y2="1000.3622" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3802"
+ id="linearGradient3997"
+ gradientUnits="userSpaceOnUse"
+ x1="44"
+ y1="1018.3622"
+ x2="52"
+ y2="1018.3622" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3802"
+ id="linearGradient3999"
+ gradientUnits="userSpaceOnUse"
+ x1="8"
+ y1="1018.3622"
+ x2="16"
+ y2="1018.3622" />
+ <filter
+ inkscape:collect="always"
+ id="filter4108">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.39"
+ id="feGaussianBlur4110" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="6"
+ inkscape:cx="32.789148"
+ inkscape:cy="31.233555"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ spacingx="1px"
+ spacingy="1px" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <g
+ id="g3971"
+ transform="translate(2,1.99998)">
+ <path
+ style="fill:#cccccc;fill-opacity:1;stroke:none;filter:url(#filter3967)"
+ d="m 57,27.00002 0,6 -8,8 -8,0 0,8 -8,8 -6,0 -8,-8 0,-8 -8,0 -8,-8 0,-6 8,-8 8,0 0,-7 8,-9 6,0 8,8 0,8 8,0 z"
+ id="path3957"
+ inkscape:connector-curvature="0"
+ transform="translate(0,988.36218)"
+ sodipodi:nodetypes="ccccccccccccccccccccc" />
+ <path
+ transform="translate(0,988.36218)"
+ inkscape:connector-curvature="0"
+ id="path3000"
+ d="m 56,28 0,4 -8,8 -8,0 0,8 -8,8 -4,0 -8,-8 0,-8 -8,0 -8,-8 0,-4 8,-8 8,0 0,-8 8,-8 4,0 4,4 4,4 0,8 8,0 z"
+ style="fill:#eff2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3788"
+ d="m 52,1024.3622 0,-8 -4,-4 -8,0 -4,-4 0,-8 -4,-4.00002 -8,0 4,-4 4,0 8,8.00002 0,8 8,0 8,8 0,4 z"
+ style="fill:url(#linearGradient3985);fill-opacity:1;stroke:none" />
+ <path
+ style="fill:url(#linearGradient3987);fill-opacity:1;stroke:none"
+ d="m 8,1012.3622 0,8 4,4 8,0 4,4 0,8 4,4 8.00001,0 -4,4 -4.00001,0 -8,-8 0,-8 -8,0 -8,-8 0,-4 z"
+ id="path3790"
+ inkscape:connector-curvature="0" />
+ <path
+ transform="translate(0,988.36218)"
+ inkscape:connector-curvature="0"
+ id="path3002"
+ d="m 36,8 -8,0 -4,4 0,8 -4,4 -8,0 -4,4 0,8 -4,-4 0,-4 8,-8 8,0 0,-8 8,-8 4,0 z"
+ style="opacity:0.67364017;fill:#ffffff;stroke:none" />
+ <path
+ transform="translate(0,988.36218)"
+ inkscape:connector-curvature="0"
+ id="path3800"
+ d="m 28,20 -8,8 0,4 8,8 4,0 8,-8 0,-4 -8,-8 z"
+ style="fill:url(#linearGradient3989);fill-opacity:1;stroke:none;filter:url(#filter3945)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3004"
+ d="m 28,1000.3622 0,16 -16,0 0,4 16,0 0,16 4,0 0,-16 16,0 0,-4 -16,0 0,-16 z"
+ style="fill:url(#radialGradient3991);fill-opacity:1;stroke:none"
+ sodipodi:nodetypes="ccccccccccccc" />
+ <path
+ style="fill:#556b6b;fill-opacity:1;stroke:none"
+ d="m 23.99998,1040.3622 8,0 4,-4 0,-8 4,-4 8,0 4.00002,-4 0,-8 4,4 0,4 -8.00002,8 -8,0 0,8 -8,8 -4,0 z"
+ id="path3786"
+ inkscape:connector-curvature="0" />
+ <path
+ transform="translate(0,988.36218)"
+ inkscape:connector-curvature="0"
+ id="path3792"
+ d="m 32,48 -4,0 -4,-4 0,4 4,4 4,0 4,-4 0,-4 z"
+ style="fill:url(#linearGradient3993);fill-opacity:1;stroke:none;filter:url(#filter3893)" />
+ <path
+ style="fill:url(#linearGradient3995);fill-opacity:1;stroke:none;filter:url(#filter3905)"
+ d="m 32,1000.3622 -4,0 -4,4 0,-4 4,-4.00002 4,0 4,4.00002 0,4 z"
+ id="path3794"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3796"
+ d="m 48,1020.3622 0,-4 -4,-4 4,0 4,4 0,4 -4,4 -4,0 z"
+ style="fill:url(#linearGradient3997);fill-opacity:1;stroke:none;filter:url(#filter3901)" />
+ <path
+ style="fill:url(#linearGradient3999);fill-opacity:1;stroke:none;filter:url(#filter3913)"
+ d="m 12,1016.3622 0,4 4,4 -4,0 -4,-4 0,-4 4,-4 4,0 z"
+ id="path3798"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#eff2f2;fill-opacity:0.10628019000000000;stroke:none;filter:url(#filter4108)"
+ d="m 28,992.3622 -8,8 0,8 -8,0 -8.0000001,8 0,4 8.0000001,8 8,0 0,8 8,8 4,0 8,-8 0,-8 8,0 8,-8 0,-4 -8,-8 -8,0 0,-8 -4,-4 -4,-4 -4,0 z m 2.90625,2.25 c 1.790052,0.15723 3.5491,1.39991 5.03125,4.1875 5.21676,3.1721 -1.835545,14.0536 7.28125,11.3125 4.64748,-0.9978 13.08236,5.9966 9.65625,10.3437 -2.87943,6.7429 -10.8243,4.9933 -14.84375,6.6563 1.50952,6.5659 -1.74938,11.1566 -6.8125,15 -4.92726,-0.671 -10.654448,-6.4059 -9.1875,-12 1.831567,-8.2895 -10.191583,-0.2312 -12.4375001,-7.2813 -7.428566,-3.9347 -0.557092,-9.4767 3.4375001,-12.7187 7.835946,1.7735 10.684647,-1.4404 9,-9.1875 L 22.5,1000.4559 c 2.310711,-3.39825 5.42283,-6.10574 8.40625,-5.8437 z"
+ id="path4073"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/pskeleton.svg b/resources/sources/pskeleton.svg
new file mode 100644
index 00000000..c2783df8
--- /dev/null
+++ b/resources/sources/pskeleton.svg
@@ -0,0 +1,581 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="32"
+ height="32"
+ id="svg2">
+ <defs
+ id="defs4">
+ <filter
+ x="-0.18000001"
+ y="-0.36000001"
+ width="1.36"
+ height="1.72"
+ color-interpolation-filters="sRGB"
+ id="filter5719">
+ <feGaussianBlur
+ id="feGaussianBlur5721"
+ stdDeviation="0.15" />
+ </filter>
+ <filter
+ x="-0.20999999"
+ y="-0.28"
+ width="1.42"
+ height="1.5599999"
+ color-interpolation-filters="sRGB"
+ id="filter5723">
+ <feGaussianBlur
+ id="feGaussianBlur5725"
+ stdDeviation="0.35" />
+ </filter>
+ <filter
+ x="-0.1728"
+ y="-0.34560001"
+ width="1.3456"
+ height="1.6912"
+ color-interpolation-filters="sRGB"
+ id="filter5711">
+ <feGaussianBlur
+ id="feGaussianBlur5713"
+ stdDeviation="0.144" />
+ </filter>
+ <filter
+ x="-0.20999999"
+ y="-0.28"
+ width="1.42"
+ height="1.5599999"
+ color-interpolation-filters="sRGB"
+ id="filter5727">
+ <feGaussianBlur
+ id="feGaussianBlur5729"
+ stdDeviation="0.35" />
+ </filter>
+ <filter
+ x="-0.14708571"
+ y="-0.25740001"
+ width="1.2941715"
+ height="1.5148"
+ color-interpolation-filters="sRGB"
+ id="filter4028-6">
+ <feGaussianBlur
+ id="feGaussianBlur4030-9"
+ stdDeviation="0.429" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ id="filter4056-6">
+ <feGaussianBlur
+ id="feGaussianBlur4058-6"
+ stdDeviation="0.676" />
+ </filter>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ transform="translate(0,-1020.3622)"
+ id="layer1">
+ <rect
+ width="24"
+ height="24"
+ x="3.9999998"
+ y="1024.3622"
+ id="rect4063-2"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <rect
+ width="26"
+ height="26"
+ x="3"
+ y="3"
+ transform="matrix(1.0769231,0,0,1.0769237,-1.2307693,1019.1314)"
+ id="rect4038-2"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none;filter:url(#filter4056-6)" />
+ <rect
+ width="20"
+ height="5.0000172"
+ x="5.9999995"
+ y="1041.3622"
+ id="rect3189-4"
+ style="fill:#494949;fill-opacity:1;stroke:none" />
+ <path
+ d="m 6.0000007,1041.3622 20.0000003,0 0,2 -18.0000003,0 0,3 -2,0 z"
+ id="path4034-6"
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4028-6)" />
+ <rect
+ width="8"
+ height="5.0000172"
+ x="5.9999995"
+ y="1035.3622"
+ id="rect3155-9"
+ style="fill:#494949;fill-opacity:1;stroke:none" />
+ <rect
+ width="8"
+ height="5.0000172"
+ x="18"
+ y="1035.3622"
+ id="rect3163-4"
+ style="fill:#494949;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1024.3622"
+ id="rect3009-1"
+ style="fill:#a1a1a1;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1024.3622"
+ id="rect3011-9"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1024.3622"
+ id="rect3013-0"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="13"
+ y="1024.3622"
+ id="rect3017-9"
+ style="fill:#919191;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="16"
+ y="1024.3622"
+ id="rect3019-6"
+ style="fill:#888888;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="19"
+ y="1024.3622"
+ id="rect3021-8"
+ style="fill:#8f8f8f;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="22"
+ y="1024.3622"
+ id="rect3023-6"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1024.3622"
+ id="rect3025-7"
+ style="fill:#989898;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1027.3622"
+ id="rect3027-7"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1027.3622"
+ id="rect3029-6"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1027.3622"
+ id="rect3031-0"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1027.3622"
+ id="rect3033-4"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="13"
+ y="1027.3622"
+ id="rect3035-4"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="16"
+ y="1027.3622"
+ id="rect3037-2"
+ style="fill:#c1c1c1;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="19"
+ y="1027.3622"
+ id="rect3039-4"
+ style="fill:#bebebe;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="22"
+ y="1027.3622"
+ id="rect3041-2"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1027.3622"
+ id="rect3043-1"
+ style="fill:#919191;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1030.3622"
+ id="rect3045-3"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1030.3622"
+ id="rect3047-1"
+ style="fill:#c7c7c7;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1030.3622"
+ id="rect3049-5"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1030.3622"
+ id="rect3051-9"
+ style="fill:#cacaca;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="13"
+ y="1030.3622"
+ id="rect3053-2"
+ style="fill:#d8d8d8;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="16"
+ y="1030.3622"
+ id="rect3055-9"
+ style="fill:#cfcfcf;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="19"
+ y="1030.3622"
+ id="rect3057-7"
+ style="fill:#cfcfcf;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="22"
+ y="1030.3622"
+ id="rect3059-2"
+ style="fill:#bababa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1030.3622"
+ id="rect3061-7"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <path
+ d="m 6.0000007,1035.3622 8.0000003,0 0,2 -6.0000003,0 0,3 -2,0 z"
+ id="path4032-3"
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4028-6)" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1033.3622"
+ id="rect3063-8"
+ style="fill:#bababa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1033.3622"
+ id="rect3065-6"
+ style="fill:#c4c4c4;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1033.3622"
+ id="rect3067-1"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1033.3622"
+ id="rect3069-9"
+ style="fill:#bababa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="13"
+ y="1033.3622"
+ id="rect3071-7"
+ style="fill:#c1c1c1;fill-opacity:1;stroke:none" />
+ <path
+ d="m 18.000001,1035.3622 8,0 0,2 -6,0 0,3 -2,0 z"
+ id="path4036-6"
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4028-6)" />
+ <rect
+ width="3"
+ height="3"
+ x="16"
+ y="1033.3622"
+ id="rect3073-1"
+ style="fill:#afafaf;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="19"
+ y="1033.3622"
+ id="rect3075-8"
+ style="fill:#cacaca;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="22"
+ y="1033.3622"
+ id="rect3077-8"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1033.3622"
+ id="rect3079-2"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1036.3622"
+ id="rect3153-6"
+ style="fill:#c4c4c4;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="13"
+ y="1036.3622"
+ id="rect3159-7"
+ style="fill:#c4c4c4;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="16"
+ y="1036.3622"
+ id="rect3161-0"
+ style="fill:#cacaca;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1036.3622"
+ id="rect3167-3"
+ style="fill:#bababa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1039.3622"
+ id="rect3169-4"
+ style="fill:#a7a7a7;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1039.3622"
+ id="rect3171-8"
+ style="fill:#c1c1c1;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1039.3622"
+ id="rect3173-5"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1039.3622"
+ id="rect3175-0"
+ style="fill:#cacaca;fill-opacity:1;stroke:none" />
+ <rect
+ width="6"
+ height="3"
+ x="13"
+ y="1039.3622"
+ id="rect3177-4"
+ style="fill:#828282;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="19"
+ y="1039.3622"
+ id="rect3181-9"
+ style="fill:#cacaca;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="22"
+ y="1039.3622"
+ id="rect3183-7"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1039.3622"
+ id="rect3185-6"
+ style="fill:#8f8f8f;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1042.3622"
+ id="rect3187-1"
+ style="fill:#9e9e9e;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1042.3622"
+ id="rect3203-8"
+ style="fill:#919191;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1045.3622"
+ id="rect3205-4"
+ style="fill:#828282;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1045.3622"
+ id="rect3207-2"
+ style="fill:#7f7f7f;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1045.3622"
+ id="rect3209-3"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="10"
+ y="1045.3622"
+ id="rect3211-3"
+ style="fill:#858585;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="13"
+ y="1045.3622"
+ id="rect3213-9"
+ style="fill:#858585;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="16"
+ y="1045.3622"
+ id="rect3215-7"
+ style="fill:#919191;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="19"
+ y="1045.3622"
+ id="rect3217-0"
+ style="fill:#858585;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="22"
+ y="1045.3622"
+ id="rect3219-4"
+ style="fill:#949494;fill-opacity:1;stroke:none" />
+ <rect
+ width="3"
+ height="3"
+ x="25"
+ y="1045.3622"
+ id="rect3221-5"
+ style="fill:#888888;fill-opacity:1;stroke:none" />
+ <path
+ d="m 4.0000007,1023.3622 -1,1 0,24 1,1 24.0000003,0 1,-1 0,-24 -1,-1 z m 0,1 24.0000003,0 0,24 -24.0000003,0 z"
+ id="rect3223-4"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <rect
+ width="4"
+ height="3"
+ x="7.9999995"
+ y="1036.3622"
+ id="rect4124-4"
+ style="fill:#008080;fill-opacity:1;stroke:none;filter:url(#filter5727)" />
+ <rect
+ width="2"
+ height="1"
+ x="9"
+ y="1037.3622"
+ id="rect4084-3"
+ style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5711)" />
+ <rect
+ width="4"
+ height="3"
+ x="20"
+ y="1036.3622"
+ id="rect4124-9-2"
+ style="fill:#008080;fill-opacity:1;stroke:none;filter:url(#filter5723)" />
+ <rect
+ width="2"
+ height="1"
+ x="21"
+ y="1037.3622"
+ id="rect4086-7"
+ style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5719)" />
+ </g>
+</svg>
diff --git a/resources/sources/skeleton.svg b/resources/sources/skeleton.svg
new file mode 100644
index 00000000..5d55f272
--- /dev/null
+++ b/resources/sources/skeleton.svg
@@ -0,0 +1,610 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="skeleton.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/skeleton128.png"
+ inkscape:export-xdpi="360"
+ inkscape:export-ydpi="360">
+ <defs
+ id="defs4">
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter5723"
+ x="-0.20999999"
+ width="1.42"
+ y="-0.28"
+ height="1.5599999">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.35"
+ id="feGaussianBlur5725" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter5727"
+ x="-0.20999999"
+ width="1.42"
+ y="-0.28"
+ height="1.5599999">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.35"
+ id="feGaussianBlur5729" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4028-6"
+ x="-0.14708571"
+ width="1.2941715"
+ y="-0.25740001"
+ height="1.5148">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.429"
+ id="feGaussianBlur4030-9" />
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ inkscape:collect="always"
+ id="filter4056-6">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.676"
+ id="feGaussianBlur4058-6" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.313708"
+ inkscape:cx="-18.309169"
+ inkscape:cy="22.958832"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1614"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2996"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <rect
+ style="fill:#999999;fill-opacity:1;stroke:none"
+ id="rect4063-2"
+ width="24"
+ height="24"
+ x="3.9999998"
+ y="1024.3622" />
+ <rect
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none;filter:url(#filter4056-6)"
+ id="rect4038-2"
+ width="26"
+ height="26"
+ x="3"
+ y="3"
+ transform="matrix(1.0769231,0,0,1.0769237,-1.2307693,1019.1314)" />
+ <rect
+ y="1041.3622"
+ x="5.9999995"
+ height="5.0000172"
+ width="20"
+ id="rect3189-4"
+ style="fill:#494949;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4028-6)"
+ d="m 6.0000007,1041.3622 20.0000003,0 0,2 -18.0000003,0 0,3 -2,0 z"
+ id="path4034-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="fill:#494949;fill-opacity:1;stroke:none"
+ id="rect3155-9"
+ width="8"
+ height="5.0000172"
+ x="5.9999995"
+ y="1035.3622" />
+ <rect
+ y="1035.3622"
+ x="18"
+ height="5.0000172"
+ width="8"
+ id="rect3163-4"
+ style="fill:#494949;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a1a1a1;fill-opacity:1;stroke:none"
+ id="rect3009-1"
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="6.9999995"
+ height="3"
+ width="3"
+ id="rect3011-9"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none"
+ id="rect3013-0"
+ width="3"
+ height="3"
+ x="10"
+ y="1024.3622" />
+ <rect
+ style="fill:#919191;fill-opacity:1;stroke:none"
+ id="rect3017-9"
+ width="3"
+ height="3"
+ x="13"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="16"
+ height="3"
+ width="3"
+ id="rect3019-6"
+ style="fill:#888888;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#8f8f8f;fill-opacity:1;stroke:none"
+ id="rect3021-8"
+ width="3"
+ height="3"
+ x="19"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="22"
+ height="3"
+ width="3"
+ id="rect3023-6"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#989898;fill-opacity:1;stroke:none"
+ id="rect3025-7"
+ width="3"
+ height="3"
+ x="25"
+ y="1024.3622" />
+ <rect
+ y="1027.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3027-7"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none"
+ id="rect3029-6"
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3031-0"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none"
+ id="rect3033-4"
+ width="3"
+ height="3"
+ x="10"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3035-4"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#c1c1c1;fill-opacity:1;stroke:none"
+ id="rect3037-2"
+ width="3"
+ height="3"
+ x="16"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3039-4"
+ style="fill:#bebebe;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none"
+ id="rect3041-2"
+ width="3"
+ height="3"
+ x="22"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3043-1"
+ style="fill:#919191;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3045-3"
+ style="fill:#9b9b9b;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#c7c7c7;fill-opacity:1;stroke:none"
+ id="rect3047-1"
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3049-5"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#cacaca;fill-opacity:1;stroke:none"
+ id="rect3051-9"
+ width="3"
+ height="3"
+ x="10"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3053-2"
+ style="fill:#d8d8d8;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#cfcfcf;fill-opacity:1;stroke:none"
+ id="rect3055-9"
+ width="3"
+ height="3"
+ x="16"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3057-7"
+ style="fill:#cfcfcf;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#bababa;fill-opacity:1;stroke:none"
+ id="rect3059-2"
+ width="3"
+ height="3"
+ x="22"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3061-7"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ inkscape:connector-curvature="0"
+ id="path4032-3"
+ d="m 6.0000007,1035.3622 8.0000003,0 0,2 -6.0000003,0 0,3 -2,0 z"
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4028-6)" />
+ <rect
+ style="fill:#bababa;fill-opacity:1;stroke:none"
+ id="rect3063-8"
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="6.9999995"
+ height="3"
+ width="3"
+ id="rect3065-6"
+ style="fill:#c4c4c4;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none"
+ id="rect3067-1"
+ width="3"
+ height="3"
+ x="10"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3069-9"
+ style="fill:#bababa;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#c1c1c1;fill-opacity:1;stroke:none"
+ id="rect3071-7"
+ width="3"
+ height="3"
+ x="13"
+ y="1033.3622" />
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4028-6)"
+ d="m 18.000001,1035.3622 8,0 0,2 -6,0 0,3 -2,0 z"
+ id="path4036-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ y="1033.3622"
+ x="16"
+ height="3"
+ width="3"
+ id="rect3073-1"
+ style="fill:#afafaf;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#cacaca;fill-opacity:1;stroke:none"
+ id="rect3075-8"
+ width="3"
+ height="3"
+ x="19"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="22"
+ height="3"
+ width="3"
+ id="rect3077-8"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none"
+ id="rect3079-2"
+ width="3"
+ height="3"
+ x="25"
+ y="1033.3622" />
+ <rect
+ y="1036.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3153-6"
+ style="fill:#c4c4c4;fill-opacity:1;stroke:none" />
+ <rect
+ y="1036.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3159-7"
+ style="fill:#c4c4c4;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#cacaca;fill-opacity:1;stroke:none"
+ id="rect3161-0"
+ width="3"
+ height="3"
+ x="16"
+ y="1036.3622" />
+ <rect
+ y="1036.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3167-3"
+ style="fill:#bababa;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a7a7a7;fill-opacity:1;stroke:none"
+ id="rect3169-4"
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="6.9999995"
+ height="3"
+ width="3"
+ id="rect3171-8"
+ style="fill:#c1c1c1;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none"
+ id="rect3173-5"
+ width="3"
+ height="3"
+ x="10"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3175-0"
+ style="fill:#cacaca;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#828282;fill-opacity:1;stroke:none"
+ id="rect3177-4"
+ width="6"
+ height="3"
+ x="13"
+ y="1039.3622" />
+ <rect
+ style="fill:#cacaca;fill-opacity:1;stroke:none"
+ id="rect3181-9"
+ width="3"
+ height="3"
+ x="19"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="22"
+ height="3"
+ width="3"
+ id="rect3183-7"
+ style="fill:#aaaaaa;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#8f8f8f;fill-opacity:1;stroke:none"
+ id="rect3185-6"
+ width="3"
+ height="3"
+ x="25"
+ y="1039.3622" />
+ <rect
+ style="fill:#9e9e9e;fill-opacity:1;stroke:none"
+ id="rect3187-1"
+ width="3"
+ height="3"
+ x="3.9999998"
+ y="1042.3622" />
+ <rect
+ style="fill:#919191;fill-opacity:1;stroke:none"
+ id="rect3203-8"
+ width="3"
+ height="3"
+ x="25"
+ y="1042.3622" />
+ <rect
+ y="1045.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3205-4"
+ style="fill:#828282;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#7f7f7f;fill-opacity:1;stroke:none"
+ id="rect3207-2"
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3209-3"
+ style="fill:#a3a3a3;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#858585;fill-opacity:1;stroke:none"
+ id="rect3211-3"
+ width="3"
+ height="3"
+ x="10"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3213-9"
+ style="fill:#858585;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#919191;fill-opacity:1;stroke:none"
+ id="rect3215-7"
+ width="3"
+ height="3"
+ x="16"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3217-0"
+ style="fill:#858585;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#949494;fill-opacity:1;stroke:none"
+ id="rect3219-4"
+ width="3"
+ height="3"
+ x="22"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3221-5"
+ style="fill:#888888;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#333333;fill-opacity:1;stroke:none"
+ d="m 4.0000007,1023.3622 -1,1 0,24 1,1 24.0000003,0 1,-1 0,-24 -1,-1 z m 0,1 24.0000003,0 0,24 -24.0000003,0 z"
+ id="rect3223-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccc" />
+ <rect
+ style="fill:#008080;fill-opacity:1;stroke:none;filter:url(#filter5727)"
+ id="rect4124-4"
+ width="4"
+ height="3"
+ x="7.9999995"
+ y="1036.3622" />
+ <rect
+ style="fill:#00ffff;fill-opacity:1;stroke:none;"
+ id="rect4084-3"
+ width="2"
+ height="1"
+ x="9"
+ y="1037.3622" />
+ <rect
+ style="fill:#008080;fill-opacity:1;stroke:none;filter:url(#filter5723)"
+ id="rect4124-9-2"
+ width="4"
+ height="3"
+ x="20"
+ y="1036.3622" />
+ <rect
+ style="fill:#00ffff;fill-opacity:1;stroke:none;"
+ id="rect4086-7"
+ width="2"
+ height="1"
+ x="21"
+ y="1037.3622" />
+ </g>
+</svg>
diff --git a/resources/sources/squarecreeper.svg b/resources/sources/squarecreeper.svg
new file mode 100644
index 00000000..a1b0f4d1
--- /dev/null
+++ b/resources/sources/squarecreeper.svg
@@ -0,0 +1,828 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="squarecreeper.svg">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3144">
+ <stop
+ style="stop-color:#ffbe01;stop-opacity:1;"
+ offset="0"
+ id="stop3146" />
+ <stop
+ id="stop3922"
+ offset="0.48206061"
+ style="stop-color:#ff0101;stop-opacity:1" />
+ <stop
+ id="stop3920"
+ offset="0.76562566"
+ style="stop-color:#e70000;stop-opacity:0.45098039;" />
+ <stop
+ style="stop-color:#d40000;stop-opacity:0;"
+ offset="1"
+ id="stop3148" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3917">
+ <stop
+ id="stop3919"
+ offset="0"
+ style="stop-color:#115008;stop-opacity:1;" />
+ <stop
+ style="stop-color:#39c228;stop-opacity:1;"
+ offset="0.49999952"
+ id="stop3921" />
+ <stop
+ id="stop3923"
+ offset="1"
+ style="stop-color:#c7f7c2;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3892">
+ <stop
+ style="stop-color:#1a1a1a;stop-opacity:1;"
+ offset="0"
+ id="stop3894" />
+ <stop
+ style="stop-color:#1a1a1a;stop-opacity:0;"
+ offset="1"
+ id="stop3896" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3871">
+ <stop
+ style="stop-color:#115008;stop-opacity:1;"
+ offset="0"
+ id="stop3873" />
+ <stop
+ id="stop3900"
+ offset="0.5"
+ style="stop-color:#39c228;stop-opacity:1;" />
+ <stop
+ style="stop-color:#c7f7c2;stop-opacity:1;"
+ offset="1"
+ id="stop3875" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3867">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.792"
+ id="feGaussianBlur3869" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3892"
+ id="linearGradient3898"
+ x1="16"
+ y1="31"
+ x2="16"
+ y2="26"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3871"
+ id="linearGradient5605"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,1020.3622)"
+ x1="27"
+ y1="26"
+ x2="5"
+ y2="3" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3871-3"
+ id="linearGradient5605-2"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,1020.3622)"
+ x1="27"
+ y1="26"
+ x2="5"
+ y2="3" />
+ <linearGradient
+ id="linearGradient3871-3">
+ <stop
+ style="stop-color:#115008;stop-opacity:1;"
+ offset="0"
+ id="stop3873-5" />
+ <stop
+ id="stop3900-7"
+ offset="0.5"
+ style="stop-color:#39c228;stop-opacity:1;" />
+ <stop
+ style="stop-color:#c7f7c2;stop-opacity:1;"
+ offset="1"
+ id="stop3875-9" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3917"
+ id="linearGradient3147"
+ x1="31"
+ y1="1052.3622"
+ x2="4"
+ y2="1024.3622"
+ gradientUnits="userSpaceOnUse"
+ spreadMethod="reflect" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3144"
+ id="radialGradient3958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.98990735,-2.841494e-6,-1.2215289e-6,0.96907996,-6.585324,13.296415)"
+ cx="10"
+ cy="1030.3622"
+ fx="10"
+ fy="1030.3622"
+ r="3.03125" />
+ <filter
+ inkscape:collect="always"
+ id="filter4012"
+ x="-0.37037037"
+ width="1.7407407"
+ y="-0.37037037"
+ height="1.7407407">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.30864198"
+ id="feGaussianBlur4014" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4206"
+ x="-0.44837625"
+ width="1.8967525"
+ y="-0.44837625"
+ height="1.8967525">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.37364687"
+ id="feGaussianBlur4208" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="218.7892"
+ inkscape:cy="-32.230167"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4186"
+ showgrid="false"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1"
+ inkscape:snap-grids="true"
+ inkscape:snap-to-guides="true"
+ inkscape:snap-nodes="true"
+ inkscape:snap-bbox="false"
+ inkscape:snap-object-midpoints="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <rect
+ style="fill:#2b1100;fill-opacity:1;stroke:none"
+ id="rect3063"
+ width="22"
+ height="22"
+ x="5"
+ y="5"
+ rx="5"
+ ry="5"
+ transform="translate(0,1020.3622)" />
+ <path
+ id="path3065"
+ d="m 6,1026.3622 0,22 22,0 0,-22 z m 2,2 6,0 0,6 6,0 0,-6 6,0 0,6 -6,0 0,3 3,0 0,9 -3,0 0,-3 -6,0 0,3 -3,0 0,-9 3,0 0,-3 -6,0 z"
+ style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3867)"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccc" />
+ <g
+ id="g4186"
+ transform="matrix(7.8663398,0,0,5.253808,-157.10208,-4373.7892)">
+ <rect
+ y="9"
+ x="21"
+ height="2"
+ width="2"
+ id="rect4188"
+ style="opacity:0.25104603;fill:#ff0101;fill-opacity:1;stroke:#4d4d4d;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter4206)"
+ transform="matrix(0.74984559,0,0,1.4646069,5.5088131,1015.3024)" />
+ </g>
+ <path
+ style="fill:url(#linearGradient5605);fill-opacity:1;stroke:none"
+ d="m 7,1022.3622 c -2.77,0 -5,0 -5,0 l 0,30 28,0 c 0,-10 0,-20 0,-30 z m 0,5 6,0 0,6 6,0 0,-6 6,0 0,6 -6,0 0,3 3,0 0,9 -3,0 0,-3 -6,0 0,3 -3,0 0,-9 3,0 0,-3 -6,0 z"
+ id="rect3045"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sccccsccccccccccccccccccccc" />
+ <g
+ id="g5466"
+ style="opacity:0.3"
+ transform="translate(0,-1)">
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3902"
+ d="m 3.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3904"
+ d="m 6.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3906"
+ d="m 3.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3908"
+ d="m 0.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3914"
+ d="m 3.999974,1028.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3916"
+ d="m 0.999974,1028.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3918"
+ d="m 6.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3924"
+ d="m 9.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3926"
+ d="m 12.999974,1028.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3930"
+ d="m 12.999974,1025.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3932"
+ d="m 9.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3936"
+ d="m 12.999974,1022.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect3999"
+ d="m 18.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4007"
+ d="m 15.99998,1031.3622 0,-3 3,0 0,3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4009"
+ d="m 24.99998,1031.3622 0,-3 3,0 0,3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4011"
+ d="m 24.99998,1034.3622 0,-3 3,0 0,3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4013"
+ d="m 21.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4015"
+ d="m 24.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4017"
+ d="m 15.99998,1028.3622 0,-3 3,0 0,3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4019"
+ d="m 21.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4023"
+ d="m 24.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4027"
+ d="m 18.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4029"
+ d="m 15.99998,1025.3622 0,-3 3,0 0,3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4035"
+ d="m 15.99998,1034.3622 0,-3 3,0 0,3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4039"
+ d="m 6.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4041"
+ d="m 6.99998,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4043"
+ d="m 9.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4049"
+ d="m 3.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4053"
+ d="m 12.99998,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4059"
+ d="m 3.99998,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4063"
+ d="m 12.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4069"
+ d="m 6.99998,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4071"
+ d="m 3.99998,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4073"
+ d="m 6.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4075"
+ d="m 3.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4077"
+ d="m 3.99998,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4133"
+ d="m 28,1028.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4143"
+ d="m 28,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4155"
+ d="m 28,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4159"
+ d="m 28,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4161"
+ d="m 28,1025.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4175"
+ d="m 28,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4185"
+ d="m 28,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4197"
+ d="m 28,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4203"
+ d="m 28,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4207"
+ d="m 6.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4211"
+ d="m 9.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4213"
+ d="m 9.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4215"
+ d="m 6.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4217"
+ d="m 3.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4219"
+ d="m 12.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4221"
+ d="m 12.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4245"
+ d="m 3.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4261"
+ d="m 0.99998,1034.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4263"
+ d="m 0.99998,1031.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4267"
+ d="m 0.99998,1037.3622 0,3 3,0 0,-3 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4273"
+ d="m 0.99998,1043.3622 0,3 3,0 0,-3 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4275"
+ d="m 0.99998,1040.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4303"
+ d="m 0.99998,1049.3622 0,3 3,0 0,-3 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4305"
+ d="m 0.99998,1046.3622 0,3 3,0 0,-3 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4093"
+ d="m 12.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4099"
+ d="m 15.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4105"
+ d="m 21.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4109"
+ d="m 21.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4113"
+ d="m 18.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4115"
+ d="m 21.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4117"
+ d="m 21.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4119"
+ d="m 9.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4377"
+ d="m 21.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4379"
+ d="m 18.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4381"
+ d="m 15.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4383"
+ d="m 15.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4387"
+ d="m 18.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4389"
+ d="m 15.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4391"
+ d="m 21.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4393"
+ d="m 21.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4395"
+ d="m 21.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4397"
+ d="m 24.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4399"
+ d="m 27.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4401"
+ d="m 24.999974,1049.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4403"
+ d="m 27.999974,1046.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4405"
+ d="m 24.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4407"
+ d="m 24.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4409"
+ d="m 27.999974,1043.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4411"
+ d="m 27.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4417"
+ d="m 18.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4419"
+ d="m 21.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4433"
+ d="m 21.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4435"
+ d="m 21.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#e6e6e6;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4439"
+ d="m 24.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#999999;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4441"
+ d="m 27.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#f2f2f2;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4443"
+ d="m 24.999974,1040.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4445"
+ d="m 27.999974,1037.3622 3,0 0,3 -3,0 z"
+ style="fill:#cccccc;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4447"
+ d="m 24.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#666666;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4449"
+ d="m 24.999974,1031.3622 3,0 0,3 -3,0 z"
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4451"
+ d="m 27.999974,1034.3622 3,0 0,3 -3,0 z"
+ style="fill:#333333;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4453"
+ d="m 27.999974,1031.3622 3,0 0,3 -3,0 z"
+ style="fill:#ececec;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect4455"
+ d="m 15.999974,1031.3622 3,0 0,3 -3,0 z"
+ style="fill:#808080;fill-opacity:1;stroke:none" />
+ </g>
+ <path
+ style="fill:#000000;stroke:none"
+ d="m 0,1020.3622 c 0,10.0937 0,21.0424 0,32 l 2,0 c 0,-10 0,-20 0,-30 7.6666667,0 28,0 28,0 0,9.4258 0,19.9066 0,30 l 2,0 c 0,-10.6667 0,-21.3333 0,-32 z"
+ id="rect3887"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ style="opacity:0.35;fill:url(#linearGradient3147);fill-opacity:1;stroke:none"
+ d="m 1,1021.3622 c 0,9.7507 0,20.4801 0,31 l 1,0 c 0,-10 0,-20 0,-30 7.6666667,0 28,0 28,0 0,9.4258 0,19.9066 0,30 l 1,0 c 0,-10.3333 0,-20.6667 0,-31 z"
+ id="rect3098"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <rect
+ style="fill:url(#linearGradient3898);fill-opacity:1;stroke:none"
+ id="rect3890"
+ width="32"
+ height="6"
+ x="0"
+ y="26"
+ transform="translate(0,1020.3622)" />
+ <rect
+ y="1008.7997"
+ x="0.24999999"
+ height="6.0625"
+ width="6.0625"
+ id="rect3931"
+ style="fill:url(#radialGradient3958);fill-opacity:1;stroke:#4d4d4d;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#rect3931"
+ id="use3954"
+ transform="translate(-12.8125,-2.374983)"
+ width="32"
+ height="32" />
+ <g
+ id="g4046"
+ style="opacity:1;">
+ <rect
+ transform="matrix(1.5471436,0,0,1.5471436,-12.037158,1014.8908)"
+ style="fill:#ff0101;fill-opacity:1;stroke:#4d4d4d;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter4012)"
+ id="rect4042"
+ width="2"
+ height="2"
+ x="21"
+ y="9" />
+ <rect
+ transform="matrix(0.70378989,0,0,0.70378989,6.5166224,1023.3243)"
+ y="9"
+ x="21"
+ height="2"
+ width="2"
+ id="rect3960"
+ style="fill:#ffc501;fill-opacity:1;stroke:#4d4d4d;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter4012)" />
+ <rect
+ style="fill:#fcff01;fill-opacity:1;stroke:#4d4d4d;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter4012)"
+ id="rect4044"
+ width="2"
+ height="2"
+ x="21"
+ y="9"
+ transform="matrix(0.16522605,0,0,0.16522605,18.365027,1028.7099)" />
+ </g>
+ <use
+ x="0"
+ y="0"
+ xlink:href="#g4046"
+ id="use4096"
+ transform="translate(-12.000001,0)"
+ width="32"
+ height="32" />
+ </g>
+</svg>
diff --git a/resources/sources/status-bad.svg b/resources/sources/status-bad.svg
new file mode 100644
index 00000000..54334e06
--- /dev/null
+++ b/resources/sources/status-bad.svg
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="status-bad.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/enderpearl.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4245">
+ <stop
+ id="stop4247"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ style="stop-color:#106810;stop-opacity:1"
+ offset="0.89412045"
+ id="stop4251" />
+ <stop
+ id="stop4249"
+ offset="1"
+ style="stop-color:#199c19;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4239">
+ <stop
+ id="stop4241"
+ offset="0"
+ style="stop-color:#513000;stop-opacity:1;" />
+ <stop
+ id="stop4243"
+ offset="1"
+ style="stop-color:#eea700;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4030">
+ <stop
+ id="stop4032"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4034"
+ offset="1"
+ style="stop-color:#0c493f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956" />
+ <stop
+ style="stop-color:#199c19;stop-opacity:1;"
+ offset="1"
+ id="stop3958" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3884">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.505"
+ id="feGaussianBlur3886" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3898"
+ x="-0.20110182"
+ width="1.4022036"
+ y="-0.20210065"
+ height="1.4042013">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0604979"
+ id="feGaussianBlur3900" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3916"
+ x="-0.11491533"
+ width="1.2298307"
+ y="-0.11548609"
+ height="1.2309722">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6059988"
+ id="feGaussianBlur3918" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4239"
+ id="radialGradient4024"
+ gradientUnits="userSpaceOnUse"
+ cx="37.15184"
+ cy="33.513309"
+ fx="37.15184"
+ fy="33.513309"
+ r="20"
+ gradientTransform="matrix(-1.2888095,0.63917683,-0.44430494,-0.89587535,99.563439,45.881313)" />
+ <filter
+ inkscape:collect="always"
+ id="filter4110"
+ x="-0.17716113"
+ width="1.3543223"
+ y="-0.17804105"
+ height="1.3560821">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.93424815"
+ id="feGaussianBlur4112" />
+ </filter>
+ <linearGradient
+ id="linearGradient3954-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-4" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-2" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35355339"
+ inkscape:cx="-1038.0652"
+ inkscape:cy="556.75939"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4116"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1612"
+ inkscape:window-height="1026"
+ inkscape:window-x="1677"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="11.25,96.5"
+ id="guide4252" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <g
+ id="g4116"
+ transform="matrix(1.3594572,0,0,1.3553409,-10.875658,-362.67325)">
+ <path
+ transform="matrix(1.1769403,0,0,1.1769403,-6.1232836,982.70009)"
+ d="M 52,32 C 52,43.045695 43.045695,52 32,52 20.954305,52 12,43.045695 12,32 12,20.954305 20.954305,12 32,12 c 11.045695,0 20,8.954305 20,20 z"
+ sodipodi:ry="20"
+ sodipodi:rx="20"
+ sodipodi:cy="32"
+ sodipodi:cx="32"
+ id="path3014"
+ style="fill:url(#radialGradient4024);fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3866"
+ transform="matrix(1.1769403,0,0,1.1769403,-5.6620909,982.68843)"
+ d="m 28,16 c -6.627417,0 -12,5.372583 -12,12 0,0.20181 0.0214,0.394383 0.03125,0.59375 C 20,20 22,22 28.65625,16.03125 28.436252,16.019242 28.222996,16 28,16 z"
+ style="fill:#ffbf00;fill-opacity:1;stroke:none;filter:url(#filter3884)" />
+ <path
+ transform="matrix(1.487885,0,0,1.487885,-10.597031,-493.22036)"
+ style="fill:#ffef00;fill-opacity:1;stroke:none;filter:url(#filter3898)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3888"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3902"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#ffdd00;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ transform="matrix(-1.8359746,0,0,-1.8359746,81.432966,2885.3572)" />
+ <path
+ transform="matrix(-1.1914115,0,0,-1.1914115,69.087304,2235.4229)"
+ style="fill:#f4ecd7;fill-opacity:1;stroke:none;filter:url(#filter3916);opacity:0.7518797"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3920"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="csccsc"
+ inkscape:connector-curvature="0"
+ id="path3964"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 5.570868,-4.4816 8.905966,-4.4575 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4110);opacity:0.69924812"
+ transform="matrix(0.58516297,0,0,0.58516297,6.7391969,416.10681)" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/status-good.svg b/resources/sources/status-good.svg
new file mode 100644
index 00000000..3b311806
--- /dev/null
+++ b/resources/sources/status-good.svg
@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="enderpearl.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/enderpearl.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4245">
+ <stop
+ id="stop4247"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ style="stop-color:#106810;stop-opacity:1"
+ offset="0.89412045"
+ id="stop4251" />
+ <stop
+ id="stop4249"
+ offset="1"
+ style="stop-color:#199c19;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4239">
+ <stop
+ id="stop4241"
+ offset="0"
+ style="stop-color:#043400;stop-opacity:1" />
+ <stop
+ id="stop4243"
+ offset="1"
+ style="stop-color:#199c19;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4030">
+ <stop
+ id="stop4032"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4034"
+ offset="1"
+ style="stop-color:#0c493f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956" />
+ <stop
+ style="stop-color:#199c19;stop-opacity:1;"
+ offset="1"
+ id="stop3958" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3884">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.505"
+ id="feGaussianBlur3886" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3898"
+ x="-0.20110182"
+ width="1.4022036"
+ y="-0.20210065"
+ height="1.4042013">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0604979"
+ id="feGaussianBlur3900" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3916"
+ x="-0.11491533"
+ width="1.2298307"
+ y="-0.11548609"
+ height="1.2309722">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6059988"
+ id="feGaussianBlur3918" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3992">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.44"
+ id="feGaussianBlur3994" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4239"
+ id="radialGradient4024"
+ gradientUnits="userSpaceOnUse"
+ cx="37.15184"
+ cy="33.513309"
+ fx="37.15184"
+ fy="33.513309"
+ r="20"
+ gradientTransform="matrix(-1.2888095,0.63917683,-0.44430494,-0.89587535,99.563439,45.881313)" />
+ <filter
+ inkscape:collect="always"
+ id="filter4082"
+ x="-0.13465964"
+ width="1.2693193"
+ y="-0.12490681"
+ height="1.2498136">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.763901"
+ id="feGaussianBlur4084" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4110"
+ x="-0.17716113"
+ width="1.3543223"
+ y="-0.17804105"
+ height="1.3560821">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.93424815"
+ id="feGaussianBlur4112" />
+ </filter>
+ <linearGradient
+ id="linearGradient3954-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-4" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-2" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4245"
+ id="radialGradient4231"
+ cx="11.428607"
+ cy="52.782928"
+ fx="11.428607"
+ fy="52.782928"
+ r="22.333248"
+ gradientTransform="matrix(0,-0.99787164,0.98982309,1.711883e-6,-20.245763,43.404188)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="8"
+ inkscape:cx="30.78415"
+ inkscape:cy="28.132388"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4116"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1612"
+ inkscape:window-height="1026"
+ inkscape:window-x="1677"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="11.25,96.5"
+ id="guide4252" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <g
+ id="g4116"
+ transform="matrix(1.3594572,0,0,1.3553409,-10.875658,-362.67325)">
+ <path
+ transform="matrix(1.1769403,0,0,1.1769403,-6.1232836,982.70009)"
+ d="M 52,32 C 52,43.045695 43.045695,52 32,52 20.954305,52 12,43.045695 12,32 12,20.954305 20.954305,12 32,12 c 11.045695,0 20,8.954305 20,20 z"
+ sodipodi:ry="20"
+ sodipodi:rx="20"
+ sodipodi:cy="32"
+ sodipodi:cx="32"
+ id="path3014"
+ style="fill:url(#radialGradient4024);fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3866"
+ transform="matrix(1.1769403,0,0,1.1769403,-5.6620909,982.68843)"
+ d="m 28,16 c -6.627417,0 -12,5.372583 -12,12 0,0.20181 0.0214,0.394383 0.03125,0.59375 C 20,20 22,22 28.65625,16.03125 28.436252,16.019242 28.222996,16 28,16 z"
+ style="fill:#08ff00;fill-opacity:1;stroke:none;filter:url(#filter3884)" />
+ <path
+ transform="matrix(1.487885,0,0,1.487885,-10.597031,-493.22036)"
+ style="fill:#14ff00;fill-opacity:1;stroke:none;filter:url(#filter3898)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3888"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3902"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#56ff00;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ transform="matrix(-1.8359746,0,0,-1.8359746,81.432966,2885.3572)" />
+ <path
+ transform="matrix(-1.1914115,0,0,-1.1914115,69.087304,2235.4229)"
+ style="fill:#d7f4d7;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3920"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="csccsc"
+ inkscape:connector-curvature="0"
+ id="path3964"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 5.570868,-4.4816 8.905966,-4.4575 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4110)"
+ transform="matrix(0.58516297,0,0,0.58516297,6.7391969,416.10681)" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/status-terrible.svg b/resources/sources/status-terrible.svg
new file mode 100644
index 00000000..b0de7bfd
--- /dev/null
+++ b/resources/sources/status-terrible.svg
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="status-terrible.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/enderpearl.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient4245">
+ <stop
+ id="stop4247"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ style="stop-color:#106810;stop-opacity:1"
+ offset="0.89412045"
+ id="stop4251" />
+ <stop
+ id="stop4249"
+ offset="1"
+ style="stop-color:#199c19;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4239">
+ <stop
+ id="stop4241"
+ offset="0"
+ style="stop-color:#510000;stop-opacity:1;" />
+ <stop
+ id="stop4243"
+ offset="1"
+ style="stop-color:#ee0000;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4030">
+ <stop
+ id="stop4032"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" />
+ <stop
+ id="stop4034"
+ offset="1"
+ style="stop-color:#0c493f;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3954">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956" />
+ <stop
+ style="stop-color:#199c19;stop-opacity:1;"
+ offset="1"
+ id="stop3958" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3884">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.505"
+ id="feGaussianBlur3886" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3898"
+ x="-0.20110182"
+ width="1.4022036"
+ y="-0.20210065"
+ height="1.4042013">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0604979"
+ id="feGaussianBlur3900" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3916"
+ x="-0.11491533"
+ width="1.2298307"
+ y="-0.11548609"
+ height="1.2309722">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6059988"
+ id="feGaussianBlur3918" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4239"
+ id="radialGradient4024"
+ gradientUnits="userSpaceOnUse"
+ cx="37.15184"
+ cy="33.513309"
+ fx="37.15184"
+ fy="33.513309"
+ r="20"
+ gradientTransform="matrix(-1.2888095,0.63917683,-0.44430494,-0.89587535,99.563439,45.881313)" />
+ <filter
+ inkscape:collect="always"
+ id="filter4110"
+ x="-0.17716113"
+ width="1.3543223"
+ y="-0.17804105"
+ height="1.3560821">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.93424815"
+ id="feGaussianBlur4112" />
+ </filter>
+ <linearGradient
+ id="linearGradient3954-9">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3956-4" />
+ <stop
+ style="stop-color:#0f5f52;stop-opacity:1;"
+ offset="1"
+ id="stop3958-2" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3954"
+ id="radialGradient4250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1486906,1.2798015,-0.74419869,0.66795831,19.056258,-30.328315)"
+ cx="32"
+ cy="32"
+ fx="32"
+ fy="32"
+ r="20" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.5"
+ inkscape:cx="-732.84524"
+ inkscape:cy="332.3527"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4116"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1612"
+ inkscape:window-height="1026"
+ inkscape:window-x="1677"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2985"
+ empspacing="2"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="11.25,96.5"
+ id="guide4252" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-988.36218)">
+ <g
+ id="g4116"
+ transform="matrix(1.3594572,0,0,1.3553409,-10.875658,-362.67325)">
+ <path
+ transform="matrix(1.1769403,0,0,1.1769403,-6.1232836,982.70009)"
+ d="M 52,32 C 52,43.045695 43.045695,52 32,52 20.954305,52 12,43.045695 12,32 12,20.954305 20.954305,12 32,12 c 11.045695,0 20,8.954305 20,20 z"
+ sodipodi:ry="20"
+ sodipodi:rx="20"
+ sodipodi:cy="32"
+ sodipodi:cx="32"
+ id="path3014"
+ style="fill:url(#radialGradient4024);fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3866"
+ transform="matrix(1.1769403,0,0,1.1769403,-5.6620909,982.68843)"
+ d="m 28,16 c -6.627417,0 -12,5.372583 -12,12 0,0.20181 0.0214,0.394383 0.03125,0.59375 C 20,20 22,22 28.65625,16.03125 28.436252,16.019242 28.222996,16 28,16 z"
+ style="fill:#ff8f8f;fill-opacity:1;stroke:none;filter:url(#filter3884)" />
+ <path
+ transform="matrix(1.487885,0,0,1.487885,-10.597031,-493.22036)"
+ style="fill:#ff7878;fill-opacity:1;stroke:none;filter:url(#filter3898)"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3888"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="ssccs"
+ inkscape:connector-curvature="0"
+ id="path3902"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#ff5252;fill-opacity:1;stroke:none;filter:url(#filter3916)"
+ transform="matrix(-1.8359746,0,0,-1.8359746,81.432966,2885.3572)" />
+ <path
+ transform="matrix(-1.1914115,0,0,-1.1914115,69.087304,2235.4229)"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3916);opacity:0.75187970000000004"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 3.96875,-8.5937 5.96875,-6.5937 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ id="path3920"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccs" />
+ <path
+ sodipodi:nodetypes="csccsc"
+ inkscape:connector-curvature="0"
+ id="path3964"
+ d="m 28,1004.3622 c -6.627417,0 -12,5.3726 -12,12 0,0.2018 0.0214,0.3944 0.03125,0.5937 5.570868,-4.4816 8.905966,-4.4575 12.625,-12.5625 -0.219998,-0.012 -0.433254,-0.031 -0.65625,-0.031 z"
+ style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4110);opacity:0.69924812"
+ transform="matrix(0.58516297,0,0,0.58516297,6.7391969,416.10681)" />
+ </g>
+ </g>
+</svg>
diff --git a/resources/sources/steve.svg b/resources/sources/steve.svg
new file mode 100644
index 00000000..2233272c
--- /dev/null
+++ b/resources/sources/steve.svg
@@ -0,0 +1,534 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="steve.svg"
+ inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/steve128.png"
+ inkscape:export-xdpi="360"
+ inkscape:export-ydpi="360">
+ <defs
+ id="defs4">
+ <filter
+ inkscape:collect="always"
+ id="filter3927"
+ x="-0.10864197"
+ width="1.2172839"
+ y="-0.10864198"
+ height="1.217284">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.1769547"
+ id="feGaussianBlur3929" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#484848"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4"
+ inkscape:cx="-15.767253"
+ inkscape:cy="34.171841"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1607"
+ inkscape:window-height="1030"
+ inkscape:window-x="1676"
+ inkscape:window-y="-3"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2996"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1020.3622)">
+ <path
+ style="fill:#002020;fill-opacity:1;stroke:none;filter:url(#filter3927)"
+ d="m 5.6875,1024.4872 c -1,0 -2,1 -2,2 l 0,22 c 0,1 1,2 2,2 l 22,0 c 1,0 2,-1 2,-2 l 0,-22 c 0,-1 -1,-2 -2,-2 z"
+ id="rect3223-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none"
+ d="m 6,1024.3622 20,0 c 1,0 2,1 2,2 l 0,7 -3,0 0,-3 -18,0 0,3 -3,0 0,-7.005 c 0,-0.995 1,-1.995 2,-1.995 z"
+ id="rect3978"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ style="fill:#421d0a;fill-opacity:1;stroke:none"
+ d="m 10,22 3,0 0,-3 6,0 0,3 3,0 0,6 -12,0 z"
+ id="rect3981"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccc"
+ transform="translate(0,1020.3622)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none"
+ id="rect3080"
+ width="8"
+ height="5.0000172"
+ x="6"
+ y="1035.3622" />
+ <rect
+ y="1035.3622"
+ x="18"
+ height="4.9999957"
+ width="8"
+ id="rect3082"
+ style="fill:#ffffff;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#9c694c;fill-opacity:1;stroke:none"
+ d="m 7,10 18,0 0,3 3,0 0,13 c 0,1 -1,2 -2,2 l -4,0 0,-6 -3,0 0,-3 6,0 0,-3 -6,0 0,3 -6,0 0,-3 -6,0 0,3 6,0 0,3 -3,0 0,6 -4,0 C 5,28 4,27 4,26 l 0,-10 0,-3 3,0 z"
+ id="rect3941"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccc"
+ transform="translate(0,1020.3622)" />
+ <rect
+ y="1024.3622"
+ x="6.9999995"
+ height="3"
+ width="3"
+ id="rect3011-9"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#2f1f0f;fill-opacity:1;stroke:none"
+ id="rect3013-0"
+ width="3"
+ height="3"
+ x="10"
+ y="1024.3622" />
+ <rect
+ style="fill:#281c0b;fill-opacity:1;stroke:none"
+ id="rect3017-9"
+ width="3"
+ height="3"
+ x="13"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="16"
+ height="3"
+ width="3"
+ id="rect3019-6"
+ style="fill:#241808;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#261a0a;fill-opacity:1;stroke:none"
+ id="rect3021-8"
+ width="3"
+ height="3"
+ x="19"
+ y="1024.3622" />
+ <rect
+ y="1024.3622"
+ x="22"
+ height="3"
+ width="3"
+ id="rect3023-6"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ y="1027.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3027-7"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none"
+ id="rect3029-6"
+ width="3"
+ height="3"
+ x="6.9999995"
+ y="1027.3622" />
+ <rect
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none"
+ id="rect3033-4"
+ width="3"
+ height="3"
+ x="10"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3035-4"
+ style="fill:#332411;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#422a12;fill-opacity:1;stroke:none"
+ id="rect3037-2"
+ width="3"
+ height="3"
+ x="16"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="19"
+ height="3"
+ width="3"
+ id="rect3039-4"
+ style="fill:#3f2a15;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#2c1e0e;fill-opacity:1;stroke:none"
+ id="rect3041-2"
+ width="3"
+ height="3"
+ x="22"
+ y="1027.3622" />
+ <rect
+ y="1027.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3043-1"
+ style="fill:#281c0b;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="3.9999998"
+ height="3"
+ width="3"
+ id="rect3045-3"
+ style="fill:#2b1e0d;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="25"
+ height="3"
+ width="3"
+ id="rect3061-7"
+ style="fill:#342512;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#8a4c3d;fill-opacity:1;stroke:none"
+ id="rect3089"
+ width="6"
+ height="3"
+ x="13"
+ y="1042.3622" />
+ <g
+ id="g3856"
+ transform="translate(0,1.7382813e-5)">
+ <rect
+ y="1039.3622"
+ x="13"
+ height="3"
+ width="6"
+ id="rect3177-4"
+ style="fill:#6a4030;fill-opacity:1;stroke:none" />
+ <rect
+ y="1042.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3085"
+ style="fill:#41210c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#41210c;fill-opacity:1;stroke:none"
+ id="rect3091"
+ width="3"
+ height="3"
+ x="19"
+ y="1042.3622" />
+ <rect
+ y="1045.3622"
+ x="13"
+ height="3"
+ width="3"
+ id="rect3093"
+ style="fill:#421d0a;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#45220e;fill-opacity:1;stroke:none"
+ id="rect3095"
+ width="3"
+ height="3"
+ x="19"
+ y="1045.3622" />
+ <rect
+ y="1045.3622"
+ x="10"
+ height="3"
+ width="3"
+ id="rect3097"
+ style="fill:#45220e;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#45220e;fill-opacity:1;stroke:none"
+ id="rect3099"
+ width="3"
+ height="3"
+ x="16"
+ y="1045.3622" />
+ </g>
+ <g
+ id="g3944"
+ transform="translate(-28,1.7382813e-5)">
+ <rect
+ y="1030.3622"
+ x="35"
+ height="3"
+ width="3"
+ id="rect3047-1"
+ style="fill:#b6896c;fill-opacity:1;stroke:none" />
+ <rect
+ y="1030.3622"
+ x="38"
+ height="3"
+ width="3"
+ id="rect3051-9"
+ style="fill:#bd8e72;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#c69680;fill-opacity:1;stroke:none"
+ id="rect3053-2"
+ width="3"
+ height="3"
+ x="41"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="44"
+ height="3"
+ width="3"
+ id="rect3055-9"
+ style="fill:#bd8b72;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#bd8e74;fill-opacity:1;stroke:none"
+ id="rect3057-7"
+ width="3"
+ height="3"
+ x="47"
+ y="1030.3622" />
+ <rect
+ y="1030.3622"
+ x="50"
+ height="3"
+ width="3"
+ id="rect3059-2"
+ style="fill:#ac765a;fill-opacity:1;stroke:none" />
+ <rect
+ y="1033.3622"
+ x="32"
+ height="3"
+ width="3"
+ id="rect3063-8"
+ style="fill:#aa7d66;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b4846d;fill-opacity:1;stroke:none"
+ id="rect3065-6"
+ width="3"
+ height="3"
+ x="35"
+ y="1033.3622" />
+ <rect
+ style="fill:#aa7d66;fill-opacity:1;stroke:none"
+ id="rect3069-9"
+ width="3"
+ height="3"
+ x="38"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="41"
+ height="3"
+ width="3"
+ id="rect3071-7"
+ style="fill:#ad806d;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9c725c;fill-opacity:1;stroke:none"
+ id="rect3073-1"
+ width="3"
+ height="3"
+ x="44"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="47"
+ height="3"
+ width="3"
+ id="rect3075-8"
+ style="fill:#bb8972;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#9c694c;fill-opacity:1;stroke:none"
+ id="rect3077-8"
+ width="3"
+ height="3"
+ x="50"
+ y="1033.3622" />
+ <rect
+ y="1033.3622"
+ x="53"
+ height="3"
+ width="3"
+ id="rect3079-2"
+ style="fill:#9c694c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b4846d;fill-opacity:1;stroke:none"
+ id="rect3153-6"
+ width="3"
+ height="3"
+ x="32"
+ y="1036.3622" />
+ <rect
+ style="fill:#b57b67;fill-opacity:1;stroke:none"
+ id="rect3159-7"
+ width="3"
+ height="3"
+ x="41"
+ y="1036.3622" />
+ <rect
+ y="1036.3622"
+ x="44"
+ height="3"
+ width="3"
+ id="rect3161-0"
+ style="fill:#bb8972;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#aa7d66;fill-opacity:1;stroke:none"
+ id="rect3167-3"
+ width="3"
+ height="3"
+ x="53"
+ y="1036.3622" />
+ <rect
+ y="1039.3622"
+ x="32"
+ height="3"
+ width="3"
+ id="rect3169-4"
+ style="fill:#9c6346;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#b37b62;fill-opacity:1;stroke:none"
+ id="rect3171-8"
+ width="3"
+ height="3"
+ x="35"
+ y="1039.3622" />
+ <rect
+ style="fill:#b78272;fill-opacity:1;stroke:none"
+ id="rect3175-0"
+ width="3"
+ height="3"
+ x="38"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="47"
+ height="3"
+ width="3"
+ id="rect3181-9"
+ style="fill:#be886c;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#a26a47;fill-opacity:1;stroke:none"
+ id="rect3183-7"
+ width="3"
+ height="3"
+ x="50"
+ y="1039.3622" />
+ <rect
+ y="1039.3622"
+ x="53"
+ height="3"
+ width="3"
+ id="rect3185-6"
+ style="fill:#805334;fill-opacity:1;stroke:none" />
+ <rect
+ y="1042.3622"
+ x="32"
+ height="3"
+ width="3"
+ id="rect3187-1"
+ style="fill:#905e43;fill-opacity:1;stroke:none" />
+ <rect
+ y="1042.3622"
+ x="53"
+ height="3"
+ width="3"
+ id="rect3203-8"
+ style="fill:#815339;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#6f452c;fill-opacity:1;stroke:none"
+ d="m 32,1045.3622 3,0 0,3 -1,0 c -1,0 -2,-1 -2,-2 z"
+ id="rect3205-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ y="1045.3622"
+ x="35"
+ height="3"
+ width="3"
+ id="rect3207-2"
+ style="fill:#6d432a;fill-opacity:1;stroke:none" />
+ <rect
+ y="1045.3622"
+ x="50"
+ height="3"
+ width="3"
+ id="rect3219-4"
+ style="fill:#83553b;fill-opacity:1;stroke:none" />
+ <path
+ style="fill:#7a4e33;fill-opacity:1;stroke:none"
+ d="m 53,1045.3622 3,0 0,1 c 0,1 -1,2 -2,2 l -1,0 z"
+ id="rect3221-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ style="fill:#965f40;fill-opacity:1;stroke:none"
+ id="rect3083"
+ width="3"
+ height="3"
+ x="35"
+ y="1042.3622" />
+ <rect
+ y="1042.3622"
+ x="50"
+ height="3"
+ width="3"
+ id="rect3101"
+ style="fill:#8f5e3e;fill-opacity:1;stroke:none" />
+ </g>
+ <rect
+ style="fill:#523d89;fill-opacity:1;stroke:none"
+ id="rect3084"
+ width="3"
+ height="3"
+ x="10"
+ y="16"
+ transform="translate(0,1020.3622)" />
+ <rect
+ style="fill:#523d89;fill-opacity:1;stroke:none"
+ id="rect3086"
+ width="3"
+ height="3"
+ x="19"
+ y="16"
+ transform="translate(0,1020.3622)" />
+ </g>
+</svg>
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 00000000..7aeae59f
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,106 @@
+# run the unit tests with `make test`
+find_package(Qt5 COMPONENTS Test Core Network Widgets)
+
+include_directories(${MMC_SRC})
+
+unset(MultiMC_TESTS)
+macro(add_unit_test name)
+ unset(srcs)
+ foreach(arg ${testname} ${ARGN})
+ list(APPEND srcs ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
+ if (WIN32)
+ list(APPEND srcs ${CMAKE_CURRENT_SOURCE_DIR}/test.rc)
+ endif()
+ endforeach()
+ add_executable(tst_${name} ${srcs})
+ qt5_use_modules(tst_${name} Test Core Network Widgets)
+ target_link_libraries(tst_${name} MultiMC_common)
+ list(APPEND MultiMC_TESTS tst_${name})
+ add_test(NAME ${name} COMMAND tst_${name})
+endmacro()
+
+# Tests START #
+
+add_unit_test(pathutils tst_pathutils.cpp)
+add_unit_test(userutils tst_userutils.cpp)
+add_unit_test(UpdateChecker tst_UpdateChecker.cpp)
+add_unit_test(DownloadUpdateTask tst_DownloadUpdateTask.cpp)
+
+# Tests END #
+
+set(COVERAGE_SOURCE_DIRS
+ ${MMC_SRC}/logic/*
+ ${MMC_SRC}/logic/auth/*
+ ${MMC_SRC}/logic/auth/flows/*
+ ${MMC_SRC}/logic/lists/*
+ ${MMC_SRC}/logic/net/*
+ ${MMC_SRC}/logic/tasks/*
+ ${MMC_SRC}/gui/*
+ ${MMC_SRC}/gui/dialogs/*
+ ${MMC_SRC}/gui/widgets/*
+ ${MMC_SRC}/depends/settings/include/*
+ ${MMC_SRC}/depends/settings/src/*
+ ${MMC_SRC}/depends/util/include/*
+ ${MMC_SRC}/depends/util/src/*
+)
+
+if(MultiMC_CODE_COVERAGE)
+ unset(MultiMC_RUN_TESTS)
+ unset(MultiMC_TEST_COVERAGE_FILES)
+
+ foreach(test ${MultiMC_TESTS})
+ add_custom_target(MultiMC_RUN_TEST_${test}
+ COMMAND lcov -d ${CMAKE_CURRENT_BINARY_DIR} -z -q # clean test
+ && lcov -d ${MMC_BIN} -z -q # clean common
+ && lcov -d ${MMC_BIN}/depends/settings/CMakeFiles/libSettings.dir -z -q # clean settings
+ && lcov -d ${MMC_BIN}/depends/utils/CMakeFiles/libUtil.dir -z -q # clean utils
+ && ${MMC_BIN}/${test} -o coverage_${test}.out,xml # run test
+ && lcov -q --checksum -b ${MMC_SRC} -d ${CMAKE_CURRENT_BINARY_DIR} -c -o coverage_${test}.info # generate for test
+ && lcov -q --checksum -b ${MMC_SRC} -d ${MMC_BIN} -c -o coverage_common.info # generate for common
+ && lcov -q --checksum -b ${MMC_SRC} -d ${MMC_BIN}/depends/settings/CMakeFiles/libSettings.dir -c -o coverage_settings.info # generate for settings
+ && lcov -q --checksum -b ${MMC_SRC} -d ${MMC_BIN}/depends/util/CMakeFiles/libUtil.dir -c -o coverage_utils.info # generate for utils
+ && lcov -q --checksum -b ${MMC_SRC} -d .
+ -a coverage_${test}.info -a coverage_common.info -a coverage_settings.info -a coverage_utils.info
+ -o coverage_${test}-combined.info # combine test and common
+ && lcov -q --checksum -b ${MMC_SRC} --list-full-path --extract coverage_${test}-combined.info ${COVERAGE_SOURCE_DIRS} -o coverage_${test}-stripped.info # strip
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ VERBATIM
+ DEPENDS ${test}
+ COMMENT "Running ${test}..."
+ )
+ list(APPEND MultiMC_TEST_COVERAGE_FILES coverage_${test}-stripped.info)
+ list(APPEND MultiMC_RUN_TESTS MultiMC_RUN_TEST_${test})
+ endforeach(test)
+
+ add_custom_target(MultiMC_GENERATE_COVERAGE
+ DEPENDS ${MultiMC_RUN_TESTS}
+ COMMENT "Generating coverage files..."
+ )
+ add_custom_target(MultiMC_GENERATE_COVERAGE_HTML
+ COMMAND genhtml -t "MultiMC 5 Test Coverage" --num-spaces 4 --demangle-cpp --legend -o ${MMC_SRC}/html/coverage ${MultiMC_TEST_COVERAGE_FILES}
+ DEPENDS MultiMC_GENERATE_COVERAGE
+ COMMENT "Generating test coverage html..."
+ )
+ add_custom_target(MultiMC_RUN_TESTS DEPENDS MultiMC_GENERATE_COVERAGE_HTML)
+endif(MultiMC_CODE_COVERAGE)
+
+set(MultiMC_TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
+if(UNIX)
+ # on unix we get the third / from the filename
+ set(MultiMC_TEST_DATA_PATH "file://${MultiMC_TEST_DATA_PATH}")
+else()
+ # we don't on windows, so we have to add it ourselves
+ set(MultiMC_TEST_DATA_PATH "file:///${MultiMC_TEST_DATA_PATH}")
+endif()
+file(GLOB data_files "data/*")
+foreach(data_file ${data_files})
+ get_filename_component(filename ${data_file} NAME)
+ configure_file(
+ ${data_file}
+ ${CMAKE_CURRENT_BINARY_DIR}/data/${filename}
+ @ONLY
+ NEWLINE_STYLE LF
+ )
+endforeach()
+
+configure_file(test_config.h.in test_config.h @ONLY)
diff --git a/tests/TestUtil.h b/tests/TestUtil.h
new file mode 100644
index 00000000..e9099b15
--- /dev/null
+++ b/tests/TestUtil.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <QFile>
+#include <QCoreApplication>
+#include <QTest>
+#include <QDir>
+
+#include "MultiMC.h"
+
+#include "test_config.h"
+
+struct TestsInternal
+{
+ static QByteArray readFile(const QString &fileName)
+ {
+ QFile f(fileName);
+ f.open(QFile::ReadOnly);
+ return f.readAll();
+ }
+ static QString readFileUtf8(const QString &fileName)
+ {
+ return QString::fromUtf8(readFile(fileName));
+ }
+};
+
+#define MULTIMC_GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA( file ))
+#define MULTIMC_GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA( file ))
+
+#ifdef Q_OS_LINUX
+# define _MMC_EXTRA_ARGV , "-platform", "offscreen"
+# define _MMC_EXTRA_ARGC 2
+#else
+# define _MMC_EXTRA_ARGV
+# define _MMC_EXTRA_ARGC 0
+#endif
+
+
+
+#define QTEST_GUILESS_MAIN_MULTIMC(TestObject) \
+int main(int argc, char *argv[]) \
+{ \
+ char *argv_[] = { argv[0] _MMC_EXTRA_ARGV }; \
+ int argc_ = 1 + _MMC_EXTRA_ARGC; \
+ MultiMC app(argc_, argv_, true); \
+ app.setAttribute(Qt::AA_Use96Dpi, true); \
+ TestObject tc; \
+ return QTest::qExec(&tc, argc, argv); \
+}
diff --git a/tests/data/.gitattributes b/tests/data/.gitattributes
new file mode 100644
index 00000000..9ac803f0
--- /dev/null
+++ b/tests/data/.gitattributes
@@ -0,0 +1,2 @@
+* -text -diff
+
diff --git a/tests/data/1.json b/tests/data/1.json
new file mode 100644
index 00000000..0f2d6ced
--- /dev/null
+++ b/tests/data/1.json
@@ -0,0 +1,43 @@
+{
+ "ApiVersion": 0,
+ "Id": 1,
+ "Name": "1.0.1",
+ "Files": [
+ {
+ "Path": "fileOne",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileOneA"
+ }
+ ],
+ "Executable": true,
+ "Perms": 493,
+ "MD5": "9eb84090956c484e32cb6c08455a667b"
+ },
+ {
+ "Path": "fileTwo",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileTwo"
+ }
+ ],
+ "Executable": false,
+ "Perms": 644,
+ "MD5": "38f94f54fa3eb72b0ea836538c10b043"
+ },
+ {
+ "Path": "fileThree",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileThree"
+ }
+ ],
+ "Executable": false,
+ "Perms": "750",
+ "MD5": "f12df554b21e320be6471d7154130e70"
+ }
+ ]
+}
diff --git a/tests/data/2.json b/tests/data/2.json
new file mode 100644
index 00000000..6e2b0d3c
--- /dev/null
+++ b/tests/data/2.json
@@ -0,0 +1,31 @@
+{
+ "ApiVersion": 0,
+ "Id": 1,
+ "Name": "1.0.1",
+ "Files": [
+ {
+ "Path": "fileOne",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileOneB"
+ }
+ ],
+ "Executable": true,
+ "Perms": 493,
+ "MD5": "42915a71277c9016668cce7b82c6b577"
+ },
+ {
+ "Path": "fileTwo",
+ "Sources": [
+ {
+ "SourceType": "http",
+ "Url": "@MultiMC_TEST_DATA_PATH@/fileTwo"
+ }
+ ],
+ "Executable": false,
+ "Perms": 644,
+ "MD5": "38f94f54fa3eb72b0ea836538c10b043"
+ }
+ ]
+}
diff --git a/tests/data/channels.json b/tests/data/channels.json
new file mode 100644
index 00000000..3ad504b0
--- /dev/null
+++ b/tests/data/channels.json
@@ -0,0 +1,23 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "@MultiMC_TEST_DATA_PATH@"
+ },
+ {
+ "id": "stable",
+ "name": "Stable",
+ "description": "It's stable at least",
+ "url": "@MultiMC_TEST_DATA_PATH@"
+ },
+ {
+ "id": "42",
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": "https://dent.me/tea"
+ }
+ ]
+}
diff --git a/tests/data/errorChannels.json b/tests/data/errorChannels.json
new file mode 100644
index 00000000..333cd445
--- /dev/null
+++ b/tests/data/errorChannels.json
@@ -0,0 +1,23 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "http://example.org/stuff"
+ },
+ {
+ "id": "stable",
+ "name": "",
+ "description": "It's stable at least",
+ "url": "ftp://username@host/path/to/stuff"
+ },
+ {
+ "id": "42",
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": ""
+ }
+ ]
+}
diff --git a/tests/data/fileOneA b/tests/data/fileOneA
new file mode 100644
index 00000000..f2e41136
--- /dev/null
+++ b/tests/data/fileOneA
@@ -0,0 +1 @@
+stuff
diff --git a/tests/data/fileOneB b/tests/data/fileOneB
new file mode 100644
index 00000000..f9aba922
--- /dev/null
+++ b/tests/data/fileOneB
@@ -0,0 +1,3 @@
+stuff
+
+more stuff that came in the new version
diff --git a/tests/data/fileThree b/tests/data/fileThree
new file mode 100644
index 00000000..6353ff16
--- /dev/null
+++ b/tests/data/fileThree
@@ -0,0 +1 @@
+this is yet another file
diff --git a/tests/data/fileTwo b/tests/data/fileTwo
new file mode 100644
index 00000000..aad9a93a
--- /dev/null
+++ b/tests/data/fileTwo
@@ -0,0 +1 @@
+some other stuff
diff --git a/tests/data/garbageChannels.json b/tests/data/garbageChannels.json
new file mode 100644
index 00000000..1450fb9c
--- /dev/null
+++ b/tests/data/garbageChannels.json
@@ -0,0 +1,22 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+aa "url": "http://example.org/stuff"
+ },
+a "id": "stable",
+ "name": "Stable",
+ "description": "It's stable at least",
+ "url": "ftp://username@host/path/to/stuff"
+ },
+ {
+ "id": "42"f
+ "name": "The Channel",
+ "description": "This is the channel that is going to answer all of your questions",
+ "url": "https://dent.me/tea"
+ }
+ ]
+}
diff --git a/tests/data/index.json b/tests/data/index.json
new file mode 100644
index 00000000..20ceb9f4
--- /dev/null
+++ b/tests/data/index.json
@@ -0,0 +1,9 @@
+{
+ "ApiVersion": 0,
+ "Versions": [
+ { "Id": 0, "Name": "1.0.0" },
+ { "Id": 1, "Name": "1.0.1" },
+ { "Id": 2, "Name": "1.0.2" },
+ { "Id": 3, "Name": "1.0.3" }
+ ]
+}
diff --git a/tests/data/noChannels.json b/tests/data/noChannels.json
new file mode 100644
index 00000000..bbb2cb70
--- /dev/null
+++ b/tests/data/noChannels.json
@@ -0,0 +1,5 @@
+{
+ "format_version": 0,
+ "channels": [
+ ]
+}
diff --git a/tests/data/oneChannel.json b/tests/data/oneChannel.json
new file mode 100644
index 00000000..84727ac7
--- /dev/null
+++ b/tests/data/oneChannel.json
@@ -0,0 +1,11 @@
+{
+ "format_version": 0,
+ "channels": [
+ {
+ "id": "develop",
+ "name": "Develop",
+ "description": "The channel called \"develop\"",
+ "url": "http://example.org/stuff"
+ }
+ ]
+}
diff --git a/tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml b/tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml
new file mode 100644
index 00000000..09c162ca
--- /dev/null
+++ b/tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml
@@ -0,0 +1,17 @@
+<update version="3">
+ <install>
+ <file>
+ <source>sourceOne</source>
+ <dest>destOne</dest>
+ <mode>0777</mode>
+ </file>
+ <file>
+ <source>MultiMC.exe</source>
+ <dest>M/u/l/t/i/M/C/e/x/e</dest>
+ <mode>0644</mode>
+ </file>
+ </install>
+ <uninstall>
+ <file>toDelete.abc</file>
+ </uninstall>
+</update>
diff --git a/tests/data/tst_userutils-test_createShortcut-unix b/tests/data/tst_userutils-test_createShortcut-unix
new file mode 100755
index 00000000..1ce3a2bd
--- /dev/null
+++ b/tests/data/tst_userutils-test_createShortcut-unix
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Application
+TryExec=asdfDest
+Exec=asdfDest 'arg1' 'arg2'
+Name=asdf
+Icon=
diff --git a/tests/test.manifest b/tests/test.manifest
new file mode 100644
index 00000000..8b4dbb98
--- /dev/null
+++ b/tests/test.manifest
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <assemblyIdentity name="MultiMC.Test.0" type="win32" version="5.0.0.0" />
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
+ </dependentAssembly>
+ </dependency>
+ <description>Custom Minecraft launcher for managing multiple installs.</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!--The ID below indicates app support for Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!--The ID below indicates app support for Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly> \ No newline at end of file
diff --git a/tests/test.rc b/tests/test.rc
new file mode 100644
index 00000000..a288dba6
--- /dev/null
+++ b/tests/test.rc
@@ -0,0 +1,28 @@
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+1 RT_MANIFEST "test.manifest"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 1,0,0,0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "MultiMC Contributors"
+ VALUE "FileDescription", "Testcase"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "ProductName", "MultiMC Testcase"
+ VALUE "ProductVersion", "5"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0000, 0x04b0 // Unicode
+ END
+END
diff --git a/tests/test_config.h.in b/tests/test_config.h.in
new file mode 100644
index 00000000..69dd38e7
--- /dev/null
+++ b/tests/test_config.h.in
@@ -0,0 +1,3 @@
+#pragma once
+
+#define MultiMC_TEST_DATA_PATH "@MultiMC_TEST_DATA_PATH@"
diff --git a/tests/tst_DownloadUpdateTask.cpp b/tests/tst_DownloadUpdateTask.cpp
new file mode 100644
index 00000000..e6784402
--- /dev/null
+++ b/tests/tst_DownloadUpdateTask.cpp
@@ -0,0 +1,273 @@
+#include <QTest>
+#include <QSignalSpy>
+
+#include "TestUtil.h"
+
+#include "logic/updater/DownloadUpdateTask.h"
+#include "logic/updater/UpdateChecker.h"
+#include "depends/util/include/pathutils.h"
+
+DownloadUpdateTask::FileSourceList encodeBaseFile(const char *suffix)
+{
+ auto base = qApp->applicationDirPath();
+ QUrl localFile = QUrl::fromLocalFile(base + suffix);
+ QString localUrlString = localFile.toString(QUrl::FullyEncoded);
+ auto item = DownloadUpdateTask::FileSource("http", localUrlString);
+ return DownloadUpdateTask::FileSourceList({item});
+}
+
+Q_DECLARE_METATYPE(DownloadUpdateTask::VersionFileList)
+Q_DECLARE_METATYPE(DownloadUpdateTask::UpdateOperation)
+
+bool operator==(const DownloadUpdateTask::FileSource &f1,
+ const DownloadUpdateTask::FileSource &f2)
+{
+ return f1.type == f2.type && f1.url == f2.url && f1.compressionType == f2.compressionType;
+}
+bool operator==(const DownloadUpdateTask::VersionFileEntry &v1,
+ const DownloadUpdateTask::VersionFileEntry &v2)
+{
+ return v1.path == v2.path && v1.mode == v2.mode && v1.sources == v2.sources &&
+ v1.md5 == v2.md5;
+}
+bool operator==(const DownloadUpdateTask::UpdateOperation &u1,
+ const DownloadUpdateTask::UpdateOperation &u2)
+{
+ return u1.type == u2.type && u1.file == u2.file && u1.dest == u2.dest && u1.mode == u2.mode;
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::FileSource &f)
+{
+ dbg.nospace() << "FileSource(type=" << f.type << " url=" << f.url
+ << " comp=" << f.compressionType << ")";
+ return dbg.maybeSpace();
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::VersionFileEntry &v)
+{
+ dbg.nospace() << "VersionFileEntry(path=" << v.path << " mode=" << v.mode
+ << " md5=" << v.md5 << " sources=" << v.sources << ")";
+ return dbg.maybeSpace();
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::UpdateOperation::Type &t)
+{
+ switch (t)
+ {
+ case DownloadUpdateTask::UpdateOperation::OP_COPY:
+ dbg << "OP_COPY";
+ break;
+ case DownloadUpdateTask::UpdateOperation::OP_DELETE:
+ dbg << "OP_DELETE";
+ break;
+ case DownloadUpdateTask::UpdateOperation::OP_MOVE:
+ dbg << "OP_MOVE";
+ break;
+ case DownloadUpdateTask::UpdateOperation::OP_CHMOD:
+ dbg << "OP_CHMOD";
+ break;
+ }
+ return dbg.maybeSpace();
+}
+
+QDebug operator<<(QDebug dbg, const DownloadUpdateTask::UpdateOperation &u)
+{
+ dbg.nospace() << "UpdateOperation(type=" << u.type << " file=" << u.file
+ << " dest=" << u.dest << " mode=" << u.mode << ")";
+ return dbg.maybeSpace();
+}
+
+class DownloadUpdateTaskTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+ }
+ void cleanupTestCase()
+ {
+ }
+
+ void test_writeInstallScript()
+ {
+ DownloadUpdateTask task(
+ QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/")).toString(), 0);
+
+ DownloadUpdateTask::UpdateOperationList ops;
+
+ ops << DownloadUpdateTask::UpdateOperation::CopyOp("sourceOne", "destOne", 0777)
+ << DownloadUpdateTask::UpdateOperation::CopyOp("MultiMC.exe", "M/u/l/t/i/M/C/e/x/e")
+ << DownloadUpdateTask::UpdateOperation::DeleteOp("toDelete.abc");
+ auto testFile = "tests/data/tst_DownloadUpdateTask-test_writeInstallScript.xml";
+ const QString script = QDir::temp().absoluteFilePath("MultiMCUpdateScript.xml");
+ QVERIFY(task.writeInstallScript(ops, script));
+ QCOMPARE(TestsInternal::readFileUtf8(script).replace(QRegExp("[\r\n]+"), "\n"),
+ MULTIMC_GET_TEST_FILE_UTF8(testFile).replace(QRegExp("[\r\n]+"), "\n"));
+ }
+
+// DISABLED: fails.
+/*
+ void test_parseVersionInfo_data()
+ {
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<DownloadUpdateTask::VersionFileList>("list");
+ QTest::addColumn<QString>("error");
+ QTest::addColumn<bool>("ret");
+
+ QTest::newRow("one")
+ << MULTIMC_GET_TEST_FILE("tests/data/1.json")
+ << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{"fileOne",
+ 493,
+ encodeBaseFile("/tests/data/fileOneA"),
+ "9eb84090956c484e32cb6c08455a667b"}
+ << DownloadUpdateTask::VersionFileEntry{"fileTwo",
+ 644,
+ encodeBaseFile("/tests/data/fileTwo"),
+ "38f94f54fa3eb72b0ea836538c10b043"}
+ << DownloadUpdateTask::VersionFileEntry{"fileThree",
+ 750,
+ encodeBaseFile("/tests/data/fileThree"),
+ "f12df554b21e320be6471d7154130e70"})
+ << QString() << true;
+ QTest::newRow("two")
+ << MULTIMC_GET_TEST_FILE("tests/data/2.json")
+ << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{"fileOne",
+ 493,
+ encodeBaseFile("/tests/data/fileOneB"),
+ "42915a71277c9016668cce7b82c6b577"}
+ << DownloadUpdateTask::VersionFileEntry{"fileTwo",
+ 644,
+ encodeBaseFile("/tests/data/fileTwo"),
+ "38f94f54fa3eb72b0ea836538c10b043"})
+ << QString() << true;
+ }
+ void test_parseVersionInfo()
+ {
+ QFETCH(QByteArray, data);
+ QFETCH(DownloadUpdateTask::VersionFileList, list);
+ QFETCH(QString, error);
+ QFETCH(bool, ret);
+
+ DownloadUpdateTask::VersionFileList outList;
+ QString outError;
+ bool outRet = DownloadUpdateTask("", 0).parseVersionInfo(data, &outList, &outError);
+ QCOMPARE(outRet, ret);
+ QCOMPARE(outList, list);
+ QCOMPARE(outError, error);
+ }
+*/
+ void test_processFileLists_data()
+ {
+ QTest::addColumn<DownloadUpdateTask *>("downloader");
+ QTest::addColumn<DownloadUpdateTask::VersionFileList>("currentVersion");
+ QTest::addColumn<DownloadUpdateTask::VersionFileList>("newVersion");
+ QTest::addColumn<DownloadUpdateTask::UpdateOperationList>("expectedOperations");
+
+ DownloadUpdateTask *downloader = new DownloadUpdateTask(QString(), -1);
+
+ // update fileOne, keep fileTwo, remove fileThree
+ QTest::newRow("test 1")
+ << downloader << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileOne", 493,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource(
+ "http", "http://host/path/fileOne-1"),
+ "9eb84090956c484e32cb6c08455a667b"}
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileTwo", 644,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource(
+ "http", "http://host/path/fileTwo-1"),
+ "38f94f54fa3eb72b0ea836538c10b043"}
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileThree", 420,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource(
+ "http", "http://host/path/fileThree-1"),
+ "f12df554b21e320be6471d7154130e70"})
+ << (DownloadUpdateTask::VersionFileList()
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileOne", 493,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource("http",
+ "http://host/path/fileOne-2"),
+ "42915a71277c9016668cce7b82c6b577"}
+ << DownloadUpdateTask::VersionFileEntry{
+ "tests/data/fileTwo", 644,
+ DownloadUpdateTask::FileSourceList()
+ << DownloadUpdateTask::FileSource("http",
+ "http://host/path/fileTwo-2"),
+ "38f94f54fa3eb72b0ea836538c10b043"})
+ << (DownloadUpdateTask::UpdateOperationList()
+ << DownloadUpdateTask::UpdateOperation::DeleteOp("tests/data/fileThree")
+ << DownloadUpdateTask::UpdateOperation::CopyOp(
+ PathCombine(downloader->updateFilesDir(),
+ QString("tests/data/fileOne").replace("/", "_")),
+ "tests/data/fileOne", 493));
+ }
+ void test_processFileLists()
+ {
+ QFETCH(DownloadUpdateTask *, downloader);
+ QFETCH(DownloadUpdateTask::VersionFileList, currentVersion);
+ QFETCH(DownloadUpdateTask::VersionFileList, newVersion);
+ QFETCH(DownloadUpdateTask::UpdateOperationList, expectedOperations);
+
+ DownloadUpdateTask::UpdateOperationList operations;
+
+ downloader->processFileLists(new NetJob("Dummy"), currentVersion, newVersion,
+ operations);
+ qDebug() << (operations == expectedOperations);
+ qDebug() << operations;
+ qDebug() << expectedOperations;
+ QCOMPARE(operations, expectedOperations);
+ }
+/*
+ void test_masterTest()
+ {
+ QLOG_INFO() << "#####################";
+ MMC->m_version.build = 1;
+ MMC->m_version.channel = "develop";
+ auto channels =
+ QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/channels.json"));
+ auto root = QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/"));
+ QLOG_DEBUG() << "channels: " << channels;
+ QLOG_DEBUG() << "root: " << root;
+ MMC->updateChecker()->setChannelListUrl(channels.toString());
+ MMC->updateChecker()->setCurrentChannel("develop");
+
+ DownloadUpdateTask task(root.toString(), 2);
+
+ QSignalSpy succeededSpy(&task, SIGNAL(succeeded()));
+
+ task.start();
+
+ QVERIFY(succeededSpy.wait());
+ }
+*/
+ void test_OSXPathFixup()
+ {
+ QString path, pathOrig;
+ bool result;
+ // Proper OSX path
+ pathOrig = path = "MultiMC.app/Foo/Bar/Baz";
+ qDebug() << "Proper OSX path: " << path;
+ result = DownloadUpdateTask::fixPathForOSX(path);
+ QCOMPARE(path, QString("Foo/Bar/Baz"));
+ QCOMPARE(result, true);
+
+ // Bad OSX path
+ pathOrig = path = "translations/klingon.lol";
+ qDebug() << "Bad OSX path: " << path;
+ result = DownloadUpdateTask::fixPathForOSX(path);
+ QCOMPARE(path, pathOrig);
+ QCOMPARE(result, false);
+ }
+};
+
+QTEST_GUILESS_MAIN_MULTIMC(DownloadUpdateTaskTest)
+
+#include "tst_DownloadUpdateTask.moc"
diff --git a/tests/tst_UpdateChecker.cpp b/tests/tst_UpdateChecker.cpp
new file mode 100644
index 00000000..0dcb242f
--- /dev/null
+++ b/tests/tst_UpdateChecker.cpp
@@ -0,0 +1,183 @@
+#include <QTest>
+#include <QSignalSpy>
+
+#include "depends/settings/settingsobject.h"
+#include "depends/settings/setting.h"
+
+#include "TestUtil.h"
+#include "logic/updater/UpdateChecker.h"
+
+Q_DECLARE_METATYPE(UpdateChecker::ChannelListEntry)
+
+bool operator==(const UpdateChecker::ChannelListEntry &e1, const UpdateChecker::ChannelListEntry &e2)
+{
+ return e1.id == e2.id &&
+ e1.name == e2.name &&
+ e1.description == e2.description &&
+ e1.url == e2.url;
+}
+
+QDebug operator<<(QDebug dbg, const UpdateChecker::ChannelListEntry &c)
+{
+ dbg.nospace() << "ChannelListEntry(id=" << c.id << " name=" << c.name << " description=" << c.description << " url=" << c.url << ")";
+ return dbg.maybeSpace();
+}
+
+class ResetSetting
+{
+public:
+ ResetSetting(std::shared_ptr<Setting> setting) : setting(setting), oldValue(setting->get()) {}
+ ~ResetSetting()
+ {
+ setting->set(oldValue);
+ }
+
+ std::shared_ptr<Setting> setting;
+ QVariant oldValue;
+};
+
+class UpdateCheckerTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ static QString findTestDataUrl(const char *file)
+ {
+ return QUrl::fromLocalFile(QFINDTESTDATA(file)).toString();
+ }
+ void tst_ChannelListParsing_data()
+ {
+ QTest::addColumn<QString>("channel");
+ QTest::addColumn<QString>("channelUrl");
+ QTest::addColumn<bool>("hasChannels");
+ QTest::addColumn<bool>("valid");
+ QTest::addColumn<QList<UpdateChecker::ChannelListEntry> >("result");
+
+ QTest::newRow("garbage")
+ << QString()
+ << findTestDataUrl("tests/data/garbageChannels.json")
+ << false
+ << false
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("errors")
+ << QString()
+ << findTestDataUrl("tests/data/errorChannels.json")
+ << false
+ << true
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("no channels")
+ << QString()
+ << findTestDataUrl("tests/data/noChannels.json")
+ << false
+ << true
+ << QList<UpdateChecker::ChannelListEntry>();
+ QTest::newRow("one channel")
+ << QString("develop")
+ << findTestDataUrl("tests/data/oneChannel.json")
+ << true
+ << true
+ << (QList<UpdateChecker::ChannelListEntry>() << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", "http://example.org/stuff"});
+ QTest::newRow("several channels")
+ << QString("develop")
+ << findTestDataUrl("tests/data/channels.json")
+ << true
+ << true
+ << (QList<UpdateChecker::ChannelListEntry>()
+ << UpdateChecker::ChannelListEntry{"develop", "Develop", "The channel called \"develop\"", MultiMC_TEST_DATA_PATH}
+ << UpdateChecker::ChannelListEntry{"stable", "Stable", "It's stable at least", MultiMC_TEST_DATA_PATH}
+ << UpdateChecker::ChannelListEntry{"42", "The Channel", "This is the channel that is going to answer all of your questions", "https://dent.me/tea"});
+ }
+ void tst_ChannelListParsing()
+ {
+ ResetSetting resetUpdateChannel(MMC->settings()->getSetting("UpdateChannel"));
+
+ QFETCH(QString, channel);
+ QFETCH(QString, channelUrl);
+ QFETCH(bool, hasChannels);
+ QFETCH(bool, valid);
+ QFETCH(QList<UpdateChecker::ChannelListEntry>, result);
+
+ MMC->settings()->set("UpdateChannel", channel);
+
+ UpdateChecker checker;
+
+ QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
+ QVERIFY(channelListLoadedSpy.isValid());
+
+ checker.setChannelListUrl(channelUrl);
+
+ checker.updateChanList();
+
+ if (valid)
+ {
+ QVERIFY(channelListLoadedSpy.wait());
+ QCOMPARE(channelListLoadedSpy.size(), 1);
+ }
+ else
+ {
+ channelListLoadedSpy.wait();
+ QCOMPARE(channelListLoadedSpy.size(), 0);
+ }
+
+ QCOMPARE(checker.hasChannels(), hasChannels);
+ QCOMPARE(checker.getChannelList(), result);
+ }
+
+ void tst_UpdateChecking_data()
+ {
+ QTest::addColumn<QString>("channel");
+ QTest::addColumn<QString>("channelUrl");
+ QTest::addColumn<int>("currentBuild");
+ QTest::addColumn<QList<QVariant> >("result");
+
+ QTest::newRow("valid channel")
+ << "develop" << findTestDataUrl("tests/data/channels.json")
+ << 2
+ << (QList<QVariant>() << QString() << "1.0.3" << 3);
+ }
+ void tst_UpdateChecking()
+ {
+ ResetSetting resetUpdateChannel(MMC->settings()->getSetting("UpdateChannel"));
+
+ QFETCH(QString, channel);
+ QFETCH(QString, channelUrl);
+ QFETCH(int, currentBuild);
+ QFETCH(QList<QVariant>, result);
+
+ MMC->settings()->set("UpdateChannel", channel);
+ MMC->m_version.build = currentBuild;
+
+ UpdateChecker checker;
+ checker.setChannelListUrl(channelUrl);
+
+ QSignalSpy updateAvailableSpy(&checker, SIGNAL(updateAvailable(QString,QString,int)));
+ QVERIFY(updateAvailableSpy.isValid());
+ QSignalSpy channelListLoadedSpy(&checker, SIGNAL(channelListLoaded()));
+ QVERIFY(channelListLoadedSpy.isValid());
+
+ checker.updateChanList();
+ QVERIFY(channelListLoadedSpy.wait());
+
+ checker.m_channels[0].url = QUrl::fromLocalFile(QDir::current().absoluteFilePath("tests/data/")).toString();
+
+ checker.checkForUpdate(false);
+
+ QVERIFY(updateAvailableSpy.wait());
+ QList<QVariant> res = result;
+ res[0] = checker.m_channels[0].url;
+ QCOMPARE(updateAvailableSpy.first(), res);
+ }
+};
+
+QTEST_GUILESS_MAIN_MULTIMC(UpdateCheckerTest)
+
+#include "tst_UpdateChecker.moc"
diff --git a/tests/tst_pathutils.cpp b/tests/tst_pathutils.cpp
new file mode 100644
index 00000000..a1310d00
--- /dev/null
+++ b/tests/tst_pathutils.cpp
@@ -0,0 +1,74 @@
+#include <QTest>
+#include "TestUtil.h"
+
+#include "depends/util/include/pathutils.h"
+
+class PathUtilsTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void test_PathCombine1_data()
+ {
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("path1");
+ QTest::addColumn<QString>("path2");
+
+ QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
+ QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
+#if defined(Q_OS_WIN)
+ QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
+ QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
+ QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
+#endif
+ }
+ void test_PathCombine1()
+ {
+ QFETCH(QString, result);
+ QFETCH(QString, path1);
+ QFETCH(QString, path2);
+
+ QCOMPARE(PathCombine(path1, path2), result);
+ }
+
+ void test_PathCombine2_data()
+ {
+ QTest::addColumn<QString>("result");
+ QTest::addColumn<QString>("path1");
+ QTest::addColumn<QString>("path2");
+ QTest::addColumn<QString>("path3");
+
+ QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
+ QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
+ QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
+ QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
+#if defined(Q_OS_WIN)
+ QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
+ QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
+ QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
+ QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
+#endif
+ }
+ void test_PathCombine2()
+ {
+ QFETCH(QString, result);
+ QFETCH(QString, path1);
+ QFETCH(QString, path2);
+ QFETCH(QString, path3);
+
+ QCOMPARE(PathCombine(path1, path2, path3), result);
+ }
+};
+
+QTEST_GUILESS_MAIN_MULTIMC(PathUtilsTest)
+
+#include "tst_pathutils.moc"
diff --git a/tests/tst_userutils.cpp b/tests/tst_userutils.cpp
new file mode 100644
index 00000000..3bc980c0
--- /dev/null
+++ b/tests/tst_userutils.cpp
@@ -0,0 +1,71 @@
+#include <QTest>
+#include <QStandardPaths>
+#include "TestUtil.h"
+
+#include "depends/util/include/userutils.h"
+
+class UserUtilsTest : public QObject
+{
+ Q_OBJECT
+private
+slots:
+ void initTestCase()
+ {
+
+ }
+ void cleanupTestCase()
+ {
+
+ }
+
+ void test_getDesktop()
+ {
+ QCOMPARE(Util::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
+ }
+
+// this is only valid on linux
+// FIXME: implement on windows, OSX, then test.
+#if defined(Q_OS_LINUX)
+ void test_createShortcut_data()
+ {
+ QTest::addColumn<QString>("location");
+ QTest::addColumn<QString>("dest");
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("iconLocation");
+ QTest::addColumn<QByteArray>("result");
+
+ QTest::newRow("unix") << QDir::currentPath()
+ << "asdfDest"
+ << (QStringList() << "arg1" << "arg2")
+ << "asdf"
+ << QString()
+ #if defined(Q_OS_LINUX)
+ << MULTIMC_GET_TEST_FILE("data/tst_userutils-test_createShortcut-unix")
+ #elif defined(Q_OS_WIN)
+ << QByteArray()
+ #endif
+ ;
+ }
+
+ void test_createShortcut()
+ {
+ QFETCH(QString, location);
+ QFETCH(QString, dest);
+ QFETCH(QStringList, args);
+ QFETCH(QString, name);
+ QFETCH(QString, iconLocation);
+ QFETCH(QByteArray, result);
+
+ QVERIFY(Util::createShortCut(location, dest, args, name, iconLocation));
+ QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
+
+ //QDir().remove(location);
+ }
+#endif
+};
+
+
+QTEST_GUILESS_MAIN_MULTIMC(UserUtilsTest)
+
+#include "tst_userutils.moc"
diff --git a/translations/mmc_de.ts b/translations/mmc_de.ts
new file mode 100644
index 00000000..7bca2272
--- /dev/null
+++ b/translations/mmc_de.ts
@@ -0,0 +1,2556 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="vanished">Dialog</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/AboutDialog.ui" line="+89"/>
+ <source>MultiMC</source>
+ <translation>MultiMC</translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>About</source>
+ <translation>Ãœber</translation>
+ </message>
+ <message>
+ <source>MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple installations of Minecraft at once.</source>
+ <translation type="vanished">MultiMC ist ein alternativer Launcher, der das Management von Minecraft vereinfacht, indem er es dir erlaubt, mehrere Installationen von Minecraft zu verwalten.</translation>
+ </message>
+ <message>
+ <location line="-91"/>
+ <source>About MultiMC</source>
+ <translation>Ãœber MultiMC</translation>
+ </message>
+ <message>
+ <location line="+100"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MultiMC ist ein alternativer Launcher, der das Management von Minecraft vereinfacht, indem er es dir erlaubt, mehrere Installationen von Minecraft zu verwalten.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <source>© 2013 MultiMC Contributors</source>
+ <translation>© 2013 MultiMC-Mitwirkende</translation>
+ </message>
+ <message>
+ <location line="+15"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/Forkk/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/MultiMC/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location line="+28"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Andrew Okin &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Petr Mrázek &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Sky &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://www.twitter.com/drayshak&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@drayshak&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:600;&quot;&gt;With thanks to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Orochimarufan &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;TakSuyu &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:taksuyu@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;taksuyu@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Kilobyte &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:stiepen22@gmx.de&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;stiepen22@gmx.de&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Jan (02JanDal) &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:02jandal@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;02jandal@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Robotbrain &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/skylordelros&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@skylordelros&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Rootbear75 &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/rootbear75&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@rootbear75&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt; (build server)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Andrew Okin &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Petr Mrázek &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Sky &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://www.twitter.com/drayshak&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@drayshak&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Ubuntu&apos;; font-size:10pt; font-weight:600;&quot;&gt;Mit dank an&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Orochimarufan &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;TakSuyu &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:taksuyu@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;taksuyu@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Kilobyte &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:stiepen22@gmx.de&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;stiepen22@gmx.de&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Jan (02JanDal) &amp;lt;&lt;/span&gt;&lt;a href=&quot;mailto:02jandal@gmail.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;02jandal@gmail.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Robotbrain &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/skylordelros&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@skylordelros&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Rootbear75 &amp;lt;&lt;/span&gt;&lt;a href=&quot;https://twitter.com/rootbear75&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;@rootbear75&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&amp;gt; (bau server)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+25"/>
+ <source>No Language file loaded.</source>
+ <extracomment>Hey, Translator, feel free to put credit to you here</extracomment>
+ <translation>Deutsche Sprachdatei von Kilobyte (siehe oben). Aktualisiert von xnrand (nsfw auf IRC), Jan und ACGaming.</translation>
+ </message>
+ <message>
+ <location line="+39"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;DejaVu Sans Mono&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012-2014 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;QSLog&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (c) 2010, Razvan Petru&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without modification,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions of source code must retain the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions in binary form must reproduce the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer in the documentation and/or other&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* The name of the contributors may not be used to endorse or promote products&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Group View (instance view)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; /*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 Rafael Fernández López &amp;lt;ereslibre@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 John Tapsell &amp;lt;tapsell@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is free software; you can redistribute it and/or&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * modify it under the terms of the GNU Library General Public&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * License as published by the Free Software Foundation; either&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * version 2 of the License, or (at your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Library General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You should have received a copy of the GNU Library General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * along with this library; see the file COPYING.LIB. If not, write to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Boston, MA 02110-1301, USA.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Pack200&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;The GNU General Public License (GPL)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Version 2, June 1991&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;+ &amp;quot;CLASSPATH&amp;quot; EXCEPTION TO THE GPL&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Certain source files distributed by Oracle America and/or its affiliates are&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;subject to the following clarification and special exception to the GPL, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;only where Oracle has expressly included in the particular source file&apos;s header&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the words &amp;quot;Oracle designates this particular file as subject to the &amp;quot;Classpath&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;exception as provided by Oracle in the LICENSE file that accompanied this code.&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; Linking this library statically or dynamically with other modules is making&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; a combined work based on this library. Thus, the terms and conditions of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the GNU General Public License cover the whole combination.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; As a special exception, the copyright holders of this library give you&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; permission to link this library with independent modules to produce an&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; executable, regardless of the license terms of these independent modules,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; and to copy and distribute the resulting executable under terms of your&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; choice, provided that you also meet, for each linked independent module,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the terms and conditions of the license of that module. An independent&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; module is a module which is not derived from or based on this library. If&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; you modify this library, you may extend this exception to your version of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the library, but you are not obligated to do so. If you do not wish to do&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; so, delete this exception statement from your version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Quazip&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (C) 2005-2011 Sergey A. Tachenov&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is free software; you can redistribute it and/or modify it&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the Free Software Foundation; either version 2 of the License, or (at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is distributed in the hope that it will be useful, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You should have received a copy of the GNU Lesser General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;along with this program; if not, write to the Free Software Foundation,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;See COPYING file for the full LGPL text.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Original ZIP package is copyrighted by Gilles Vollant, see&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;quazip/(un)zip.h files for details, basically it&apos;s zlib license.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;xz-minidec&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;/*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * XZ decompressor&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Authors: Lasse Collin &amp;lt;lasse.collin@tukaani.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Igor Pavlov &amp;lt;http://7-zip.org/&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This file has been put into the public domain.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You can do whatever you want with this file.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;DejaVu Sans Mono&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012-2014 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;QSLog&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (c) 2010, Razvan Petru&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;All rights reserved.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without modification,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;are permitted provided that the following conditions are met:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions of source code must retain the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* Redistributions in binary form must reproduce the above copyright notice, this&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; list of conditions and the following disclaimer in the documentation and/or other&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;* The name of the contributors may not be used to endorse or promote products&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Group View (instance view)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; /*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 Rafael Fernández López &amp;lt;ereslibre@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Copyright (C) 2007 John Tapsell &amp;lt;tapsell@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is free software; you can redistribute it and/or&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * modify it under the terms of the GNU Library General Public&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * License as published by the Free Software Foundation; either&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * version 2 of the License, or (at your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This library is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Library General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You should have received a copy of the GNU Library General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * along with this library; see the file COPYING.LIB. If not, write to&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Boston, MA 02110-1301, USA.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Pack200&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;The GNU General Public License (GPL)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Version 2, June 1991&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;+ &amp;quot;CLASSPATH&amp;quot; EXCEPTION TO THE GPL&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Certain source files distributed by Oracle America and/or its affiliates are&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;subject to the following clarification and special exception to the GPL, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;only where Oracle has expressly included in the particular source file&apos;s header&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the words &amp;quot;Oracle designates this particular file as subject to the &amp;quot;Classpath&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;exception as provided by Oracle in the LICENSE file that accompanied this code.&amp;quot;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; Linking this library statically or dynamically with other modules is making&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; a combined work based on this library. Thus, the terms and conditions of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the GNU General Public License cover the whole combination.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; As a special exception, the copyright holders of this library give you&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; permission to link this library with independent modules to produce an&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; executable, regardless of the license terms of these independent modules,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; and to copy and distribute the resulting executable under terms of your&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; choice, provided that you also meet, for each linked independent module,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the terms and conditions of the license of that module. An independent&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; module is a module which is not derived from or based on this library. If&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; you modify this library, you may extend this exception to your version of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; the library, but you are not obligated to do so. If you do not wish to do&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; so, delete this exception statement from your version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;Quazip&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Copyright (C) 2005-2011 Sergey A. Tachenov&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is free software; you can redistribute it and/or modify it&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;the Free Software Foundation; either version 2 of the License, or (at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;your option) any later version.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This program is distributed in the hope that it will be useful, but&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;General Public License for more details.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You should have received a copy of the GNU Lesser General Public License&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;along with this program; if not, write to the Free Software Foundation,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;See COPYING file for the full LGPL text.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Original ZIP package is copyrighted by Gilles Vollant, see&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;quazip/(un)zip.h files for details, basically it&apos;s zlib license.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:18pt; font-weight:600;&quot;&gt;xz-minidec&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;/*&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * XZ decompressor&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Authors: Lasse Collin &amp;lt;lasse.collin@tukaani.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * Igor Pavlov &amp;lt;http://7-zip.org/&amp;gt;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; *&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * This file has been put into the public domain.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; * You can do whatever you want with this file.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+140"/>
+ <source>Forking/Redistribution</source>
+ <translation>Abspaltung/Weiterverbreitung</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;We keep MultiMC open source because we think it&apos;s important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;Part of the reason for using the Apache license is we don&apos;t want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project&apos;s origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;/span&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt; font-weight:600;&quot;&gt;without&lt;/span&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt; implying that you have our blessing.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:7.8pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;Wir wollen, dass MultiMC Open Source bleibt, da wir glauben, dass es wichtig ist, den Quellcode von einem Projekt wie diesem, einzusehen. Daher verbreiten wir MultiMC unter der Apache-Lizenz&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;Eine der Gründe, warum wir die Apache-Lizenz gewählt haben, ist, dass wir nicht wollen, dass der &amp;quot;MultiMC&amp;quot;-Name beim Weiterverbreiten benutzt wird. Dies bedeutet, dass Leute sich die Zeit nehmen müssen, den Code durchzugehen und alle Referenzen zu &amp;quot;MultiMC&amp;quot;, miteinbegriffen aber nicht begrenzt zu dem &amp;quot;MultiMC&amp;quot;-Logo und den Fenstertiteln (kein *MultiMC-fork* im Titel)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt;Die Apache-Lizenz gibt Ihnen ein angemessenes Recht, den Namen zu benutzen - eine Bemerkung im Ãœber-Dialog und in der Lizenz ist akzeptabel. Es sollte allerdings klar sein, dass das Projekt eine Abspaltung &lt;/span&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt; font-weight:600;&quot;&gt;ohne&lt;/span&gt;&lt;span style=&quot; font-family:&apos;Bitstream Vera Sans&apos;; font-size:11pt;&quot;&gt; unserem Segen ist&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/Forkk/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/Forkk/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/Forkk/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/Forkk/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="-219"/>
+ <source>Credits</source>
+ <translation>Dank an</translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Andrew Okin &amp;lt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Petr Mrázek &amp;lt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Orochimarufan &amp;lt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="obsolete">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Andrew Okin &amp;lt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Petr Mrázek &amp;lt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Orochimarufan &amp;lt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Deutsche Ãœbersetzung: Kilobyte &amp;lt;&lt;a href=&quot;mailto:stiepen22@gmx.de&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;stiepen22@gmx.de&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+53"/>
+ <source>License</source>
+ <translation>Lizenz</translation>
+ </message>
+ <message>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;MultiMC uses bspatch, &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2003-2005 Colin Percival&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;All rights reserved&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Redistribution and use in source and binary forms, with or without&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;modification, are permitted providing that the following conditions&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;are met: &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;1. Redistributions of source code must retain the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; notice, this list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;2. Redistributions in binary form must reproduce the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; notice, this list of conditions and the following disclaimer in the&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; documentation and/or other materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS&apos;&apos; AND ANY EXPRESS OR&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="obsolete">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;MultiMC uses bspatch, &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2003-2005 Colin Percival&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;All rights reserved&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Redistribution and use in source and binary forms, with or without&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;modification, are permitted providing that the following conditions&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;are met: &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;1. Redistributions of source code must retain the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; notice, this list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;2. Redistributions in binary form must reproduce the above copyright&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; notice, this list of conditions and the following disclaimer in the&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; documentation and/or other materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS&apos;&apos; AND ANY EXPRESS OR&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+193"/>
+ <source>About Qt</source>
+ <translation>Ãœber Qt</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Close</source>
+ <translation>Schließen</translation>
+ </message>
+</context>
+<context>
+ <name>AccountListDialog</name>
+ <message>
+ <location filename="../gui/dialogs/AccountListDialog.ui" line="+14"/>
+ <source>Manage Accounts</source>
+ <translation>Kontoverwaltung</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome! If you&apos;re new here, you can click the &amp;quot;Add&amp;quot; button to add your Mojang or Minecraft account.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Willkommen! Solltest du neu sein, kannst du die &amp;quot;Hinzufügen&amp;quot;-Schaltfläche drucken um dein Mojang- oder Minecraft-Konto hinzuzufügen&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>&amp;Add</source>
+ <translation>&amp;Hinzufügen</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>&amp;Remove</source>
+ <translation>&amp;Entfernen</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set the currently selected account as the active account. The active account is the account that is used to log in (unless it is overridden in an instance-specific setting).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Mache das ausgewählten Konto zum voreingestellten Konto. Das voreingestellte Konto ist das Konto das zum Einloggen benutzt wird (es sei denn es wird von einer Instanz-spezifischen Einstellung überschrieben).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&amp;Set Default</source>
+ <translation>&amp;Benutze als Voreinstellung</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Set no default account. This will cause MultiMC to prompt you to select an account every time you launch an instance that doesn&apos;t have its own default set.</source>
+ <translation>Mache die Voreinstellung rückgängig. Wenn kein Konto voreingestellt ist, wird MultiMC dich bei jedem Start einer Instanz fragen, welches Konto benutzt werden soll, es sei denn, die Instanz hat ein instanzspezifisches Konto eingestellt.</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&amp;No Default</source>
+ <translation>&amp;Keine Voreinstellung</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/AccountListDialog.cpp" line="+71"/>
+ <source>Please enter your Mojang or Minecraft account username and password to add your account.</source>
+ <translation>Bitte gib Benutzernamen und Passwort deines Mojang- oder Minecraft-Kontos an, um es hinzuzufügen.</translation>
+ </message>
+ <message>
+ <location line="+84"/>
+ <source>Login error.</source>
+ <translation>Loginfehler.</translation>
+ </message>
+</context>
+<context>
+ <name>AccountSelectDialog</name>
+ <message>
+ <location filename="../gui/dialogs/AccountSelectDialog.ui" line="+14"/>
+ <source>Select an Account</source>
+ <translation>Wähle ein Konto</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Select an account.</source>
+ <translation>Wähle ein Konto.</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Use as default?</source>
+ <translation>Als Voreinstellung benutzen?</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Use as default for this instance only?</source>
+ <translation>Nur für diese Instanz als Voreinstellung benutzen?</translation>
+ </message>
+</context>
+<context>
+ <name>AssetsMigrateTask</name>
+ <message>
+ <location filename="../logic/assets/AssetsMigrateTask.cpp" line="+19"/>
+ <source>Migrating legacy assets...</source>
+ <translation>Migriere bestehende Daten...</translation>
+ </message>
+</context>
+<context>
+ <name>AuthenticateTask</name>
+ <message>
+ <location filename="../logic/auth/flows/AuthenticateTask.cpp" line="+197"/>
+ <source>Authenticating: Sending request...</source>
+ <translation>Authentifizierung: Sende Anfrage...</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Authenticating: Processing response...</source>
+ <translation>Authentifizierung: Bearbeite Antwort...</translation>
+ </message>
+</context>
+<context>
+ <name>ConsoleWindow</name>
+ <message>
+ <location filename="../gui/ConsoleWindow.ui" line="+14"/>
+ <source>MultiMC Console</source>
+ <translation>MultiMC-Konsole</translation>
+ </message>
+ <message>
+ <location line="+34"/>
+ <source>Upload Log</source>
+ <translation>Log hochladen</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>&amp;Kill Minecraft</source>
+ <translation>&amp;Minecraft töten</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>&amp;Close</source>
+ <translation>&amp;Schließen</translation>
+ </message>
+ <message>
+ <source>Kill Minecraft</source>
+ <translation type="vanished">Minecraft töten</translation>
+ </message>
+ <message>
+ <source>Close</source>
+ <translation type="vanished">Schließen</translation>
+ </message>
+ <message>
+ <location filename="../gui/ConsoleWindow.cpp" line="+161"/>
+ <source>Kill Minecraft?</source>
+ <translation>Minecraft töten?</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>This can cause the instance to get corrupted and should only be used if Minecraft is frozen for some reason</source>
+ <translation>Dies kann diese Instanz beschädigen und sollte daher nur genutzt werden, wenn Minecraft eingefroren ist</translation>
+ </message>
+</context>
+<context>
+ <name>CopyInstanceDialog</name>
+ <message>
+ <location filename="../gui/dialogs/CopyInstanceDialog.ui" line="+17"/>
+ <source>Copy Instance</source>
+ <translation>Kopiere Instanz</translation>
+ </message>
+ <message>
+ <location line="+57"/>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+</context>
+<context>
+ <name>DownloadUpdateTask</name>
+ <message>
+ <location filename="../logic/updater/DownloadUpdateTask.cpp" line="+80"/>
+ <source>Finding information about the current version...</source>
+ <translation>Finde Informationen zur benutzten Version...</translation>
+ </message>
+ <message>
+ <location line="+21"/>
+ <source>Loading version information...</source>
+ <translation>Lade Versionsinformationen...</translation>
+ </message>
+ <message>
+ <location line="+50"/>
+ <source>Failed to download version info files.</source>
+ <translation>Laden der Versionsdateien ist fehlgeschlagen.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Reading file list for new version...</source>
+ <translation>Bearbeite die Dateiliste der neuen Version...</translation>
+ </message>
+ <message>
+ <location line="+15"/>
+ <source>Reading file list for current version...</source>
+ <translation>Bearbeite die Dateiliste der benutzten Version...</translation>
+ </message>
+ <message>
+ <location line="+90"/>
+ <source>Failed to process update lists...</source>
+ <translation>Fehler beim Bearbeiten der Updateliste...</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Downloading %1 update files.</source>
+ <translation>%1 Dateien werden heruntergeladen.</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Processing file lists - figuring out how to install the update...</source>
+ <translation>Bearbeite Dateilisten - Rechne aus, wie das Update installiert werden soll...</translation>
+ </message>
+ <message>
+ <location line="+206"/>
+ <source>Failed to write update script file.</source>
+ <translation>Fehler beim Schreiben des Updatescripts.</translation>
+ </message>
+ <message>
+ <location line="+43"/>
+ <source>Failed to download update files.</source>
+ <translation>Fehler beim Herunterladen der Updatedateien.</translation>
+ </message>
+</context>
+<context>
+ <name>EditAccountDialog</name>
+ <message>
+ <location filename="../gui/dialogs/EditAccountDialog.ui" line="+14"/>
+ <source>Edit Account</source>
+ <translation>Bearbeite Konto</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Message label placeholder.</source>
+ <translation>Message label placeholder.</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Email / Username</source>
+ <translation>E-Mail / Benutzername</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Password</source>
+ <translation>Passwort</translation>
+ </message>
+</context>
+<context>
+ <name>EditNotesDialog</name>
+ <message>
+ <location filename="../gui/dialogs/EditNotesDialog.ui" line="+14"/>
+ <source>Edit Notes</source>
+ <translation>Notizen bearbeiten</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/EditNotesDialog.cpp" line="+30"/>
+ <source>Edit notes of %1</source>
+ <translation>Notizen von %1 bearbeiten</translation>
+ </message>
+</context>
+<context>
+ <name>ForgeListLoadTask</name>
+ <message>
+ <location filename="../logic/lists/ForgeVersionList.cpp" line="+161"/>
+ <source>Fetching Forge version lists...</source>
+ <translation>Lade die Forge-Versionslisten...</translation>
+ </message>
+</context>
+<context>
+ <name>IconPickerDialog</name>
+ <message>
+ <location filename="../gui/dialogs/IconPickerDialog.ui" line="+14"/>
+ <source>Pick icon</source>
+ <translation>Symbol auswählen</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/IconPickerDialog.cpp" line="+64"/>
+ <source>Add Icon</source>
+ <translation>Symbol hinzufügen</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Remove Icon</source>
+ <translation>Symbol entfernen</translation>
+ </message>
+ <message>
+ <location line="+37"/>
+ <source>Select Icons</source>
+ <extracomment>The title of the select icons open file dialog</extracomment>
+ <translation>Symbol auswählen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Icons</source>
+ <extracomment>The type of icon files</extracomment>
+ <translation>Symbole</translation>
+ </message>
+</context>
+<context>
+ <name>InstanceSettings</name>
+ <message>
+ <location filename="../gui/dialogs/InstanceSettings.ui" line="+14"/>
+ <source>Instance Settings</source>
+ <translation>Instanzeinstellungen</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Minecraft</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Window Size</source>
+ <translation>Fenstergröße</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Start Minecraft maximized?</source>
+ <translation>Minecraft maximiert starten?</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Window height:</source>
+ <translation>Fensterhöhe:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Window width:</source>
+ <translation>Fensterbreite:</translation>
+ </message>
+ <message>
+ <location line="+44"/>
+ <source>Console Settings</source>
+ <translation>Konsoleneinstellungen</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Show console while the game is running?</source>
+ <translation>Konsole anzeigen, während das Spiel läuft?</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Automatically close console when the game quits?</source>
+ <translation>Konsole automatisch schließen, nachdem das Spiel beendet wurde?</translation>
+ </message>
+ <message>
+ <location line="+159"/>
+ <source>Browse...</source>
+ <translation>Durchsuchen...</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Auto-detect...</source>
+ <translation>Auto-Erkennung...</translation>
+ </message>
+ <message>
+ <source>Account Settings</source>
+ <translation type="vanished">Konteneinstellungen</translation>
+ </message>
+ <message>
+ <source>Login automatically when an instance icon is double clicked?</source>
+ <translation type="vanished">Automatisch einloggen, wenn das Instanzsymbol doppelt gecklickt wurde?</translation>
+ </message>
+ <message>
+ <location line="-142"/>
+ <source>Java</source>
+ <translation>Java</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Memory</source>
+ <translation>Arbeitsspeicher</translation>
+ </message>
+ <message>
+ <location line="+28"/>
+ <source>Minimum memory allocation:</source>
+ <translation>Min. Arbeitsspeicher:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Maximum memory allocation:</source>
+ <translation>Max. Arbeitsspeicher:</translation>
+ </message>
+ <message>
+ <location line="+39"/>
+ <source>PermGen:</source>
+ <translation>PermGen:</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Java Settings</source>
+ <translation>Java-Einstellungen</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Test</source>
+ <translation>Teste</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Java path:</source>
+ <translation>Java-Pfad:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>JVM arguments:</source>
+ <translation>JVM-Argumente:</translation>
+ </message>
+ <message>
+ <source>Auto-detect</source>
+ <translation type="vanished">Automatisch erkennen</translation>
+ </message>
+ <message>
+ <location line="+33"/>
+ <source>Custom Commands</source>
+ <translation>Eigene Befehle</translation>
+ </message>
+ <message>
+ <location line="+15"/>
+ <source>Post-exit command:</source>
+ <translation>Nach-Abschluss-Befehl:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Pre-launch command:</source>
+ <translation>Vor-Start-Befehl:</translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC&apos;s working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</source>
+ <translation>Vor-Start-Befehle werden ausgeführt, bevor die Instanz startet, Nach-Ende-Befehle nachdem die Instanz beendet wurde. Beide werden im Hauptverzeichnis von MultiMC gestartet. Verfügbare Umgebungsvariablen: INST_ID, INST_DIR, INST_NAME.</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/InstanceSettings.cpp" line="+195"/>
+ <source>Select a Java version</source>
+ <translation>Wähle eine Java-Version</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Find Java executable</source>
+ <translation>Java-Programm finden</translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>Java test success</source>
+ <translation>Java-Test erfolgreich abgeschlossen</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Java test failure</source>
+ <translation>Java-Test fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>The specified java binary didn&apos;t work. You should use the auto-detect feature, or set the path to the java executable.</source>
+ <translation>Das ausgewählte Java-Programm hat nicht funktioniert. Du solltest die Auto-Erkennung benutzen, oder den Pfad zum Java-Programm angeben.</translation>
+ </message>
+</context>
+<context>
+ <name>JavaListLoadTask</name>
+ <message>
+ <location filename="../logic/lists/JavaVersionList.cpp" line="+175"/>
+ <source>Detecting Java installations...</source>
+ <translation>Suche nach Java-Installationen...</translation>
+ </message>
+</context>
+<context>
+ <name>LWJGLSelectDialog</name>
+ <message>
+ <location filename="../gui/dialogs/LwjglSelectDialog.ui" line="+14"/>
+ <source>Manage Lwjgl Versions</source>
+ <translation>LWJGL-Versionsverwaltung</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Status label...</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>&amp;Refresh</source>
+ <translation>&amp;Aktualisieren</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/LwjglSelectDialog.cpp" line="+61"/>
+ <source>Loading LWJGL version list...</source>
+ <translation>Lade LWJGL-Versionsliste...</translation>
+ </message>
+</context>
+<context>
+ <name>LegacyModEditDialog</name>
+ <message>
+ <location filename="../gui/dialogs/LegacyModEditDialog.ui" line="+14"/>
+ <source>Edit Mods</source>
+ <translation>Mods bearbeiten</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Jar Mods</source>
+ <translation>Jar-Mods</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <location line="+77"/>
+ <location line="+69"/>
+ <location line="+70"/>
+ <source>&amp;Add</source>
+ <translation>&amp;Hinzufügen</translation>
+ </message>
+ <message>
+ <location line="-209"/>
+ <location line="+77"/>
+ <location line="+69"/>
+ <location line="+70"/>
+ <source>&amp;Remove</source>
+ <translation>&amp;Entfernen</translation>
+ </message>
+ <message>
+ <location line="-209"/>
+ <source>MCForge</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Move &amp;Up</source>
+ <translation>Bewege &amp;nach oben</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Move &amp;Down</source>
+ <translation>Bewege &amp;nach unten</translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <source>Core Mods</source>
+ <translation>Coremods</translation>
+ </message>
+ <message>
+ <location line="+44"/>
+ <location line="+69"/>
+ <location line="+70"/>
+ <source>&amp;View Folder</source>
+ <translation>&amp;Ordner öffnen</translation>
+ </message>
+ <message>
+ <location line="-117"/>
+ <source>Loader Mods</source>
+ <translation>Normale Mods</translation>
+ </message>
+ <message>
+ <location line="+72"/>
+ <source>Texture Packs</source>
+ <translation>Texturenpakete</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/OneSixModEditDialog.cpp" line="+303"/>
+ <location filename="../gui/dialogs/LegacyModEditDialog.cpp" line="+258"/>
+ <source>Select Loader Mods</source>
+ <extracomment>Title of regular mod selection dialog</extracomment>
+ <translation>Mods auswählen</translation>
+ </message>
+ <message>
+ <location line="+27"/>
+ <source>Select Resource Packs</source>
+ <translation>Ressourcenpakete auswählen</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/LegacyModEditDialog.cpp" line="-58"/>
+ <source>Select Core Mods</source>
+ <extracomment>Title of core mod selection dialog</extracomment>
+ <translation>Coremods auswählen</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Select Forge version</source>
+ <translation>Wähle Forge-Version</translation>
+ </message>
+ <message>
+ <location line="+37"/>
+ <source>Select Jar Mods</source>
+ <extracomment>Title of jar mod selection dialog</extracomment>
+ <translation>Jarmods auswählen</translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>Select Texture Packs</source>
+ <extracomment>Title of texture pack selection dialog</extracomment>
+ <translation>Texturenpakete auswählen</translation>
+ </message>
+</context>
+<context>
+ <name>LegacyUpdate</name>
+ <message>
+ <location filename="../logic/LegacyUpdate.cpp" line="+79"/>
+ <source>Downloading new LWJGL...</source>
+ <translation>LWJGL wird heruntergeladen...</translation>
+ </message>
+ <message>
+ <location line="+68"/>
+ <source>Installing new LWJGL...</source>
+ <translation>Neues LWJGL wird installiert...</translation>
+ </message>
+ <message>
+ <location line="+76"/>
+ <source>Installing new LWJGL - extracting </source>
+ <translation>Das neue LWJGL wird installiert - entpacken</translation>
+ </message>
+ <message>
+ <location line="+30"/>
+ <source>Checking for jar updates...</source>
+ <translation>Suche nach Jar-Änderungen...</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Downloading new minecraft.jar ...</source>
+ <translation>Neue minecraft.jar wird heruntergeladen...</translation>
+ </message>
+ <message>
+ <location line="+34"/>
+ <source>Installing mods: Adding </source>
+ <translation>Mod-Installation: Hinzufügen </translation>
+ </message>
+ <message>
+ <location line="+86"/>
+ <source>Installing mods: Backing up minecraft.jar ...</source>
+ <translation>Mod-Installation: Erstellen einer Sicherheitskopie von minecraft.jar...</translation>
+ </message>
+ <message>
+ <location line="+25"/>
+ <source>Installing mods: Opening minecraft.jar ...</source>
+ <translation>Mod-Installation: minecraft.jar wird geöffnet...</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Installing mods: Adding mod files...</source>
+ <translation>Mod-Installation: Mod-Dateien werden hinzugefügt...</translation>
+ </message>
+</context>
+<context>
+ <name>LoginDialog</name>
+ <message>
+ <source>Login</source>
+ <translation type="vanished">Einloggen</translation>
+ </message>
+ <message>
+ <source>Username:</source>
+ <translation type="vanished">Nutzername:</translation>
+ </message>
+ <message>
+ <source>Password:</source>
+ <translation type="vanished">Passwort:</translation>
+ </message>
+ <message>
+ <source>Password</source>
+ <translation type="vanished">Passwort</translation>
+ </message>
+ <message>
+ <source>Forget</source>
+ <translation type="vanished">Vergessen</translation>
+ </message>
+ <message>
+ <source>&amp;Remember Username?</source>
+ <translation type="vanished">&amp;Nutzernamen speichern?</translation>
+ </message>
+ <message>
+ <source>R&amp;emember Password?</source>
+ <translation type="vanished">&amp;Passwort speichern?</translation>
+ </message>
+ <message>
+ <source>Offline Once</source>
+ <extracomment>Use offline mode one time</extracomment>
+ <translation type="vanished">Einmal Offline-Modus verwenden</translation>
+ </message>
+ <message>
+ <source>Name</source>
+ <extracomment>The username during login (placeholder)</extracomment>
+ <translation type="vanished">Name</translation>
+ </message>
+</context>
+<context>
+ <name>LoginTask</name>
+ <message>
+ <source>Logging in...</source>
+ <translation type="vanished">Einloggen...</translation>
+ </message>
+ <message>
+ <source>Failed to parse Minecraft version string.</source>
+ <translation type="obsolete">Konnte Minecraft-Versionsstring nicht parsen.</translation>
+ </message>
+ <message>
+ <source>Invalid username or password.</source>
+ <translation type="vanished">Falscher Nutzername oder Passwort.</translation>
+ </message>
+ <message>
+ <source>Launcher outdated, please update.</source>
+ <translation type="vanished">Veralteter Launcher, bitte lade ein Update herunter.</translation>
+ </message>
+ <message>
+ <source>Login failed: %1</source>
+ <translation type="vanished">Login fehlgeschlagen: %1</translation>
+ </message>
+ <message>
+ <source>The login servers are currently unavailable. Check http://help.mojang.com/ for more info.</source>
+ <translation type="vanished">Derzeit kann auf die Login-Server nicht zugegriffen werden. Für weitere Informationen siehe http://help.mojang.com/.</translation>
+ </message>
+ <message>
+ <source>Login failed: Unknown HTTP error %1 occurred.</source>
+ <translation type="vanished">Login fehlgeschlagen. Unbekannter HTTP-Fehler: %1.</translation>
+ </message>
+ <message>
+ <source>Login canceled.</source>
+ <translation type="vanished">Login abgebrochen.</translation>
+ </message>
+</context>
+<context>
+ <name>MCModInfoFrame</name>
+ <message>
+ <location filename="../gui/widgets/MCModInfoFrame.ui" line="+26"/>
+ <source>Frame</source>
+ <translation>Frame</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <location filename="../gui/widgets/MCModInfoFrame.cpp" line="+54"/>
+ <source>Select a mod to view title and authors...</source>
+ <translation>Wähle eine Modifikation aus, um Titel und Autor(en) zu sehen...</translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <location filename="../gui/widgets/MCModInfoFrame.cpp" line="+1"/>
+ <source>Select a mod to view description...</source>
+ <translation>Wähle eine Modifikation aus, um die Beschreibung zu sehen...</translation>
+ </message>
+ <message>
+ <location filename="../gui/widgets/MCModInfoFrame.cpp" line="-11"/>
+ <source>No description provided in mcmod.info</source>
+ <translation>mcmod.info wurde mit keiner Beschreibung versehen</translation>
+ </message>
+</context>
+<context>
+ <name>MCVListLoadTask</name>
+ <message>
+ <location filename="../logic/lists/MinecraftVersionList.cpp" line="+142"/>
+ <source>Loading instance version list...</source>
+ <translation>Lade Liste von Minecraft-Versionen...</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../gui/MainWindow.ui" line="+14"/>
+ <source>MultiMC 5</source>
+ <translation>MultiMC 5</translation>
+ </message>
+ <message>
+ <location line="+30"/>
+ <source>Main Toolbar</source>
+ <translation>Haupt-Werkzeugleiste</translation>
+ </message>
+ <message>
+ <location line="+41"/>
+ <source>Instance Toolbar</source>
+ <translation>Instanz-Werkzeugleiste</translation>
+ </message>
+ <message>
+ <location line="+40"/>
+ <source>News Toolbar</source>
+ <translation>Nachrichten-Werkzeugleiste</translation>
+ </message>
+ <message>
+ <location line="+34"/>
+ <source>Add Instance</source>
+ <translation>Instanz hinzufügen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <location line="+332"/>
+ <source>Add a new instance.</source>
+ <translation>Neue Instanz erstellen.</translation>
+ </message>
+ <message>
+ <location line="-323"/>
+ <source>View Instance Folder</source>
+ <translation>Instanzordner öffnen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Open the instance folder in a file browser.</source>
+ <translation>Instanzordner im Dateimanager öffnen.</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Refresh</source>
+ <translation>Aktualisieren</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Reload the instance list.</source>
+ <translation>Instanzliste neuladen.</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>View Central Mods Folder</source>
+ <translation>Zenstralen Mod-Ordner öffnen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Open the central mods folder in a file browser.</source>
+ <translation>Zentralen Mod-Ordner in einem Dateimanager öffnen.</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Check for Updates</source>
+ <translation>Auf Updates überprüfen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Check for new updates for MultiMC</source>
+ <translation>Auf Updates für MultiMC prüfen</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <location line="+133"/>
+ <source>Settings</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <location line="-130"/>
+ <location line="+3"/>
+ <source>Change settings.</source>
+ <translation>Einstellungen ändern.</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Report a Bug</source>
+ <translation>Fehler melden</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Open the bug tracker to report a bug with MultiMC.</source>
+ <translation>Fehler-Verfolgung öffnen, um einen Fehler zu melden (Bitte auf Englisch ;)).</translation>
+ </message>
+ <message>
+ <source>News</source>
+ <translation type="vanished">Neuigkeiten</translation>
+ </message>
+ <message>
+ <source>Open the MultiMC dev blog to read news about MultiMC.</source>
+ <translation type="vanished">Den MultiMC-Entwicklerblog öffnen, um Neuigkeiten über MultiMC zu erhalten.</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>More News</source>
+ <translation>Mehr Nachrichten</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>More news...</source>
+ <translation>Mehr Nachrichten...</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Open the MultiMC development blog to read more news about MultiMC.</source>
+ <translation>Öffne den MultiMC-Entwicklerblog, um weitere Neuigkeiten über MultiMC zu erhalten.</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <location line="+6"/>
+ <source>About MultiMC</source>
+ <translation>Ãœber MultiMC</translation>
+ </message>
+ <message>
+ <location line="-3"/>
+ <source>View information about MultiMC.</source>
+ <translation>Informationen über MultiMC anzeigen.</translation>
+ </message>
+ <message>
+ <location line="+11"/>
+ <source>Play</source>
+ <translation>Spielen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Launch the selected instance.</source>
+ <translation>Die ausgewählte Instanz starten.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Instance Name</source>
+ <translation>Instanzname</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Rename the selected instance.</source>
+ <translation>Ausgewählte Instanz umbenennen.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Change Group</source>
+ <translation>Gruppe ändern</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Change the selected instance&apos;s group.</source>
+ <translation>Die Gruppe der ausgewählten Instanz ändern.</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Change Icon</source>
+ <translation>Symbol ändern</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Change the selected instance&apos;s icon.</source>
+ <translation>Das Symbol der ausgewählten Instanz ändern.</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Edit Notes</source>
+ <translation>Notizen bearbeiten</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Edit the notes for the selected instance.</source>
+ <translation>Notizen für die ausgewählte Instanz bearbeiten.</translation>
+ </message>
+ <message>
+ <location line="+11"/>
+ <location line="+3"/>
+ <source>Change settings for the selected instance.</source>
+ <translation>Einstellungen für die ausgewählte Instanz bearbeiten.</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Make Shortcut</source>
+ <translation>Verknüpfung erstellen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Make a shortcut on the desktop for the selected instance.</source>
+ <translation>Erstellt eine Verknüpfung für die ausgewählte Instanz auf dem Desktop.</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Manage Saves</source>
+ <translation>Speicherstände verwalten</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Manage saves for the selected instance.</source>
+ <translation>Die Speicherstände der ausgewählten Instanz verwalten.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Edit Mods</source>
+ <translation>Mods bearbeiten</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Edit the mods for the selected instance.</source>
+ <translation>Die Mods der ausgewähten Instanz bearbeiten.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Change Version</source>
+ <translation>Version ändern</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Change the selected instance&apos;s Minecraft version.</source>
+ <translation>Die Minecraftversion der ausgewählten Instanz ändern.</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Change LWJGL</source>
+ <translation>LWJGL ändern</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Change the version of LWJGL for the selected instance to use.</source>
+ <translation>Die zu benutzende Version von LWJGL für die aktuelle Instanz ändern.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Instance Folder</source>
+ <translation>Instanzordner</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Open the selected instance&apos;s root folder in a file browser.</source>
+ <translation>Das Hauptverzeichnis der aktuellen Instanz im Dateimanager öffnen.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Delete</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+3"/>
+ <source>Delete the selected instance.</source>
+ <translation>Ausgewählte Instanz löschen.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Config Folder</source>
+ <translation>Konfig-Ordner</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Open the instance&apos;s config folder</source>
+ <translation>Den Konfigurationsordner im Dateimanager anzeigen</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Meow</source>
+ <translation>Miau</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600; color:#ff0004;&quot;&gt;Catnarok!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Or just a cat with a ball of yarn?&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;WHO KNOWS?!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;:/icons/instances/tnt&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Copy Instance</source>
+ <translation>Kopiere Instanz</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Copy the selected instance.</source>
+ <translation>Kopiere die ausgewählte Instanz.</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <location filename="../gui/MainWindow.cpp" line="+201"/>
+ <source>Manage Accounts</source>
+ <translation>Verwalte Konten</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Manage your Mojang or Minecraft accounts.</source>
+ <translation>Verwalte deine Mojang- und Minecraft-Konten.</translation>
+ </message>
+ <message>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600; color:#ff0004;&quot;&gt;Catnatok!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Or just a cat with a ball of yarn?&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;WHO KNOWS?!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;:/icons/instances/tnt&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-weight:600; color:#ff0004;&quot;&gt;Catnatok!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;Or just a cat with a ball of yarn?&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;WHO KNOWS?!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;:/icons/instances/tnt&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../gui/MainWindow.cpp" line="-9"/>
+ <source>No instance selected</source>
+ <translation>Keine Instanz ausgewählt</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>Accounts</source>
+ <translation>Konten</translation>
+ </message>
+ <message>
+ <location line="+67"/>
+ <source>No update found.</source>
+ <translation>Keine neue Version gefunden.</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>No MultiMC update was found!
+You are using the latest version.</source>
+ <translation>Es wurde kein Update für MultiMC gefunden!
+Du verwendest bereits die neueste Version.</translation>
+ </message>
+ <message>
+ <location line="+52"/>
+ <source>No accounts added!</source>
+ <translation>Keine Konten angegeben!</translation>
+ </message>
+ <message>
+ <location line="+37"/>
+ <source>No Default Account</source>
+ <translation>Kein voreingestelltes Konto</translation>
+ </message>
+ <message>
+ <location line="+95"/>
+ <source>Loading news...</source>
+ <translation>Nachrichten werden geladen...</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>No news available.</source>
+ <translation>Keine Nachrichten verfügbar.</translation>
+ </message>
+ <message>
+ <location line="+105"/>
+ <location line="+7"/>
+ <location line="+7"/>
+ <location line="+12"/>
+ <location line="+8"/>
+ <location line="+37"/>
+ <location line="+7"/>
+ <location line="+7"/>
+ <location line="+436"/>
+ <source>Error</source>
+ <translation>Fehler</translation>
+ </message>
+ <message>
+ <location line="-486"/>
+ <location line="+487"/>
+ <source>MultiMC cannot download Minecraft or update instances unless you have at least one account added.
+Please add your Mojang or Minecraft account.</source>
+ <translation>MultiMC kann Minecraft nicht herunterladen und keine Instanzen aktualisieren, solange du kein Konto erstellt hast.
+Bitte füge dein Mojang- oder Minecraft-Konto hinzu.</translation>
+ </message>
+ <message>
+ <location line="-390"/>
+ <source>Group name</source>
+ <translation>Gruppenname</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Enter a new group name.</source>
+ <translation>Neuen Gruppennamen eingeben.</translation>
+ </message>
+ <message>
+ <location line="+91"/>
+ <source>CAREFUL</source>
+ <translation>ACHTUNG</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>This is permanent! Are you sure?
+About to delete: </source>
+ <translation>Die Änderung ist dauerhaft! Bist du dir sicher?
+Die folgende Instanz löschen:</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>Instance name</source>
+ <translation>Instanzname</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Enter a new instance name.</source>
+ <translation>Neuen Instanznamen eingeben.</translation>
+ </message>
+ <message>
+ <location line="+89"/>
+ <source>No Accounts</source>
+ <translation>Keine Konten</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>In order to play Minecraft, you must have at least one Mojang or Minecraft account logged in to MultiMC.Would you like to open the account manager to add an account now?</source>
+ <translation>Um Minecraft spielen zu können, musst du mindestens ein Mojang- oder Minecraft-Konto in MultiMC hinterlegen. Möchtest du die Kontoverwaltung öffnen, um ein Konto hinzuzufügen?</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Which account would you like to use?</source>
+ <translation>Welches Konto möchtest du benutzen?</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>Your account is currently not logged in. Please enter your password to log in again.</source>
+ <translation>Dein Konto ist momentan nicht angemeldet. Bitte gib dein Passwort an, um dich anzumelden.</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Play Offline</source>
+ <translation>Offline spielen</translation>
+ </message>
+ <message>
+ <location line="+90"/>
+ <source>Error updating instance</source>
+ <translation>Fehler beim Aktualisieren der Instanz</translation>
+ </message>
+ <message>
+ <location line="+30"/>
+ <source>MultiMC Shortcut</source>
+ <translation>MultiMC-Verknüpfung</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Enter a Shortcut Name.</source>
+ <translation>Verknüpfungsnamen eingeben.</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Not useful</source>
+ <translation>Sinnlos</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>A Dummy Shortcut was created. it will not do anything productive</source>
+ <translation>Eine Dummy-Verknüpfung wurde erstellt. Sie wird jedoch absolut nichts bewirken</translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source>Change Minecraft version</source>
+ <translation>Minecraft-Version ändern</translation>
+ </message>
+ <message>
+ <location line="+18"/>
+ <source>Are you sure?</source>
+ <translation>Bist du sicher?</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>This will remove any library/version customization you did previously. This includes things like Forge install and similar.</source>
+ <translation>Dies wird sämtliche Bibliotheken-/Versions-Anpassungen, die du vorgenommen hast, entfernen. Dies schließt Dinge wie Forge mit ein.</translation>
+ </message>
+ <message>
+ <location line="+40"/>
+ <source>Instance settings</source>
+ <translation>Instanzeinstellungen</translation>
+ </message>
+ <message>
+ <location line="+38"/>
+ <source>Rename Instance</source>
+ <translation>Instanz umbenennen</translation>
+ </message>
+ <message>
+ <location line="+77"/>
+ <source>Select a Java version</source>
+ <translation>Wähle eine Java-Version</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Invalid version selected</source>
+ <translation>Ungültige Version ausgewählt</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>You didn&apos;t select a valid Java version, so MultiMC will select the default. You can change this in the settings dialog.</source>
+ <translation>Du hast keine gültige Java-Version ausgewählt, daher wird MultiMC die Voreinstellung benutzen. Du kannst dies in den Einstellungen ändern.</translation>
+ </message>
+</context>
+<context>
+ <name>MinecraftProcess</name>
+ <message>
+ <location filename="../logic/MinecraftProcess.cpp" line="+139"/>
+ <source>Minecraft exited with exitcode %1.</source>
+ <extracomment>Message displayed on instance exit</extracomment>
+ <translation>Minecraft wurde mit Status %1 beendet.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Minecraft crashed with exitcode %1.</source>
+ <extracomment>Message displayed on instance crashed</extracomment>
+ <translation>Minecraft ist mit dem Status %1 abgestürzt</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Minecraft was killed by user.</source>
+ <extracomment>Message displayed after the instance exits due to kill request</extracomment>
+ <translation>Minecraft wurde durch den Nutzer getötet.</translation>
+ </message>
+ <message>
+ <location line="+52"/>
+ <source>Could not launch minecraft!</source>
+ <extracomment>Error message displayed if instace can&apos;t start</extracomment>
+ <translation>Konnte Minecraft nicht starten!</translation>
+ </message>
+</context>
+<context>
+ <name>MultiMC</name>
+ <message>
+ <source>display this help and exit.</source>
+ <translation type="obsolete">Zeigt diese Hilfe und beendet das Programm.</translation>
+ </message>
+ <message>
+ <source>display program version and exit.</source>
+ <translation type="obsolete">Zeigt die Programmversion an und beendet das Programm.</translation>
+ </message>
+ <message>
+ <source>use the supplied directory as MultiMC root instead of the binary location (use &apos;.&apos; for current)</source>
+ <translation type="obsolete">Benutze das angegebene Verzeichnis als Hauptverzeichnis anstelle des Speicherorts. (Benutze &apos;.&apos;, um das aktuelle Verzeichnis zu verwenden)</translation>
+ </message>
+ <message>
+ <source>replaces the given file with the running executable</source>
+ <translation type="obsolete">Ersetzt die angegebene Datei mit der laufenden Anwendung</translation>
+ </message>
+ <message>
+ <source>&lt;path&gt;</source>
+ <translation type="obsolete">&lt;Pfad&gt;</translation>
+ </message>
+ <message>
+ <source>doesn&apos;t restart MultiMC after installing updates</source>
+ <translation type="obsolete">MultiMC nach dem Update nicht neustarten</translation>
+ </message>
+ <message>
+ <source>tries to launch the given instance</source>
+ <translation type="obsolete">Versucht, die angegebene Instanz zu starten</translation>
+ </message>
+ <message>
+ <source>&lt;inst&gt;</source>
+ <translation type="obsolete">&lt;Instanz&gt;</translation>
+ </message>
+ <message>
+ <source>CommandLineError: </source>
+ <translation type="obsolete">Kommandozeilenfehler:</translation>
+ </message>
+ <message>
+ <source>Try &apos;%1 -h&apos; to get help on MultiMC&apos;s command line parameters.</source>
+ <translation type="obsolete">Versuche &apos;%1 -h&apos;, um Hilfe zu MultiMCs Kommandozeilenparametern zu bekommen.</translation>
+ </message>
+ <message>
+ <source>Performing MultiMC update: </source>
+ <translation type="obsolete">Führe MultiMC-Update durch: </translation>
+ </message>
+ <message>
+ <source>Loading Instances...</source>
+ <translation type="obsolete">Lade Instanzen...</translation>
+ </message>
+</context>
+<context>
+ <name>NewInstanceDialog</name>
+ <message>
+ <location filename="../gui/dialogs/NewInstanceDialog.ui" line="+17"/>
+ <source>New Instance</source>
+ <translation>Neue Instanz</translation>
+ </message>
+ <message>
+ <location line="+57"/>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source>Version:</source>
+ <translation>Version:</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/NewInstanceDialog.cpp" line="+99"/>
+ <source>Change Minecraft version</source>
+ <translation>Ändere die Minecraft-Version...</translation>
+ </message>
+</context>
+<context>
+ <name>NewsEntry</name>
+ <message>
+ <location filename="../logic/news/NewsEntry.cpp" line="+24"/>
+ <location line="+36"/>
+ <source>Untitled</source>
+ <translation>Unbennant</translation>
+ </message>
+ <message>
+ <location line="-35"/>
+ <location line="+36"/>
+ <source>No content.</source>
+ <translation>Kein Eintrag.</translation>
+ </message>
+ <message>
+ <location line="-34"/>
+ <location line="+36"/>
+ <source>Unknown Author</source>
+ <translation>Unbekannter Autor</translation>
+ </message>
+</context>
+<context>
+ <name>OneSixFTBInstanceForge</name>
+ <message>
+ <location filename="../logic/OneSixFTBInstance.cpp" line="+37"/>
+ <source>Downloading Forge...</source>
+ <translation>Forge wird heruntergeladen...</translation>
+ </message>
+ <message>
+ <location line="+18"/>
+ <source>Installing Forge...</source>
+ <translation>Forge wird installiert...</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Couldn&apos;t load the version config</source>
+ <translation>Fehlschlag beim Laden der Versions-Konfiguration</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>Couldn&apos;t install Forge</source>
+ <translation>Fehler beim Installieren von Forge</translation>
+ </message>
+</context>
+<context>
+ <name>OneSixModEditDialog</name>
+ <message>
+ <source>Dialog</source>
+ <translatorcomment>Am i really responsible for this?</translatorcomment>
+ <translation type="vanished">Edit Mods</translation>
+ </message>
+ <message>
+ <source>Library</source>
+ <translation type="obsolete">Bibliothek</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/OneSixModEditDialog.ui" line="+179"/>
+ <source>Loader Mods</source>
+ <translation>Mods</translation>
+ </message>
+ <message>
+ <location line="-50"/>
+ <location line="+80"/>
+ <location line="+67"/>
+ <source>&amp;Add</source>
+ <translation>&amp;Hinzufügen</translation>
+ </message>
+ <message>
+ <location line="-262"/>
+ <source>Manage Mods</source>
+ <translation>Verwalte Mods</translation>
+ </message>
+ <message>
+ <location line="+19"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Main Class:</source>
+ <translation>Hauptklasse:</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Replace any current custom version with Minecraft Forge</source>
+ <translation>Die aktuelle benutzerdefinierte Version mit Minecraft Forge ersetzen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Install Forge</source>
+ <translation>Forge installieren</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Install LiteLoader</source>
+ <translation>Installiere LiteLoader</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Create an customized copy of the base version</source>
+ <translation>Eine modifizierte Kopie der Version erstellen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Customize</source>
+ <translation>Benutzerdefiniert</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Revert to original base version</source>
+ <translation>Benutzerdefinierte Einstellungen zurücksetzen</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Revert</source>
+ <translation>Zurücksetzen</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Add new libraries</source>
+ <translation>Füge neue Bibliotheken hinzu</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Remove selected libraries</source>
+ <translation>Entferne ausgewählte Bibliotheken</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <location line="+74"/>
+ <location line="+67"/>
+ <source>&amp;Remove</source>
+ <translation>&amp;Entfernen</translation>
+ </message>
+ <message>
+ <location line="-127"/>
+ <source>Open custom.json</source>
+ <translation>Öffne custom.json</translation>
+ </message>
+ <message>
+ <location line="+80"/>
+ <location line="+67"/>
+ <source>&amp;View Folder</source>
+ <translation>&amp;Ordner öffnen</translation>
+ </message>
+ <message>
+ <location line="-45"/>
+ <source>Resource Packs</source>
+ <translation>Ressourcenpakete</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/OneSixModEditDialog.cpp" line="-205"/>
+ <location line="+34"/>
+ <source>Revert?</source>
+ <translation>Zurücksetzen?</translation>
+ </message>
+ <message>
+ <location line="-34"/>
+ <source>Do you want to revert the version of this instance to its original configuration?</source>
+ <translation>Möchtest du wirklich die Version dieser Instanz zurücksetzen?</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>Error</source>
+ <translation>Fehler</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Unable to open custom.json, check the settings</source>
+ <translation>Fehler beim Öffnen der custom.json-Datei, überprüfe deine Einstellungen</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Select Forge version</source>
+ <translation>Wähle Forge-Version</translation>
+ </message>
+ <message>
+ <location line="+8"/>
+ <source>This will revert any changes you did to the version up to this point. Is that OK?</source>
+ <translation>Dies wird alle Änderungen, die du vorgenommen hast, zurücksetzen. Bist du damit einverstanden?</translation>
+ </message>
+ <message>
+ <location line="+69"/>
+ <location line="+15"/>
+ <source>LiteLoader</source>
+ <translation>LiteLoader</translation>
+ </message>
+ <message>
+ <location line="-14"/>
+ <source>There is no information available on how to install LiteLoader into this version of Minecraft</source>
+ <translation>Es gibt momentan keine Informationen zur Installation von LiteLoader für diese Version von Minecraft</translation>
+ </message>
+ <message>
+ <location line="+15"/>
+ <source>For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details.</source>
+ <translation>Aus unbekannten Gründen ist die Installation von LiteLoader fehlgeschlagen. Sieh dir die MultiMC-Logdateien an, um weitere Details zu erhalten.</translation>
+ </message>
+</context>
+<context>
+ <name>OneSixUpdate</name>
+ <message>
+ <location filename="../logic/OneSixUpdate.cpp" line="+60"/>
+ <location line="+32"/>
+ <source>Testing the Java installation...</source>
+ <translation>Java-Installation wird getestet...</translation>
+ </message>
+ <message>
+ <location line="+39"/>
+ <source>Getting the version files from Mojang...</source>
+ <translation>Versionsdateien von Mojang werden heruntergeladen...</translation>
+ </message>
+ <message>
+ <location line="+68"/>
+ <source>Updating assets index...</source>
+ <translation>Datenindex wird aktualisiert...</translation>
+ </message>
+ <message>
+ <location line="+51"/>
+ <source>Getting the assets files from Mojang...</source>
+ <translation>Daten werden von Mojang geholt...</translation>
+ </message>
+ <message>
+ <location line="+34"/>
+ <source>Getting the library files from Mojang...</source>
+ <translation>Bibliotheken werden von Mojang geholt...</translation>
+ </message>
+ <message>
+ <location line="+88"/>
+ <source>Preparing for launch...</source>
+ <translation>Start wird vorbereitet...</translation>
+ </message>
+</context>
+<context>
+ <name>ProgressDialog</name>
+ <message>
+ <location filename="../gui/dialogs/ProgressDialog.ui" line="+26"/>
+ <source>Please wait...</source>
+ <translation>Bitte warten...</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Task Status...</source>
+ <translation>Aufgabenstatus...</translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>Skip</source>
+ <translation>Ãœberspringen</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../logic/NagUtils.cpp" line="+26"/>
+ <source>JVM arguments warning</source>
+ <translation>JVM-Argument-Warnung</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>You tried to manually set a JVM memory option (using &quot;-XX:PermSize&quot;, &quot;-Xmx&quot; or &quot;-Xms&quot;) - there are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).
+Your manual settings will be overridden by the dedicated options.
+This message will be displayed until you remove them from the JVM arguments.</source>
+ <translation>Du hast versucht, eine JVM-Arbeitsspeicheroption manuell anzugeben (&quot;-XX:PermSize&quot;, &quot;-Xmx&quot; oder &quot;-Xms&quot;) - es gibt hierfür vorgesehene Felder in den Einstellungen (Java-Reiter, im Arbeitsspeicher-Bereich oben).
+Deine manuellen Einstellungen werden von den vorgesehenen überschrieben.
+Diese Mitteilung wird so lange angezeigt, bis du die Option entfernt hast.</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/ModEditDialogCommon.cpp" line="+53"/>
+ <source>How sad!</source>
+ <translation>Wie schade!</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>The mod author didn&apos;t provide a website link for this mod.</source>
+ <translation>Der Autor der Modifikation hat keine URL hinterlegt.</translation>
+ </message>
+</context>
+<context>
+ <name>RefreshTask</name>
+ <message>
+ <location filename="../logic/auth/flows/RefreshTask.cpp" line="+148"/>
+ <source>Refreshing login token...</source>
+ <translation>Erneuerung des Login-Tokens...</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Refreshing login token: Processing response...</source>
+ <translation>Erneuerung des Login-Tokens: Verarbeite Antwort...</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="../gui/dialogs/SettingsDialog.ui" line="+20"/>
+ <source>Settings</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>General</source>
+ <translation>Allgemein</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Sorting Mode</source>
+ <translation>Sortiermodus</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>By last launched</source>
+ <translation>Nach letzem Start</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>By name</source>
+ <translation>Nach Namen</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Update Settings</source>
+ <translation>Updateeinstellungen</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Use development builds?</source>
+ <translation>Entwicklungsversionen benutzen?</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Check for updates when MultiMC starts?</source>
+ <translation>Beim Start nach Updates suchen?</translation>
+ </message>
+ <message>
+ <location line="+97"/>
+ <source>Folders</source>
+ <translation>Ordner</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Instances:</source>
+ <translation>Instanzen:</translation>
+ </message>
+ <message>
+ <location line="-69"/>
+ <location line="+46"/>
+ <location line="+33"/>
+ <location line="+20"/>
+ <location line="+14"/>
+ <location line="+17"/>
+ <location line="+26"/>
+ <location line="+289"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location line="-469"/>
+ <source>FTB</source>
+ <translation>FTB</translation>
+ </message>
+ <message>
+ <location line="+31"/>
+ <source>Launcher:</source>
+ <translation>Launcher:</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>Track FTB instances</source>
+ <translation>FTB-Instanzen beobachten</translation>
+ </message>
+ <message>
+ <location line="+32"/>
+ <source>Files:</source>
+ <translation>Dateien:</translation>
+ </message>
+ <message>
+ <location line="+33"/>
+ <source>Mods:</source>
+ <translation>Mods:</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>LWJGL:</source>
+ <translation>LWJGL:</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>Icons:</source>
+ <translation>Symbole:</translation>
+ </message>
+ <message>
+ <location line="+17"/>
+ <source>External Editors (leave empty for system default)</source>
+ <translation>Externe Editor-Anwendungen (leer lassen, um die System-Voreinstellung zu benutzen)</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>JSON Editor:</source>
+ <translation>JSON-Editor:</translation>
+ </message>
+ <message>
+ <location line="+31"/>
+ <source>Minecraft</source>
+ <translation>Minecraft</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Window Size</source>
+ <translation>Fenstergröße</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Start Minecraft maximized?</source>
+ <translation>Minecraft maximiert starten?</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Window height:</source>
+ <translation>Fensterhöhe:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Window width:</source>
+ <translation>Fensterbreite:</translation>
+ </message>
+ <message>
+ <location line="+41"/>
+ <source>Console Settings</source>
+ <translation>Konsoleneinstellungen</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Show console while the game is running?</source>
+ <translation>Konsole anzeigen, während das Spiel läuft?</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Automatically close console when the game quits?</source>
+ <translation>Konsole automatisch schließen, nachdem das Spiel beendet wurde?</translation>
+ </message>
+ <message>
+ <source>Login automatically when an instance icon is double clicked?</source>
+ <translation type="vanished">Automatisch einloggen, wenn das Instanzsymbol doppelt geklickt wurde?</translation>
+ </message>
+ <message>
+ <location line="+24"/>
+ <source>Java</source>
+ <translation>Java</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Memory</source>
+ <translation>Arbeitsspeicher</translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>Minimum memory allocation:</source>
+ <translation>Min. Arbeitspeicher:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Maximum memory allocation:</source>
+ <translation>Max. Arbeitspeicher:</translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>PermGen:</source>
+ <translation>PermGen:</translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>Java Settings</source>
+ <translation>Java-Einstellungen</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Java path:</source>
+ <translation>Java-Pfad:</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Auto-detect...</source>
+ <translation>Auto-Erkennung:</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Test</source>
+ <translation>Test</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>JVM arguments:</source>
+ <translation>JVM-Argumente:</translation>
+ </message>
+ <message>
+ <source>Browse...</source>
+ <translation type="vanished">Durchsuchen...</translation>
+ </message>
+ <message>
+ <source>Auto-detect</source>
+ <translation type="vanished">Automatisch erkennen</translation>
+ </message>
+ <message>
+ <location line="+39"/>
+ <source>Custom Commands</source>
+ <translation>Eigene Befehle</translation>
+ </message>
+ <message>
+ <location line="+6"/>
+ <source>Post-exit command:</source>
+ <translation>Nach-Abschluss-Befehl:</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Pre-launch command:</source>
+ <translation>Vor-Start-Befehl:</translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC&apos;s working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</source>
+ <translation>Der Vor-Start-Befehl wird ausgeführt, bevor die Instanz startet, der Nach-Ende-Befehl, nachdem die Instanz beendet wurde. Beide werden im Hauptverzeichnis von MultiMC gestartet. Verfügbare Umgebungsvariablen: INST_ID, INST_DIR, INST_NAME.</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/SettingsDialog.cpp" line="+77"/>
+ <source>FTB Launcher Directory</source>
+ <translation>FTB-Launcher-Ordner</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>FTB Directory</source>
+ <translation>FTB-Ordner</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Instance Directory</source>
+ <translation>Instanz-Ordner</translation>
+ </message>
+ <message>
+ <location line="+12"/>
+ <source>Icons Directory</source>
+ <translation>Symbolordner</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Mods Directory</source>
+ <translation>Modordner</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>LWJGL Directory</source>
+ <translation>LWJGL-Ordner</translation>
+ </message>
+ <message>
+ <location line="+14"/>
+ <source>JSON Editor</source>
+ <translation>JSON-Editor</translation>
+ </message>
+ <message>
+ <location line="+23"/>
+ <source>Invalid</source>
+ <translation>Ungültig</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>The file chosen does not seem to be an executable</source>
+ <translation>Die ausgewählte Datei scheint keine Anwendung zu sein</translation>
+ </message>
+ <message>
+ <location line="+34"/>
+ <source>Development builds</source>
+ <translation>Entwicklungsversionen</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Development builds contain experimental features and may be unstable. Are you sure you want to enable them?</source>
+ <translation>Entwicklungsversionen enthalten experimentelle Features und können instabil sein. Möchtest du sie dennoch aktivieren?</translation>
+ </message>
+ <message>
+ <location line="+132"/>
+ <source>Select a Java version</source>
+ <translation>Wähle eine Java-Version</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Find Java executable</source>
+ <translatorcomment>Umm... this translation is a bit meh</translatorcomment>
+ <translation>Java-Anwendung finden</translation>
+ </message>
+ <message>
+ <location line="+27"/>
+ <source>Java test success</source>
+ <translation>Java-Test erfolgreich abgeschlossen</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Java test failure</source>
+ <translation>Java-Test fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>The specified java binary didn&apos;t work. You should use the auto-detect feature, or set the path to the java executable.</source>
+ <translation>Das ausgewählte Java-Program hat nicht funktioniert. Du solltest die Auto-Erkennung benutzen oder den Pfad zum Java-Programm angeben.</translation>
+ </message>
+</context>
+<context>
+ <name>TaskDialog</name>
+ <message>
+ <source>Please wait...</source>
+ <translation type="obsolete">Bitte warten...</translation>
+ </message>
+ <message>
+ <source>Task Status...</source>
+ <translation type="obsolete">Aufgabenstatus...</translation>
+ </message>
+</context>
+<context>
+ <name>UpdateDialog</name>
+ <message>
+ <location filename="../gui/dialogs/UpdateDialog.ui" line="+14"/>
+ <source>MultiMC Update</source>
+ <translation>Neue MultiMC-Version</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>A new MultiMC update is available!</source>
+ <translation>Eine neue Version von MultiMC ist verfügbar!</translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source>Update now</source>
+ <translation>Jetzt herunterladen</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Update after MultiMC closes</source>
+ <translation>Herunterladen, wenn MultiMC geschlossen wird</translation>
+ </message>
+ <message>
+ <location line="+13"/>
+ <source>Don&apos;t update yet</source>
+ <translation>Noch nicht herunterladen</translation>
+ </message>
+</context>
+<context>
+ <name>ValidateTask</name>
+ <message>
+ <location filename="../logic/auth/flows/ValidateTask.cpp" line="+58"/>
+ <source>Validating access token: Sending request...</source>
+ <translation>Validiere Zugriffstoken: Sende Anfrage...</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Validating access token: Processing response...</source>
+ <translation>Validiere Zugriffstoken: Bearbeite Antwort...</translation>
+ </message>
+</context>
+<context>
+ <name>VersionSelectDialog</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="vanished">Dialog</translation>
+ </message>
+ <message>
+ <source>Show &amp;snapshots?</source>
+ <translation type="obsolete">&apos;&amp;Snapshots&apos; anzeigen?</translation>
+ </message>
+ <message>
+ <source>Show &amp;Nostalgia?</source>
+ <translation type="obsolete">&apos;&amp;Nostalgie&apos;-Versionen anzeigen?</translation>
+ </message>
+ <message>
+ <location filename="../gui/dialogs/VersionSelectDialog.ui" line="+14"/>
+ <source>Choose Version</source>
+ <translation>Wähle Version</translation>
+ </message>
+ <message>
+ <location line="+33"/>
+ <source>Reloads the version list.</source>
+ <translation>Instanzliste aktualisieren.</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&amp;Refresh</source>
+ <translation>&amp;Aktualisieren</translation>
+ </message>
+</context>
+<context>
+ <name>YggdrasilTask</name>
+ <message>
+ <location filename="../logic/auth/YggdrasilTask.cpp" line="+98"/>
+ <source>&lt;b&gt;SSL Handshake failed.&lt;/b&gt;&lt;br/&gt;There might be a few causes for it:&lt;br/&gt;&lt;ul&gt;&lt;li&gt;You use Windows XP and need to &lt;a href=&quot;http://www.microsoft.com/en-us/download/details.aspx?id=38918&quot;&gt;update your root certificates&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Some device on your network is interfering with SSL traffic. In that case, you have bigger worries than Minecraft not starting.&lt;/li&gt;&lt;li&gt;Possibly something else. Check the MultiMC log file for details&lt;/li&gt;&lt;/ul&gt;</source>
+ <translation>&lt;b&gt;SSL-Handshake fehlgeschlagen.&lt;/b&gt;&lt;br/&gt;Es kann mehrere Erklärungen geben:&lt;br/&gt;&lt;ul&gt;&lt;li&gt;Du benutzt Windows XP und musst &lt;a href=&quot;http://www.microsoft.com/en-us/download/details.aspx?id=38918&quot;&gt;dein Stammzertifikat aktualisieren&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Ein Gerät in deinem Netzwerk mischt sich in den SSL-Verkehr ein. In diesem Fall hast du größere Probleme, als das, dass Minecraft nicht gestartet werden kann.&lt;/li&gt;&lt;li&gt;Möglicherweise etwas anderes. Prüfe die MultiMC-Logdatei für Details&lt;/li&gt;&lt;/ul&gt;</translation>
+ </message>
+ <message>
+ <location line="+47"/>
+ <source>An unknown error occurred when processing the response from the authentication server.</source>
+ <translation>Ein unbekannter Fehler ist beim Bearbeiten der Antwort des Authentifizierungs-Servers aufgetreten.</translation>
+ </message>
+ <message>
+ <location line="+5"/>
+ <source>Failed to parse Yggdrasil JSON response: %1 at offset %2.</source>
+ <translation>Fehler beim Bearbeiten der Yggdrasil-JSON-Antwort: %1 bei %2.</translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>An unknown error occurred when trying to communicate with the authentication server: %1</source>
+ <translation>Ein unbekannter Fehler ist bei der Kommunikation mit den Authentifizierungs-Servern aufgetreten: %1</translation>
+ </message>
+ <message>
+ <location line="+20"/>
+ <source>An unknown Yggdrasil error occurred.</source>
+ <translation>Ein unbekannter Yggdrasil-Fehler ist aufgetreten.</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Sending request to auth servers...</source>
+ <translation>Sende Anfrage an die Authentifizierungs-Server...</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Processing response from servers...</source>
+ <translation>Bearbeite Antwort des Authentifizierungs-Servers...</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Processing. Please wait...</source>
+ <translation>Bearbeite. Bitte warten...</translation>
+ </message>
+</context>
+</TS>