Compare commits
317 Commits
Author | SHA1 | Date |
---|---|---|
Guillaume Jacquemin | 7b64d78b5b | |
Guillaume Jacquemin | 59aa006611 | |
Guillaume Jacquemin | 81b35476a0 | |
Guillaume Jacquemin | b909f0ac13 | |
Guillaume Jacquemin | aff84ccc96 | |
Guillaume Jacquemin | 8177d61755 | |
Guillaume Jacquemin | c0943bd084 | |
Guillaume Jacquemin | 6fa21128ab | |
Guillaume Jacquemin | a8ab212931 | |
Guillaume Jacquemin | 9a7aaaeaca | |
Guillaume Jacquemin | b3bf75918e | |
Guillaume Jacquemin | 9de6766750 | |
Guillaume Jacquemin | 90ff680aff | |
Guillaume Jacquemin | 07fca7b0d0 | |
Guillaume Jacquemin | 4ea694ea6e | |
Guillaume Jacquemin | 722cc511d6 | |
Guillaume Jacquemin | 924838ecb4 | |
Guillaume Jacquemin | dfd0d56ab0 | |
Guillaume Jacquemin | 89a10ff98d | |
Guillaume Jacquemin | 71fde374a9 | |
Guillaume Jacquemin | c7cc3ba76c | |
Guillaume Jacquemin | 0aa933e766 | |
Guillaume Jacquemin | 5ea6f1e3a7 | |
Guillaume Jacquemin | 32046d9bf8 | |
Guillaume Jacquemin | 3c4dfbda9a | |
Guillaume Jacquemin | d1712bf8cb | |
Guillaume Jacquemin | 567546489f | |
Guillaume Jacquemin | c054169124 | |
Guillaume Jacquemin | b58ff5a763 | |
Guillaume Jacquemin | 28eb40f6b4 | |
Guillaume Jacquemin | d0a3375d7a | |
Guillaume Jacquemin | 393ec4a372 | |
Guillaume Jacquemin | 83e9169dae | |
Guillaume Jacquemin | 8625f8835c | |
Guillaume Jacquemin | c3a9c8dd31 | |
Guillaume Jacquemin | 51b25ea9c5 | |
Guillaume Jacquemin | 1421257c4f | |
Guillaume Jacquemin | df5fa7a39e | |
Guillaume Jacquemin | c5b4747685 | |
Guillaume Jacquemin | 7ddc8e0748 | |
Guillaume Jacquemin | 7392d961c7 | |
Guillaume Jacquemin | fa81d2428e | |
Guillaume Jacquemin | 79c97733db | |
Guillaume Jacquemin | 060daebe17 | |
Guillaume Jacquemin | 8cf5351f0b | |
Guillaume Jacquemin | e795e276da | |
Guillaume Jacquemin | bf820f65ec | |
Guillaume Jacquemin | fb6246cff7 | |
Guillaume Jacquemin | 7257b9865c | |
Guillaume Jacquemin | 45bc2b97d9 | |
Guillaume Jacquemin | 9de62db449 | |
Guillaume Jacquemin | 44656b32d5 | |
Guillaume Jacquemin | 9ec88fa521 | |
Guillaume Jacquemin | ee540b601e | |
Guillaume Jacquemin | fe10bbb3f3 | |
Guillaume Jacquemin | bb066d3134 | |
Guillaume Jacquemin | e21e7a1aba | |
Guillaume Jacquemin | 714d8cc6bb | |
Guillaume Jacquemin | a33cbdfad6 | |
Guillaume Jacquemin | b7cd78ca21 | |
Guillaume Jacquemin | 453c5391a4 | |
Guillaume Jacquemin | 05611d59b1 | |
Guillaume Jacquemin | a5a8db289a | |
Guillaume Jacquemin | 6130734764 | |
Guillaume Jacquemin | 16b8807eb7 | |
Guillaume Jacquemin | f1ea2bda25 | |
Guillaume Jacquemin | 42cec59c71 | |
Guillaume Jacquemin | 5c6a83c03b | |
Guillaume Jacquemin | e6c597ffbc | |
Guillaume Jacquemin | 6cb52761be | |
Guillaume Jacquemin | 6f2b19dbc3 | |
Guillaume Jacquemin | e61d4bba85 | |
Guillaume Jacquemin | 38532d8c35 | |
Guillaume Jacquemin | 8ba8ec3219 | |
Guillaume Jacquemin | 55eb367eb2 | |
Guillaume Jacquemin | b6398f3373 | |
Guillaume Jacquemin | b598476809 | |
Guillaume Jacquemin | 0ce03f5395 | |
Guillaume Jacquemin | 0fd157f33c | |
Guillaume Jacquemin | 1bbbf3cbfd | |
Guillaume Jacquemin | 918ead0733 | |
Guillaume Jacquemin | 42b4974b43 | |
Guillaume Jacquemin | c35735b2fc | |
Guillaume Jacquemin | 32a1a6d014 | |
Guillaume Jacquemin | 71d38f4a91 | |
Guillaume Jacquemin | 869ca07b20 | |
Guillaume Jacquemin | c1ae793800 | |
Guillaume Jacquemin | 70ddb0ce39 | |
Guillaume Jacquemin | 704f6e2f49 | |
Guillaume Jacquemin | dbc52ec28f | |
Guillaume Jacquemin | 11c089d408 | |
Guillaume Jacquemin | 6b280b2668 | |
Guillaume Jacquemin | 213269521d | |
Guillaume Jacquemin | b6ad795383 | |
Guillaume Jacquemin | 94979907b1 | |
Guillaume Jacquemin | a36d9134bf | |
Guillaume Jacquemin | 0e3e3145b7 | |
Guillaume Jacquemin | 3be094febc | |
Guillaume Jacquemin | a166948aec | |
Guillaume Jacquemin | b909aa85b7 | |
Guillaume Jacquemin | 94f6192aa8 | |
Guillaume Jacquemin | 5705f408a5 | |
Guillaume Jacquemin | 4ed7aff835 | |
Guillaume Jacquemin | 718a6fd754 | |
Guillaume Jacquemin | 677bf21c9f | |
Guillaume Jacquemin | b287c827d2 | |
Guillaume Jacquemin | 2c2e5ad936 | |
Guillaume Jacquemin | ef05c075ba | |
Guillaume Jacquemin | 14d75e0e83 | |
Guillaume Jacquemin | 63a8cf7075 | |
Guillaume Jacquemin | bf3288772e | |
Guillaume Jacquemin | bde6fc41a0 | |
Guillaume Jacquemin | ac1276761e | |
Guillaume Jacquemin | fd3306b175 | |
Guillaume Jacquemin | cf72cbe2c6 | |
Guillaume Jacquemin | f4adb9e26a | |
Guillaume Jacquemin | 592fd2ba3a | |
Guillaume Jacquemin | 84b1e276a1 | |
Guillaume Jacquemin | 0c8c2601ce | |
Guillaume Jacquemin | 147c38669f | |
Guillaume Jacquemin | 1871440a7b | |
Guillaume Jacquemin | 133c34f5f7 | |
Guillaume Jacquemin | 47890f6939 | |
Guillaume Jacquemin | 354c3ff3d9 | |
Guillaume Jacquemin | 04beebbc1c | |
Guillaume Jacquemin | 47520b89e3 | |
Guillaume Jacquemin | ce0ca07afc | |
Guillaume Jacquemin | e91c015c00 | |
Guillaume Jacquemin | 75d77413f6 | |
Guillaume Jacquemin | ddad6536c6 | |
Guillaume Jacquemin | d9f6470dc6 | |
Guillaume Jacquemin | f963ce31ad | |
Guillaume Jacquemin | be06c2d552 | |
Guillaume Jacquemin | d61977d758 | |
Guillaume Jacquemin | f36782bff0 | |
Guillaume Jacquemin | 76613c2ec5 | |
Guillaume Jacquemin | e997312286 | |
Guillaume Jacquemin | e2d31854b4 | |
Guillaume Jacquemin | ccf630c385 | |
Guillaume Jacquemin | 4ae9f83ab3 | |
Guillaume Jacquemin | dd9dcdb5f6 | |
Guillaume Jacquemin | f1a4b64219 | |
Guillaume Jacquemin | 8f4708f518 | |
Guillaume Jacquemin | b859bf7ab5 | |
Guillaume Jacquemin | db6836ec33 | |
Guillaume Jacquemin | 89bba618fb | |
Guillaume Jacquemin | 88afaaceec | |
Guillaume Jacquemin | fdb7567aea | |
Guillaume Jacquemin | 771e008e62 | |
Guillaume Jacquemin | 4d9fc46003 | |
Guillaume Jacquemin | db3eba5b59 | |
Guillaume Jacquemin | 8791eb32ac | |
Guillaume Jacquemin | 2648e1103e | |
Guillaume Jacquemin | f522d20dd4 | |
Guillaume Jacquemin | 6208825aa6 | |
Guillaume Jacquemin | 572585e648 | |
Guillaume Jacquemin | de2ba9ce7f | |
Guillaume Jacquemin | d0ddc73852 | |
Guillaume Jacquemin | a1c17b7138 | |
Guillaume Jacquemin | 350ad59f8e | |
Guillaume Jacquemin | 883d5d3f41 | |
Guillaume Jacquemin | 77d7eaefad | |
Guillaume Jacquemin | 82170b3078 | |
Guillaume Jacquemin | 88abf91047 | |
Guillaume Jacquemin | 955ec010b8 | |
Guillaume Jacquemin | 7cb9ea28b2 | |
Guillaume Jacquemin | 975f471a68 | |
Guillaume Jacquemin | 76210e147a | |
Guillaume Jacquemin | 5e06c48492 | |
Guillaume Jacquemin | 2ff32c4c78 | |
Guillaume Jacquemin | 4000421a8c | |
Guillaume Jacquemin | 8f1e3668a3 | |
Guillaume Jacquemin | a6c0614979 | |
Guillaume Jacquemin | 2cabe6a3ba | |
Guillaume Jacquemin | afc163f344 | |
Guillaume Jacquemin | 353a71d8ab | |
Guillaume Jacquemin | bbc40d7c93 | |
Guillaume Jacquemin | 28db82c8a9 | |
Guillaume Jacquemin | 9f324c30fd | |
Guillaume Jacquemin | 41cd92352d | |
Guillaume Jacquemin | 940fe3feee | |
Guillaume Jacquemin | d74a7bc219 | |
Guillaume Jacquemin | 51faed7210 | |
Guillaume Jacquemin | 8fb837bfc0 | |
Guillaume Jacquemin | 0ac1e759ca | |
Guillaume Jacquemin | a4045e8e9b | |
Guillaume Jacquemin | 1ec4522baf | |
Guillaume Jacquemin | 13d09e4aa0 | |
Guillaume Jacquemin | ed0c4a73bb | |
Guillaume Jacquemin | 8102d1d83a | |
Guillaume Jacquemin | 9a9c08391a | |
Guillaume Jacquemin | 51602c713a | |
Guillaume Jacquemin | 1621a4dbd5 | |
Guillaume Jacquemin | 7fb269f862 | |
Guillaume Jacquemin | 1378676bbc | |
Guillaume Jacquemin | 96768c1aab | |
Guillaume Jacquemin | bd05a98820 | |
Guillaume Jacquemin | 7059295cb3 | |
Guillaume Jacquemin | 065e63f27c | |
Guillaume Jacquemin | a05f3eeed0 | |
Guillaume Jacquemin | 321e8feed0 | |
Guillaume Jacquemin | c6de9c1940 | |
Guillaume Jacquemin | fe0db983ce | |
Guillaume Jacquemin | bd8ff47f1e | |
Guillaume Jacquemin | 83fa5822bf | |
Guillaume Jacquemin | 8d87cdd619 | |
Guillaume Jacquemin | 0900f92b9f | |
Guillaume Jacquemin | af71806e13 | |
Guillaume Jacquemin | 247578a386 | |
Guillaume Jacquemin | 4ca6f62d9b | |
Guillaume Jacquemin | e461d5a505 | |
Guillaume Jacquemin | d79debe69f | |
Guillaume Jacquemin | 83fe02a8dc | |
Guillaume Jacquemin | 7e452db3a4 | |
Guillaume Jacquemin | 80bb85c0d8 | |
Guillaume Jacquemin | 4df90efd67 | |
Guillaume Jacquemin | b92c37e4b6 | |
Guillaume Jacquemin | dd460b4313 | |
Guillaume Jacquemin | 05a2b1cfb0 | |
Guillaume Jacquemin | 8fedbdd4e5 | |
Guillaume Jacquemin | 3ac5288f12 | |
Guillaume Jacquemin | 8bae723018 | |
Guillaume Jacquemin | 3714162b50 | |
Guillaume Jacquemin | a22aa6f7ae | |
Guillaume Jacquemin | d03e75a8e9 | |
Guillaume Jacquemin | 4429e581f3 | |
Guillaume Jacquemin | 2d0d5817f2 | |
Guillaume Jacquemin | bfe9a2c3a8 | |
Guillaume Jacquemin | 8c81b7811b | |
Guillaume Jacquemin | 0904384e0d | |
Guillaume Jacquemin | 6f3da0b4a7 | |
Guillaume Jacquemin | 19c00a3ce3 | |
Guillaume Jacquemin | 79762e176e | |
Guillaume Jacquemin | b5b5b3b38c | |
Guillaume Jacquemin | e4cfd3834a | |
Guillaume Jacquemin | e77cce5b42 | |
Guillaume Jacquemin | 18aa7f659e | |
Guillaume Jacquemin | 1612e4372b | |
Guillaume Jacquemin | b377e0de6c | |
Guillaume Jacquemin | 0a438a4d72 | |
Guillaume Jacquemin | e839d1c19b | |
Guillaume Jacquemin | 5689ec6c1a | |
Guillaume Jacquemin | c2d0fbd941 | |
Guillaume Jacquemin | 2b2320ae0a | |
Guillaume Jacquemin | 10368e09db | |
Guillaume Jacquemin | bd255ef8d5 | |
Guillaume Jacquemin | 911e18fc0a | |
Guillaume Jacquemin | 0c257bcfa6 | |
Guillaume Jacquemin | 9bc4aaf66b | |
Guillaume Jacquemin | 79e3193309 | |
Guillaume Jacquemin | 76e36791d7 | |
Guillaume Jacquemin | 50a7b1d7f0 | |
Guillaume Jacquemin | 1caa472833 | |
Guillaume Jacquemin | d3d065c945 | |
Guillaume Jacquemin | b8b156a724 | |
Guillaume Jacquemin | 9c1aeb753e | |
Guillaume Jacquemin | 0006c90a21 | |
Guillaume Jacquemin | 0826d4aede | |
Guillaume Jacquemin | b3220ca8e1 | |
Guillaume Jacquemin | 48210c7186 | |
Guillaume Jacquemin | f500e982e6 | |
Guillaume Jacquemin | 2e1949ed5d | |
Guillaume Jacquemin | 10becfdc31 | |
Guillaume Jacquemin | f286ec0633 | |
Guillaume Jacquemin | ce29d6174c | |
Guillaume Jacquemin | de07b760d0 | |
Guillaume Jacquemin | 66d96bd893 | |
Guillaume Jacquemin | 083b60aac4 | |
Guillaume Jacquemin | 918b26ab5e | |
Guillaume Jacquemin | 32bc179120 | |
Guillaume Jacquemin | c64684b34c | |
Guillaume Jacquemin | 40840e3128 | |
Guillaume Jacquemin | c7c379c419 | |
Guillaume Jacquemin | a9a5bfb2af | |
Guillaume Jacquemin | 5f4576a2bc | |
Guillaume Jacquemin | f3318e0ed1 | |
Guillaume Jacquemin | 7fcf8b518e | |
Guillaume Jacquemin | 69021eacdf | |
Guillaume Jacquemin | bd6e55826d | |
Guillaume Jacquemin | 597e9dfe98 | |
Guillaume Jacquemin | 4cdd1b35ec | |
Guillaume Jacquemin | f323215844 | |
Guillaume Jacquemin | e580736ac6 | |
Guillaume Jacquemin | 2644a73fc9 | |
Guillaume Jacquemin | 927da387ea | |
Guillaume Jacquemin | a244e468d2 | |
Guillaume Jacquemin | 6d4bafcc2d | |
Guillaume Jacquemin | d52b381426 | |
Guillaume Jacquemin | 52f5e8eb0a | |
Guillaume Jacquemin | c4fc910ab0 | |
Guillaume Jacquemin | ded5e9bcb7 | |
Guillaume Jacquemin | ee639bcdf8 | |
Guillaume Jacquemin | 406349bbe8 | |
Guillaume Jacquemin | 22c9627b84 | |
Guillaume Jacquemin | e99ff14749 | |
Guillaume Jacquemin | d0716d6242 | |
Guillaume Jacquemin | 512fa4088b | |
Guillaume Jacquemin | ab124174b0 | |
Guillaume Jacquemin | 51127241ef | |
Guillaume Jacquemin | ee384843e9 | |
Guillaume Jacquemin | 7f32166ab0 | |
Guillaume Jacquemin | 017900afe2 | |
Guillaume Jacquemin | 52b60ff2a5 | |
Guillaume Jacquemin | 14c5a76891 | |
Guillaume Jacquemin | d9efe8191e | |
Guillaume Jacquemin | 445d7323b3 | |
Guillaume Jacquemin | 83002868d9 | |
Guillaume Jacquemin | d0eee0caeb | |
Guillaume Jacquemin | 4dd2064aae | |
Guillaume Jacquemin | e2d473da44 | |
Guillaume Jacquemin | 688e61b9ae | |
Guillaume Jacquemin | 241f5b754d | |
Guillaume Jacquemin | 2a617b3359 | |
Guillaume Jacquemin | ec8a6b0c46 | |
Guillaume Jacquemin | a16383183e | |
Guillaume Jacquemin | fa78ca2a8a | |
Guillaume Jacquemin | 3f3166691d |
|
@ -1,4 +1,4 @@
|
|||
build*/
|
||||
*build*/
|
||||
.idea/
|
||||
*.kdev4
|
||||
*~
|
||||
|
|
|
@ -26,3 +26,7 @@
|
|||
path = third-party/efsw
|
||||
url = https://github.com/SpartanJ/efsw
|
||||
branch = master
|
||||
[submodule "libcurl"]
|
||||
path = third-party/curl
|
||||
url = https://github.com/curl/curl
|
||||
branch = master
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# MassBuilderSaveTool
|
||||
# Copyright (C) 2021 Guillaume Jacquemin
|
||||
# Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
#
|
||||
# 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
|
||||
|
@ -21,16 +21,20 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/modules/" ${CMAKE_MODULE_PATH})
|
|||
|
||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
|
||||
set(BUILD_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(BUILD_STATIC_PIC ON CACHE BOOL "" FORCE)
|
||||
set(BUILD_STATIC_UNIQUE_GLOBALS OFF CACHE BOOL "" FORCE)
|
||||
|
||||
set(WITH_INTERCONNECT ON CACHE BOOL "" FORCE)
|
||||
set(WITH_PLUGINMANAGER ON CACHE BOOL "" FORCE)
|
||||
set(WITH_TESTSUITE OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_MAIN ON CACHE BOOL "" FORCE)
|
||||
set(CORRADE_BUILD_DEPRECATED OFF CACHE BOOL "" FORCE)
|
||||
set(CORRADE_BUILD_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(CORRADE_BUILD_STATIC_PIC ON CACHE BOOL "" FORCE)
|
||||
set(CORRADE_BUILD_STATIC_UNIQUE_GLOBALS OFF CACHE BOOL "" FORCE)
|
||||
set(CORRADE_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(CORRADE_WITH_INTERCONNECT OFF CACHE BOOL "" FORCE)
|
||||
set(CORRADE_WITH_PLUGINMANAGER OFF CACHE BOOL "" FORCE)
|
||||
set(CORRADE_WITH_TESTSUITE OFF CACHE BOOL "" FORCE)
|
||||
set(CORRADE_WITH_MAIN ON CACHE BOOL "" FORCE)
|
||||
set(CORRADE_UTILITY_USE_ANSI_COLORS ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third-party/corrade EXCLUDE_FROM_ALL)
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(DIRECTX OFF CACHE BOOL "" FORCE) # We use OpenGL.
|
||||
set(SDL_ATOMIC OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_CPUINFO OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_EVENTS ON CACHE BOOL "" FORCE)
|
||||
|
@ -46,26 +50,34 @@ set(SDL_TIMERS ON CACHE BOOL "" FORCE)
|
|||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third-party/SDL EXCLUDE_FROM_ALL)
|
||||
|
||||
set(TARGET_GL ON CACHE BOOL "" FORCE)
|
||||
set(TARGET_GLES OFF CACHE BOOL "" FORCE)
|
||||
set(TARGET_VK OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_AUDIO OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_DEBUGTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_GL ON CACHE BOOL "" FORCE)
|
||||
set(WITH_MESHTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_PRIMITIVES OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_SCENEGRAPH OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_SHADERS ON CACHE BOOL "" FORCE)
|
||||
set(WITH_SHADERTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_TEXT OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_TEXTURETOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_TRADE OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_VK OFF CACHE BOOL "" FORCE)
|
||||
set(WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_BUILD_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_BUILD_STATIC_PIC ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_BUILD_STATIC_UNIQUE_GLOBALS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_BUILD_DEPRECATED OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
|
||||
set(MAGNUM_TARGET_GL ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_TARGET_GLES OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_TARGET_VK OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_AUDIO OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_DEBUGTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_GL ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_MATERIALTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_MESHTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_PRIMITIVES OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_SCENEGRAPH OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_SCENETOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_SHADERS ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_SHADERTOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_TEXT OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_TEXTURETOOLS OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_TRADE OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_VK OFF CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third-party/magnum EXCLUDE_FROM_ALL)
|
||||
|
||||
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/imgui)
|
||||
set(WITH_IMGUI ON CACHE BOOL "" FORCE)
|
||||
set(MAGNUM_WITH_IMGUI ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third-party/magnum-integration EXCLUDE_FROM_ALL)
|
||||
|
||||
set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE)
|
||||
|
@ -80,7 +92,6 @@ set(BUILD_TOOLS OFF CACHE BOOL "" FORCE)
|
|||
set(BUILD_REGRESS OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_DOC OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third-party/libzip EXCLUDE_FROM_ALL)
|
||||
|
||||
set(VERBOSE OFF CACHE BOOL "" FORCE)
|
||||
|
@ -88,4 +99,20 @@ set(BUILD_TEST_APP OFF CACHE BOOL "" FORCE)
|
|||
set(EFSW_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third-party/efsw EXCLUDE_FROM_ALL)
|
||||
|
||||
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_UNICODE ON CACHE BOOL "" FORCE)
|
||||
set(ENABLE_INET_PTON OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_DEBUG OFF CACHE BOOL "" FORCE)
|
||||
set(ENABLE_THREADED_RESOLVER OFF CACHE BOOL "" FORCE)
|
||||
set(HTTP_ONLY ON CACHE BOOL "" FORCE)
|
||||
set(USE_LIBIDN2 OFF CACHE BOOL "" FORCE)
|
||||
set(USE_WIN32_IDN ON CACHE BOOL "" FORCE)
|
||||
set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE)
|
||||
set(CURL_STATIC_CRT OFF CACHE BOOL "" FORCE)
|
||||
set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE)
|
||||
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE) # For some reason, even when HTTP_ONLY is set to ON, libcurl will try to link to libssh2.
|
||||
add_subdirectory(third-party/curl EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
|
14
README.md
14
README.md
|
@ -1,16 +1,20 @@
|
|||
# M.A.S.S. Builder Save Tool
|
||||
|
||||
A save file manager and editor for M.A.S.S. Builder. Based on [wxMASSManager](https://williamjcm.ovh/git/williamjcm/wxMASSManager), this is a fork using Magnum and ImGui for the UI.
|
||||
A save file manager and editor for M.A.S.S. Builder. Based on [wxMASSManager](https://git.williamjcm.ovh/williamjcm/wxMASSManager),
|
||||
this is a fork using Magnum and ImGui for the UI.
|
||||
|
||||
## Installing
|
||||
|
||||
Get the `MassBuilderSaveTool-<version>.zip` file from the [Releases](https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases) page, and extract it somewhere. Then, launch `MassBuilderSaveTool.exe`.
|
||||
Get the `MassBuilderSaveTool-<version>.zip` file from the [the main website](https://williamjcm.ovh/mbst) or on the
|
||||
[Releases](https://git.williamjcm.ovh/williamjcm/MassBuilderSaveTool/releases) page, and extract it somewhere. Then,
|
||||
launch `MassBuilderSaveTool-<version>.exe`.
|
||||
|
||||
## Building on MSYS2 - IGNORE IF YOU JUST WANT TO USE THE APP!
|
||||
|
||||
1. Install the 64-bit (`x86_64`) version of [MSYS2](https://www.msys2.org/) in its default path (`C:\msys64`), and update it fully.
|
||||
2. Run `pacman -S git mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja`.
|
||||
3. In a `MINGW64` shell, type `git clone https://github.com/williamjcm/MassBuilderSaveTool`.
|
||||
1. Install the 64-bit (`x86_64`) version of [MSYS2](https://www.msys2.org/) in its default path (`C:\msys64`), and
|
||||
update it fully.
|
||||
2. Run `pacman -S git mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja`.
|
||||
3. In a `URCT64` shell, type `git clone https://github.com/williamjcm/MassBuilderSaveTool`.
|
||||
4. Type `cd MassBuilderSaveTool && git submodule init && git submodule update && mkdir build && cd build`.
|
||||
5. Type `cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..`
|
||||
6. Type `ninja`
|
||||
|
|
|
@ -62,8 +62,8 @@
|
|||
#
|
||||
# Features of found Corrade library are exposed in these variables:
|
||||
#
|
||||
# CORRADE_MSVC2019_COMPATIBILITY - Defined if compiled with compatibility
|
||||
# mode for MSVC 2019
|
||||
# CORRADE_MSVC_COMPATIBILITY - Defined if compiled with compatibility
|
||||
# mode for MSVC 2019+ without the /permissive- flag set
|
||||
# CORRADE_MSVC2017_COMPATIBILITY - Defined if compiled with compatibility
|
||||
# mode for MSVC 2017
|
||||
# CORRADE_MSVC2015_COMPATIBILITY - Defined if compiled with compatibility
|
||||
|
@ -100,7 +100,7 @@
|
|||
# CORRADE_TARGET_MINGW - Defined if compiling under MinGW
|
||||
# CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - Defined if PluginManager
|
||||
# doesn't support dynamic plugin loading due to platform limitations
|
||||
# CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targetting Xcode
|
||||
# CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targeting Xcode
|
||||
# XCTest
|
||||
# CORRADE_UTILITY_USE_ANSI_COLORS - Defined if ANSI escape sequences are used
|
||||
# for colored output with Utility::Debug on Windows
|
||||
|
@ -264,7 +264,7 @@
|
|||
# This file is part of Corrade.
|
||||
#
|
||||
# Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
|
||||
# 2017, 2018, 2019, 2020, 2021
|
||||
# 2017, 2018, 2019, 2020, 2021, 2022
|
||||
# Vladimír Vondruš <mosra@centrum.cz>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
@ -312,7 +312,7 @@ string(REGEX REPLACE "\n" ";" _corradeConfigure "${_corradeConfigure}")
|
|||
set(_corradeFlags
|
||||
MSVC2015_COMPATIBILITY
|
||||
MSVC2017_COMPATIBILITY
|
||||
MSVC2019_COMPATIBILITY
|
||||
MSVC_COMPATIBILITY
|
||||
BUILD_DEPRECATED
|
||||
BUILD_STATIC
|
||||
BUILD_STATIC_UNIQUE_GLOBALS
|
||||
|
@ -529,7 +529,7 @@ foreach(_component ${Corrade_FIND_COMPONENTS})
|
|||
set_property(TARGET Corrade::${_component} APPEND PROPERTY
|
||||
COMPATIBLE_INTERFACE_NUMBER_MAX CORRADE_CXX_STANDARD)
|
||||
|
||||
# Directory::libraryLocation() needs this
|
||||
# Path::libraryLocation() needs this
|
||||
if(CORRADE_TARGET_UNIX)
|
||||
set_property(TARGET Corrade::${_component} APPEND PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
# This file is part of Magnum.
|
||||
#
|
||||
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
||||
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# Copyright © 2018 Jonathan Hale <squareys@googlemail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
@ -59,11 +59,18 @@
|
|||
#
|
||||
|
||||
# In 1.71 ImGui depends on the ApplicationServices framework for macOS
|
||||
# clipboard support. It's removed again in 1.72. TODO: remove once obsolete
|
||||
# clipboard support. Since 1.72 the dependency is optional and used only if
|
||||
# IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS is enabled, but link to it
|
||||
# always to be nice to users.
|
||||
if(CORRADE_TARGET_APPLE)
|
||||
find_library(_IMGUI_ApplicationServices_LIBRARY ApplicationServices)
|
||||
mark_as_advanced(_IMGUI_ApplicationServices_LIBRARY)
|
||||
set(_IMGUI_EXTRA_LIBRARIES ${_IMGUI_ApplicationServices_LIBRARY})
|
||||
|
||||
# Since 1.82, ImGui on MinGW needs the imm32 library. For MSVC the library
|
||||
# seems to be linked implicitly so this is not needed.
|
||||
elseif(CORRADE_TARGET_WINDOWS AND CORRADE_TARGET_MINGW)
|
||||
set(_IMGUI_EXTRA_LIBRARIES imm32)
|
||||
endif()
|
||||
|
||||
# Vcpkg distributes imgui as a library with a config file, so try that first --
|
||||
|
@ -75,7 +82,6 @@ endif()
|
|||
if(NOT IMGUI_DIR AND TARGET imgui::imgui)
|
||||
if(NOT TARGET ImGui::ImGui)
|
||||
add_library(ImGui::ImGui INTERFACE IMPORTED)
|
||||
# TODO: remove once 1.71 is obsolete
|
||||
set_property(TARGET ImGui::ImGui APPEND PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES imgui::imgui ${_IMGUI_EXTRA_LIBRARIES})
|
||||
|
||||
|
@ -104,7 +110,6 @@ else()
|
|||
add_library(ImGui::ImGui INTERFACE IMPORTED)
|
||||
set_property(TARGET ImGui::ImGui APPEND PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${ImGui_INCLUDE_DIR})
|
||||
# TODO: remove once 1.71 is obsolete
|
||||
if(_IMGUI_EXTRA_LIBRARIES)
|
||||
set_property(TARGET ImGui::ImGui APPEND PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES ${_IMGUI_EXTRA_LIBRARIES})
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
# MeshTools - MeshTools library
|
||||
# Primitives - Primitives library
|
||||
# SceneGraph - SceneGraph library
|
||||
# SceneTools - SceneTools library
|
||||
# Shaders - Shaders library
|
||||
# ShaderTools - ShaderTools library
|
||||
# Text - Text library
|
||||
|
@ -201,7 +202,7 @@
|
|||
# This file is part of Magnum.
|
||||
#
|
||||
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
||||
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
|
@ -229,7 +230,7 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
|
|||
|
||||
# Unrolling the transitive dependencies here so this doesn't need to be
|
||||
# after resolving inter-component dependencies. Listing also all plugins.
|
||||
if(_component MATCHES "^(Audio|DebugTools|MeshTools|Primitives|ShaderTools|Text|TextureTools|Trade|.+Importer|.+ImageConverter|.+Font|.+ShaderConverter)$")
|
||||
if(_component MATCHES "^(Audio|DebugTools|MeshTools|Primitives|SceneTools|ShaderTools|Text|TextureTools|Trade|.+Importer|.+ImageConverter|.+Font|.+ShaderConverter)$")
|
||||
set(_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES PluginManager)
|
||||
endif()
|
||||
|
||||
|
@ -354,8 +355,8 @@ endif()
|
|||
# Component distinction (listing them explicitly to avoid mistakes with finding
|
||||
# components from other repositories)
|
||||
set(_MAGNUM_LIBRARY_COMPONENTS
|
||||
Audio DebugTools GL MeshTools Primitives SceneGraph Shaders ShaderTools
|
||||
Text TextureTools Trade
|
||||
Audio DebugTools GL MeshTools Primitives SceneGraph SceneTools Shaders
|
||||
ShaderTools Text TextureTools Trade
|
||||
WindowlessEglApplication EglContext OpenGLTester)
|
||||
set(_MAGNUM_PLUGIN_COMPONENTS
|
||||
AnyAudioImporter AnyImageConverter AnyImageImporter AnySceneConverter
|
||||
|
@ -387,8 +388,7 @@ if(CORRADE_TARGET_EMSCRIPTEN)
|
|||
endif()
|
||||
if(CORRADE_TARGET_IOS)
|
||||
list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessIosApplication)
|
||||
endif()
|
||||
if(CORRADE_TARGET_APPLE AND NOT CORRADE_TARGET_IOS)
|
||||
elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES)
|
||||
list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessCglApplication CglContext)
|
||||
endif()
|
||||
if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE)
|
||||
|
@ -430,7 +430,7 @@ if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID
|
|||
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication)
|
||||
elseif(CORRADE_TARGET_IOS)
|
||||
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessIosApplication)
|
||||
elseif(CORRADE_TARGET_APPLE)
|
||||
elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES)
|
||||
list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessCglApplication)
|
||||
elseif(CORRADE_TARGET_UNIX)
|
||||
if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES)
|
||||
|
@ -451,8 +451,11 @@ if(MAGNUM_TARGET_GL)
|
|||
# GL not required by Primitives themselves, but transitively by MeshTools
|
||||
list(APPEND _MAGNUM_Primitives_DEPENDENCIES GL)
|
||||
endif()
|
||||
|
||||
set(_MAGNUM_SceneGraph_DEPENDENCIES )
|
||||
set(_MAGNUM_SceneTools_DEPENDENCIES Trade)
|
||||
set(_MAGNUM_Shaders_DEPENDENCIES GL)
|
||||
|
||||
set(_MAGNUM_Text_DEPENDENCIES TextureTools)
|
||||
if(MAGNUM_TARGET_GL)
|
||||
list(APPEND _MAGNUM_Text_DEPENDENCIES GL)
|
||||
|
@ -466,6 +469,7 @@ endif()
|
|||
set(_MAGNUM_Trade_DEPENDENCIES )
|
||||
set(_MAGNUM_VulkanTester_DEPENDENCIES Vk)
|
||||
set(_MAGNUM_AndroidApplication_DEPENDENCIES GL)
|
||||
|
||||
set(_MAGNUM_EmscriptenApplication_DEPENDENCIES)
|
||||
if(MAGNUM_TARGET_GL)
|
||||
list(APPEND _MAGNUM_EmscriptenApplication_DEPENDENCIES GL)
|
||||
|
@ -608,7 +612,7 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
|
|||
|
||||
# Dynamic plugins don't have any prefix (e.g. `lib` on Linux),
|
||||
# search with empty prefix and then reset that back so we don't
|
||||
# accidentaly break something else
|
||||
# accidentally break something else
|
||||
set(_tmp_prefixes "${CMAKE_FIND_LIBRARY_PREFIXES}")
|
||||
set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES};")
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
# This file is part of Magnum.
|
||||
#
|
||||
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
||||
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# Copyright © 2018 Konstantinos Chatzilygeroudis <costashatz@gmail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
# This file is part of Magnum.
|
||||
#
|
||||
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
||||
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# Copyright © 2018 Jonathan Hale <squareys@googlemail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
@ -168,37 +168,38 @@ find_path(SDL2_INCLUDE_DIR
|
|||
if(CORRADE_TARGET_WINDOWS)
|
||||
find_file(SDL2_DLL_RELEASE
|
||||
NAMES SDL2.dll
|
||||
PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
|
||||
PATH_SUFFIXES bin ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
|
||||
find_file(SDL2_DLL_DEBUG
|
||||
NAMES SDL2d.dll # not sure?
|
||||
PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
|
||||
PATH_SUFFIXES bin ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
|
||||
endif()
|
||||
|
||||
# (Static) macOS / iOS dependencies
|
||||
if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$")
|
||||
if(CORRADE_TARGET_IOS)
|
||||
# (Static) macOS / iOS dependencies. On macOS these were mainly needed when
|
||||
# building SDL statically using its CMake project, on iOS always.
|
||||
if(CORRADE_TARGET_APPLE AND (SDL2_LIBRARY_DEBUG MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$" OR SDL2_LIBRARY_RELEASE MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$"))
|
||||
set(_SDL2_FRAMEWORKS
|
||||
iconv # should be in the system, needed by iOS as well now
|
||||
AudioToolbox
|
||||
AVFoundation
|
||||
CoreHaptics # needed since 2.0.18(?) on iOS and macOS
|
||||
Foundation
|
||||
Metal # needed since 2.0.8 on iOS, since 2.0.14 on macOS
|
||||
GameController) # needed since 2.0.18(?) on macOS as well
|
||||
if(CORRADE_TARGET_IOS)
|
||||
list(APPEND _SDL2_FRAMEWORKS
|
||||
CoreBluetooth # needed since 2.0.10
|
||||
CoreGraphics
|
||||
CoreMotion
|
||||
Foundation
|
||||
GameController
|
||||
Metal # needed since 2.0.8
|
||||
QuartzCore
|
||||
UIKit)
|
||||
else()
|
||||
# Those are needed when building SDL statically using its CMake project
|
||||
set(_SDL2_FRAMEWORKS
|
||||
iconv # should be in the system
|
||||
AudioToolbox
|
||||
AVFoundation
|
||||
list(APPEND _SDL2_FRAMEWORKS
|
||||
Carbon
|
||||
Cocoa
|
||||
CoreAudio
|
||||
CoreVideo
|
||||
ForceFeedback
|
||||
Foundation
|
||||
IOKit)
|
||||
endif()
|
||||
set(_SDL2_FRAMEWORK_LIBRARIES )
|
||||
|
@ -247,7 +248,7 @@ if(NOT TARGET SDL2::SDL2)
|
|||
endif()
|
||||
|
||||
# Link frameworks on macOS / iOS if we have a static SDL
|
||||
if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY MATCHES ".*libSDL2.a$")
|
||||
if(CORRADE_TARGET_APPLE AND (SDL2_LIBRARY_DEBUG MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$" OR SDL2_LIBRARY_RELEASE MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$"))
|
||||
set_property(TARGET SDL2::SDL2 APPEND PROPERTY
|
||||
INTERFACE_LINK_LIBRARIES ${_SDL2_FRAMEWORK_LIBRARIES})
|
||||
endif()
|
||||
|
|
|
@ -11,4 +11,16 @@
|
|||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
|
||||
UTF-8
|
||||
</activeCodePage>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
true/pm</dpiAware> <!-- legacy -->
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||
permonitorv2,permonitor
|
||||
</dpiAwareness> <!-- falls back to pm if pmv2 is not available -->
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
|
@ -1,5 +1,5 @@
|
|||
# MassBuilderSaveTool
|
||||
# Copyright (C) 2021 Guillaume Jacquemin
|
||||
# Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
#
|
||||
# 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
|
||||
|
@ -18,9 +18,9 @@ set(CMAKE_CXX_STANDARD 14)
|
|||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set(SAVETOOL_PROJECT_VERSION 1.0.0)
|
||||
set(SAVETOOL_PROJECT_VERSION 1.4.3)
|
||||
|
||||
find_package(Corrade REQUIRED Main Containers Utility Interconnect)
|
||||
find_package(Corrade REQUIRED Main Containers Utility)
|
||||
find_package(Magnum REQUIRED GL Sdl2Application)
|
||||
find_package(MagnumIntegration REQUIRED ImGui)
|
||||
|
||||
|
@ -28,37 +28,171 @@ set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON)
|
|||
|
||||
corrade_add_resource(Assets assets.conf)
|
||||
|
||||
add_library(Logger STATIC EXCLUDE_FROM_ALL
|
||||
Logger/Logger.h
|
||||
Logger/Logger.cpp
|
||||
Logger/EntryType.h
|
||||
Logger/MagnumLogBuffer.h
|
||||
Logger/MagnumLogBuffer.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Logger PRIVATE
|
||||
Corrade::Utility
|
||||
Magnum::Magnum
|
||||
)
|
||||
|
||||
add_library(UESaveFile STATIC EXCLUDE_FROM_ALL
|
||||
UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h
|
||||
UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h
|
||||
UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h
|
||||
UESaveFile/Serialisers/ArrayPropertySerialiser.h
|
||||
UESaveFile/Serialisers/ArrayPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/BoolPropertySerialiser.h
|
||||
UESaveFile/Serialisers/BoolPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/BytePropertySerialiser.h
|
||||
UESaveFile/Serialisers/BytePropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/ColourPropertySerialiser.h
|
||||
UESaveFile/Serialisers/ColourPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/DateTimePropertySerialiser.h
|
||||
UESaveFile/Serialisers/DateTimePropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/EnumPropertySerialiser.h
|
||||
UESaveFile/Serialisers/EnumPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/FloatPropertySerialiser.h
|
||||
UESaveFile/Serialisers/FloatPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/GuidPropertySerialiser.h
|
||||
UESaveFile/Serialisers/GuidPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/IntPropertySerialiser.h
|
||||
UESaveFile/Serialisers/IntPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/MapPropertySerialiser.h
|
||||
UESaveFile/Serialisers/MapPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/ResourcePropertySerialiser.h
|
||||
UESaveFile/Serialisers/ResourcePropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/RotatorPropertySerialiser.h
|
||||
UESaveFile/Serialisers/RotatorPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/StringPropertySerialiser.h
|
||||
UESaveFile/Serialisers/StringPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/SetPropertySerialiser.h
|
||||
UESaveFile/Serialisers/SetPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/StructSerialiser.h
|
||||
UESaveFile/Serialisers/StructSerialiser.cpp
|
||||
UESaveFile/Serialisers/TextPropertySerialiser.h
|
||||
UESaveFile/Serialisers/TextPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/UnrealPropertySerialiser.h
|
||||
UESaveFile/Serialisers/VectorPropertySerialiser.h
|
||||
UESaveFile/Serialisers/VectorPropertySerialiser.cpp
|
||||
UESaveFile/Serialisers/Vector2DPropertySerialiser.h
|
||||
UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp
|
||||
|
||||
UESaveFile/Types/ArrayProperty.h
|
||||
UESaveFile/Types/BoolProperty.h
|
||||
UESaveFile/Types/ByteProperty.h
|
||||
UESaveFile/Types/ColourStructProperty.h
|
||||
UESaveFile/Types/DateTimeStructProperty.h
|
||||
UESaveFile/Types/EnumProperty.h
|
||||
UESaveFile/Types/FloatProperty.h
|
||||
UESaveFile/Types/GenericStructProperty.h
|
||||
UESaveFile/Types/GuidStructProperty.h
|
||||
UESaveFile/Types/IntProperty.h
|
||||
UESaveFile/Types/MapProperty.h
|
||||
UESaveFile/Types/NoneProperty.h
|
||||
UESaveFile/Types/RotatorStructProperty.h
|
||||
UESaveFile/Types/SetProperty.h
|
||||
UESaveFile/Types/StringProperty.h
|
||||
UESaveFile/Types/StructProperty.h
|
||||
UESaveFile/Types/ResourceItemValue.h
|
||||
UESaveFile/Types/TextProperty.h
|
||||
UESaveFile/Types/UnrealProperty.h
|
||||
UESaveFile/Types/UnrealPropertyBase.h
|
||||
UESaveFile/Types/VectorStructProperty.h
|
||||
|
||||
UESaveFile/Debug.h
|
||||
UESaveFile/Debug.cpp
|
||||
UESaveFile/UESaveFile.h
|
||||
UESaveFile/UESaveFile.cpp
|
||||
UESaveFile/BinaryReader.h
|
||||
UESaveFile/BinaryReader.cpp
|
||||
UESaveFile/BinaryWriter.h
|
||||
UESaveFile/BinaryWriter.cpp
|
||||
UESaveFile/PropertySerialiser.h
|
||||
UESaveFile/PropertySerialiser.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(UESaveFile PRIVATE
|
||||
Corrade::Containers
|
||||
Corrade::Utility
|
||||
Magnum::Magnum
|
||||
Logger
|
||||
)
|
||||
|
||||
add_executable(MassBuilderSaveTool WIN32
|
||||
main.cpp
|
||||
SaveTool/SaveTool.h
|
||||
SaveTool/SaveTool.cpp
|
||||
SaveTool/SaveTool_drawAbout.cpp
|
||||
SaveTool/SaveTool_drawMainMenu.cpp
|
||||
SaveTool/SaveTool_FileWatcher.cpp
|
||||
SaveTool/SaveTool_Initialisation.cpp
|
||||
SaveTool/SaveTool_MainManager.cpp
|
||||
SaveTool/SaveTool_MassViewer.cpp
|
||||
SaveTool/SaveTool_MassViewer_Frame.cpp
|
||||
SaveTool/SaveTool_MassViewer_Armour.cpp
|
||||
SaveTool/SaveTool_MassViewer_Weapons.cpp
|
||||
SaveTool/SaveTool_ProfileManager.cpp
|
||||
MassBuilderManager/MassBuilderManager.h
|
||||
MassBuilderManager/MassBuilderManager.cpp
|
||||
SaveTool/SaveTool_UpdateChecker.cpp
|
||||
ProfileManager/ProfileManager.h
|
||||
ProfileManager/ProfileManager.cpp
|
||||
Profile/Profile.h
|
||||
Profile/Profile.cpp
|
||||
Profile/PropertyNames.h
|
||||
Profile/ResourceIDs.h
|
||||
MassManager/MassManager.h
|
||||
MassManager/MassManager.cpp
|
||||
Mass/Accessory.h
|
||||
Mass/ArmourPart.h
|
||||
Mass/BulletLauncherAttachment.h
|
||||
Mass/CustomStyle.h
|
||||
Mass/Decal.h
|
||||
Mass/Joints.h
|
||||
Mass/Mass.h
|
||||
Mass/Mass.cpp
|
||||
Mass/Mass_Frame.cpp
|
||||
Mass/Mass_Armour.cpp
|
||||
Mass/Mass_Weapons.cpp
|
||||
Mass/Mass_Styles.cpp
|
||||
Mass/Mass_DecalsAccessories.cpp
|
||||
Mass/PropertyNames.h
|
||||
Mass/Weapon.h
|
||||
Mass/Weapon.cpp
|
||||
Mass/WeaponPart.h
|
||||
Maps/Accessories.h
|
||||
Maps/ArmourSets.h
|
||||
Maps/ArmourSlots.hpp
|
||||
Maps/BulletLauncherAttachmentStyles.hpp
|
||||
Maps/BulletLauncherSockets.hpp
|
||||
Maps/DamageTypes.hpp
|
||||
Maps/EffectColourModes.hpp
|
||||
Maps/LastMissionId.h
|
||||
Maps/StoryProgress.h
|
||||
Maps/StyleNames.h
|
||||
Maps/WeaponParts.h
|
||||
Maps/WeaponTypes.hpp
|
||||
ToastQueue/ToastQueue.h
|
||||
ToastQueue/ToastQueue.cpp
|
||||
Utilities/Crc32.h
|
||||
FontAwesome/IconsFontAwesome5.h
|
||||
FontAwesome/IconsFontAwesome5Brands.h
|
||||
resource.rc
|
||||
${Assets})
|
||||
${Assets}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
add_compile_definitions(SAVETOOL_DEBUG_BUILD)
|
||||
endif()
|
||||
add_compile_definitions(SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}"
|
||||
SAVETOOL_CODENAME="Agonising Quark"
|
||||
SUPPORTED_GAME_VERSION="0.7.6")
|
||||
add_compile_definitions(
|
||||
SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}"
|
||||
SAVETOOL_CODENAME="Enigmatic Ellenier"
|
||||
SUPPORTED_GAME_VERSION="0.9.x"
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
||||
set_target_properties(MassBuilderSaveTool PROPERTIES OUTPUT_NAME MassBuilderSaveTool-${SAVETOOL_PROJECT_VERSION})
|
||||
|
@ -66,16 +200,23 @@ endif()
|
|||
|
||||
target_link_options(MassBuilderSaveTool PRIVATE -static -static-libgcc -static-libstdc++)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
||||
target_link_options(MassBuilderSaveTool PRIVATE -Wl,-S)
|
||||
endif()
|
||||
|
||||
target_link_libraries(MassBuilderSaveTool PRIVATE
|
||||
Corrade::Containers
|
||||
Corrade::Utility
|
||||
Corrade::Interconnect
|
||||
Corrade::Main
|
||||
Magnum::Magnum
|
||||
Magnum::GL
|
||||
Magnum::Sdl2Application
|
||||
MagnumIntegration::ImGui
|
||||
Logger
|
||||
UESaveFile
|
||||
efsw
|
||||
zip
|
||||
libcurl
|
||||
imm32
|
||||
wtsapi32)
|
||||
wtsapi32
|
||||
)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
enum class EntryType {
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
|
@ -0,0 +1,112 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <Corrade/Utility/Debug.h>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
using Containers::Array;
|
||||
using Utility::Debug;
|
||||
using Utility::Warning;
|
||||
using Utility::Error;
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
Logger&
|
||||
Logger::instance() {
|
||||
static Logger logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void
|
||||
Logger::initialise() {
|
||||
#ifndef SAVETOOL_DEBUG_BUILD
|
||||
_logFile.open("SaveToolLog.txt", std::ios::trunc);
|
||||
_logFile << "In case you encounter a bug:\n" <<
|
||||
"1. Do not run the Save Tool again, as this log will be cleared.\n" <<
|
||||
"2. Go to either the official Sekai Project Discord guild, or the community M.A.S.S. Builder one.\n" <<
|
||||
"3. Mention me (William JCM#2301) to get my attention, with a description of the bug.\n"
|
||||
" Please include as many details as possible, I don't want to play \"20 questions\", and neither do you.\n" <<
|
||||
"4. Send me this file _when I ask for it_, preferably in DMs.\n" <<
|
||||
std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Logger::indent() {
|
||||
_indentLevel++;
|
||||
}
|
||||
|
||||
void
|
||||
Logger::unindent() {
|
||||
if(_indentLevel > 0) {
|
||||
_indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Logger::log(EntryType type, StringView location, StringView message) {
|
||||
Debug d{
|
||||
#ifndef SAVETOOL_DEBUG_BUILD
|
||||
&_logFile
|
||||
#else
|
||||
&std::cout
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#define COLOURED_TEXT(colour, text) Debug::color(Debug::Color::colour) << (text) << Debug::resetColor
|
||||
#else
|
||||
#define COLOURED_TEXT(colour, text) (text)
|
||||
#endif
|
||||
switch(type) {
|
||||
case EntryType::Info:
|
||||
d << COLOURED_TEXT(Default, "[ INFO]"_s);
|
||||
break;
|
||||
case EntryType::Warning:
|
||||
d << COLOURED_TEXT(Yellow, "[WARNING]"_s);
|
||||
break;
|
||||
case EntryType::Error:
|
||||
d << COLOURED_TEXT(Red, "[ ERROR]"_s);
|
||||
break;
|
||||
}
|
||||
#undef COLOURED_TEXT
|
||||
|
||||
d << "["_s << Debug::nospace << location << Debug::nospace << "]";
|
||||
|
||||
for(UnsignedInt i = 0; i < _indentLevel; i++) {
|
||||
d << Debug::nospace << " "_s << Debug::nospace;
|
||||
}
|
||||
|
||||
d << ((message.back() == '\n') ? message.exceptSuffix(1) : message);
|
||||
}
|
||||
|
||||
void
|
||||
Logger::lockMutex() {
|
||||
_logMutex.lock();
|
||||
}
|
||||
|
||||
void
|
||||
Logger::unlockMutex() {
|
||||
_logMutex.unlock();
|
||||
}
|
||||
|
||||
Logger&
|
||||
logger() {
|
||||
return Logger::instance();
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include <mutex>
|
||||
#ifndef SAVETOOL_DEBUG_BUILD
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include <Corrade/Containers/String.h>
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "EntryType.h"
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
using Containers::ArrayView;
|
||||
using Containers::String;
|
||||
using Containers::StringView;
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
Logger(const Logger&) = delete;
|
||||
Logger& operator=(const Logger&) = delete;
|
||||
|
||||
Logger(Logger&&) = delete;
|
||||
Logger& operator=(Logger&&) = delete;
|
||||
|
||||
static auto instance() -> Logger&;
|
||||
|
||||
void initialise();
|
||||
|
||||
void indent();
|
||||
void unindent();
|
||||
|
||||
void log(EntryType type, StringView location, StringView message);
|
||||
|
||||
void lockMutex();
|
||||
void unlockMutex();
|
||||
|
||||
private:
|
||||
Logger() = default;
|
||||
|
||||
#ifndef SAVETOOL_DEBUG_BUILD
|
||||
std::ofstream _logFile;
|
||||
#endif
|
||||
|
||||
UnsignedInt _indentLevel = 0;
|
||||
|
||||
std::mutex _logMutex{};
|
||||
};
|
||||
|
||||
auto logger() -> Logger&;
|
||||
|
||||
#define LOG(entry_type, message) logger().lockMutex(); \
|
||||
logger().log(EntryType::entry_type, \
|
||||
Utility::format("{}:{}", StringView{__builtin_FILE()}.find("src"_s).data() + 4, __builtin_LINE()), \
|
||||
message); \
|
||||
logger().unlockMutex()
|
||||
|
||||
#define LOG_INFO(message) LOG(Info, message)
|
||||
#define LOG_WARNING(message) LOG(Warning, message)
|
||||
#define LOG_ERROR(message) LOG(Error, message)
|
||||
|
||||
#define LOG_INFO_FORMAT(message, ...) LOG_INFO(Utility::format(message, __VA_ARGS__))
|
||||
#define LOG_WARNING_FORMAT(message, ...) LOG_WARNING(Utility::format(message, __VA_ARGS__))
|
||||
#define LOG_ERROR_FORMAT(message, ...) LOG_ERROR(Utility::format(message, __VA_ARGS__))
|
|
@ -0,0 +1,30 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "MagnumLogBuffer.h"
|
||||
|
||||
MagnumLogBuffer::MagnumLogBuffer(EntryType type): std::stringbuf(std::ios_base::out), _type{type} {}
|
||||
|
||||
MagnumLogBuffer::~MagnumLogBuffer() = default;
|
||||
|
||||
int
|
||||
MagnumLogBuffer::sync() {
|
||||
logger().lockMutex();
|
||||
logger().log(_type, "Corrade/Magnum"_s, str().c_str());
|
||||
logger().unlockMutex();
|
||||
str({});
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include "EntryType.h"
|
||||
|
||||
class MagnumLogBuffer : public std::stringbuf {
|
||||
public:
|
||||
explicit MagnumLogBuffer(EntryType type);
|
||||
~MagnumLogBuffer();
|
||||
|
||||
private:
|
||||
int sync() override;
|
||||
|
||||
EntryType _type;
|
||||
};
|
|
@ -0,0 +1,684 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
using namespace Magnum;
|
||||
|
||||
enum AccessorySize {
|
||||
S,
|
||||
M,
|
||||
L,
|
||||
XL
|
||||
};
|
||||
|
||||
struct AccessoryData{
|
||||
Containers::StringView name;
|
||||
AccessorySize size = AccessorySize::S;
|
||||
};
|
||||
|
||||
static const std::map<Int, AccessoryData> accessories {
|
||||
// region Primitives
|
||||
{1, {"Cube"_s, AccessorySize::S}},
|
||||
{2, {"Pentagon"_s, AccessorySize::S}},
|
||||
{3, {"Hexagon"_s, AccessorySize::S}},
|
||||
{4, {"Cylinder"_s, AccessorySize::S}},
|
||||
{5, {"Sphere"_s, AccessorySize::S}},
|
||||
{6, {"TriPyramid"_s, AccessorySize::S}},
|
||||
{7, {"SquPyramid"_s, AccessorySize::S}},
|
||||
{8, {"PenPyramid"_s, AccessorySize::S}},
|
||||
{9, {"HexPyramid"_s, AccessorySize::S}},
|
||||
{10, {"Cone"_s, AccessorySize::S}},
|
||||
{11, {"SquStick"_s, AccessorySize::S}},
|
||||
{12, {"PenStick"_s, AccessorySize::S}},
|
||||
{13, {"HexStick"_s, AccessorySize::S}},
|
||||
{14, {"CycStick"_s, AccessorySize::S}},
|
||||
{15, {"Capsule"_s, AccessorySize::S}},
|
||||
{16, {"Decal Pad 01"_s, AccessorySize::S}},
|
||||
{17, {"Decal Pad 02"_s, AccessorySize::S}},
|
||||
{18, {"Decal Pad 03"_s, AccessorySize::S}},
|
||||
{19, {"Decal Pad 04"_s, AccessorySize::S}},
|
||||
{20, {"Decal Pad 05"_s, AccessorySize::S}},
|
||||
{21, {"Triangle"_s, AccessorySize::S}},
|
||||
{22, {"ThinStar"_s, AccessorySize::S}},
|
||||
{23, {"Star"_s, AccessorySize::S}},
|
||||
{24, {"SixSideStar"_s, AccessorySize::S}},
|
||||
{25, {"Asterisk"_s, AccessorySize::S}},
|
||||
{26, {"Ring"_s, AccessorySize::S}},
|
||||
{27, {"SawedRing"_s, AccessorySize::S}},
|
||||
{28, {"HalfRing"_s, AccessorySize::S}},
|
||||
{29, {"Cresent"_s, AccessorySize::S}},
|
||||
{30, {"Donut"_s, AccessorySize::S}},
|
||||
{31, {"FiveCogWheel"_s, AccessorySize::S}},
|
||||
{32, {"SixCogWheel"_s, AccessorySize::S}},
|
||||
{33, {"SevenCogWheel"_s, AccessorySize::S}},
|
||||
{34, {"EightCogWheel"_s, AccessorySize::S}},
|
||||
{35, {"TwelveCogWheel"_s, AccessorySize::S}},
|
||||
|
||||
{51, {"SquBevel"_s, AccessorySize::S}},
|
||||
{52, {"TriBevel"_s, AccessorySize::S}},
|
||||
{53, {"PenBevel"_s, AccessorySize::S}},
|
||||
{54, {"HexBevel"_s, AccessorySize::S}},
|
||||
{55, {"CycBevel"_s, AccessorySize::S}},
|
||||
{56, {"RecBevel"_s, AccessorySize::S}},
|
||||
{57, {"DaiBevel"_s, AccessorySize::S}},
|
||||
{58, {"MonBevel"_s, AccessorySize::S}},
|
||||
{59, {"CofBevel"_s, AccessorySize::S}},
|
||||
{60, {"JevBevel"_s, AccessorySize::S}},
|
||||
{61, {"SquEmboss"_s, AccessorySize::S}},
|
||||
{62, {"TriEmboss"_s, AccessorySize::S}},
|
||||
{63, {"PenEmboss"_s, AccessorySize::S}},
|
||||
{64, {"HexEmboss"_s, AccessorySize::S}},
|
||||
{65, {"CycEmboss"_s, AccessorySize::S}},
|
||||
{66, {"RecEmboss"_s, AccessorySize::S}},
|
||||
{67, {"DaiEmboss"_s, AccessorySize::S}},
|
||||
{68, {"MonEmboss"_s, AccessorySize::S}},
|
||||
{69, {"CofEmboss"_s, AccessorySize::S}},
|
||||
{70, {"JevEmboss"_s, AccessorySize::S}},
|
||||
|
||||
{101, {"Flat Hex Pin"_s, AccessorySize::S}},
|
||||
{102, {"Cross Circle Pin"_s, AccessorySize::S}},
|
||||
{103, {"Flat Circle Pin"_s, AccessorySize::S}},
|
||||
{104, {"Hex Circle Pin"_s, AccessorySize::S}},
|
||||
{105, {"Circle Button Pin"_s, AccessorySize::S}},
|
||||
{106, {"Hexagon Pin"_s, AccessorySize::S}},
|
||||
{107, {"Cross Square Pin"_s, AccessorySize::S}},
|
||||
{108, {"Flat Square Pin"_s, AccessorySize::S}},
|
||||
{109, {"Quad Corner Pin"_s, AccessorySize::S}},
|
||||
{110, {"Bi Corner Pin"_s, AccessorySize::S}},
|
||||
{111, {"Circle Pin"_s, AccessorySize::S}},
|
||||
{112, {"Flat End Pin"_s, AccessorySize::S}},
|
||||
{113, {"Flat Cut Pin"_s, AccessorySize::S}},
|
||||
{114, {"Radial Pin"_s, AccessorySize::S}},
|
||||
{115, {"Diamiter Pin"_s, AccessorySize::S}},
|
||||
|
||||
{151, {"TriPoint"_s, AccessorySize::S}},
|
||||
{152, {"SquPoint"_s, AccessorySize::S}},
|
||||
{153, {"PenPoint"_s, AccessorySize::S}},
|
||||
{154, {"HexPoint"_s, AccessorySize::S}},
|
||||
{155, {"CycPoint"_s, AccessorySize::S}},
|
||||
{156, {"Bevel SquCutPoint"_s, AccessorySize::S}},
|
||||
{157, {"Bevel HexCutPoint"_s, AccessorySize::S}},
|
||||
{158, {"Bevel HexPoint"_s, AccessorySize::S}},
|
||||
{159, {"Bevel CycCutPoint"_s, AccessorySize::S}},
|
||||
{160, {"Bevel CycPoint"_s, AccessorySize::S}},
|
||||
|
||||
{201, {"Shaped Edge 01"_s, AccessorySize::M}},
|
||||
{202, {"Shaped Edge 02"_s, AccessorySize::M}},
|
||||
{203, {"Shaped Edge 03"_s, AccessorySize::M}},
|
||||
{204, {"Shaped Edge 04"_s, AccessorySize::M}},
|
||||
{205, {"Shaped Edge 05"_s, AccessorySize::M}},
|
||||
{206, {"Shaped Edge 06"_s, AccessorySize::M}},
|
||||
{207, {"Shaped Edge 07"_s, AccessorySize::M}},
|
||||
{208, {"Shaped Edge 08"_s, AccessorySize::M}},
|
||||
{209, {"Shaped Edge 09"_s, AccessorySize::M}},
|
||||
{210, {"Shaped Edge 10"_s, AccessorySize::M}},
|
||||
{211, {"Shaped Edge 11"_s, AccessorySize::M}},
|
||||
{212, {"Shaped Edge 12"_s, AccessorySize::M}},
|
||||
{213, {"Shaped Edge 13"_s, AccessorySize::M}},
|
||||
{214, {"Shaped Edge 14"_s, AccessorySize::M}},
|
||||
{215, {"Shaped Edge 15"_s, AccessorySize::M}},
|
||||
{216, {"Shaped Edge 16"_s, AccessorySize::M}},
|
||||
{217, {"Shaped Edge 17"_s, AccessorySize::M}},
|
||||
{218, {"Shaped Edge 18"_s, AccessorySize::M}},
|
||||
{219, {"Shaped Edge 19"_s, AccessorySize::M}},
|
||||
{220, {"Shaped Edge 20"_s, AccessorySize::M}},
|
||||
|
||||
{251, {"Fish Tail 01"_s, AccessorySize::M}},
|
||||
{252, {"Fish Tail 02"_s, AccessorySize::M}},
|
||||
{253, {"Fish Tail 03"_s, AccessorySize::M}},
|
||||
{254, {"Fish Tail 04"_s, AccessorySize::M}},
|
||||
{255, {"Fish Tail 05"_s, AccessorySize::M}},
|
||||
{256, {"Based Separator 01"_s, AccessorySize::M}},
|
||||
{257, {"Based Separator 02"_s, AccessorySize::M}},
|
||||
{258, {"Based Separator 03"_s, AccessorySize::M}},
|
||||
{259, {"Based Separator 04"_s, AccessorySize::M}},
|
||||
{260, {"Based Separator 05"_s, AccessorySize::M}},
|
||||
{261, {"Based Separator 06"_s, AccessorySize::M}},
|
||||
{262, {"Based Separator 07"_s, AccessorySize::M}},
|
||||
{263, {"Based Separator 08"_s, AccessorySize::M}},
|
||||
{264, {"Based Separator 09"_s, AccessorySize::M}},
|
||||
{265, {"Based Separator 10"_s, AccessorySize::M}},
|
||||
|
||||
{301, {"Rectangular Box 01"_s, AccessorySize::M}},
|
||||
{302, {"Rectangular Box 02"_s, AccessorySize::M}},
|
||||
{303, {"Rectangular Box 03"_s, AccessorySize::M}},
|
||||
{304, {"Rectangular Box 04"_s, AccessorySize::M}},
|
||||
{305, {"Rectangular Box 05"_s, AccessorySize::M}},
|
||||
{306, {"CofBox 01"_s, AccessorySize::M}},
|
||||
{307, {"CofBox 02"_s, AccessorySize::M}},
|
||||
{308, {"CofBox 03"_s, AccessorySize::M}},
|
||||
{309, {"CofBox 04"_s, AccessorySize::M}},
|
||||
{310, {"CofBox 05"_s, AccessorySize::M}},
|
||||
{311, {"Triangular Box 01"_s, AccessorySize::M}},
|
||||
{312, {"Triangular Box 02"_s, AccessorySize::M}},
|
||||
{313, {"Triangular Box 03"_s, AccessorySize::M}},
|
||||
{314, {"Triangular Box 04"_s, AccessorySize::M}},
|
||||
{315, {"Triangular Box 05"_s, AccessorySize::M}},
|
||||
{316, {"Diagonal Box A01"_s, AccessorySize::M}},
|
||||
{317, {"Diagonal Box A02"_s, AccessorySize::M}},
|
||||
{318, {"Diagonal Box A03"_s, AccessorySize::M}},
|
||||
{319, {"Diagonal Box A04"_s, AccessorySize::M}},
|
||||
{320, {"Diagonal Box A05"_s, AccessorySize::M}},
|
||||
{321, {"Diagonal Box B01"_s, AccessorySize::M}},
|
||||
{322, {"Diagonal Box B02"_s, AccessorySize::M}},
|
||||
{323, {"Diagonal Box B03"_s, AccessorySize::M}},
|
||||
{324, {"Diagonal Box B04"_s, AccessorySize::M}},
|
||||
{325, {"Diagonal Box B05"_s, AccessorySize::M}},
|
||||
// endregion
|
||||
|
||||
// region Armours
|
||||
{1001, {"Short Layer 01"_s, AccessorySize::M}},
|
||||
{1002, {"Short Layer 02"_s, AccessorySize::M}},
|
||||
{1003, {"Short Layer 03"_s, AccessorySize::M}},
|
||||
{1004, {"Short Layer 04"_s, AccessorySize::M}},
|
||||
{1005, {"Short Layer 05"_s, AccessorySize::M}},
|
||||
{1006, {"Long Layer 01"_s, AccessorySize::M}},
|
||||
{1007, {"Long Layer 02"_s, AccessorySize::M}},
|
||||
{1008, {"Long Layer 03"_s, AccessorySize::M}},
|
||||
{1009, {"Long Layer 04"_s, AccessorySize::M}},
|
||||
{1010, {"Long Layer 05"_s, AccessorySize::M}},
|
||||
{1011, {"Diagonal Long Layer 01"_s, AccessorySize::M}},
|
||||
{1012, {"Diagonal Long Layer 02"_s, AccessorySize::M}},
|
||||
{1013, {"Diagonal Long Layer 03"_s, AccessorySize::M}},
|
||||
{1014, {"Diagonal Long Layer 04"_s, AccessorySize::M}},
|
||||
{1015, {"Diagonal Long Layer 05"_s, AccessorySize::M}},
|
||||
|
||||
{1051, {"Sloped Layer 01"_s, AccessorySize::M}},
|
||||
{1052, {"Sloped Layer 02"_s, AccessorySize::M}},
|
||||
{1053, {"Sloped Layer 03"_s, AccessorySize::M}},
|
||||
{1054, {"Sloped Layer 04"_s, AccessorySize::M}},
|
||||
{1055, {"Sloped Layer 05"_s, AccessorySize::M}},
|
||||
{1056, {"Sloped Layer 06"_s, AccessorySize::M}},
|
||||
{1057, {"Sloped Layer 07"_s, AccessorySize::M}},
|
||||
{1058, {"Sloped Layer 08"_s, AccessorySize::M}},
|
||||
{1059, {"Sloped Layer 09"_s, AccessorySize::M}},
|
||||
{1060, {"Sloped Layer 10"_s, AccessorySize::M}},
|
||||
{1061, {"Sloped Layer 11"_s, AccessorySize::M}},
|
||||
{1062, {"Sloped Layer 12"_s, AccessorySize::M}},
|
||||
{1063, {"Sloped Layer 13"_s, AccessorySize::M}},
|
||||
{1064, {"Sloped Layer 14"_s, AccessorySize::M}},
|
||||
{1065, {"Sloped Layer 15"_s, AccessorySize::M}},
|
||||
|
||||
{1101, {"Raised Center 01"_s, AccessorySize::M}},
|
||||
{1102, {"Raised Center 02"_s, AccessorySize::M}},
|
||||
{1103, {"Raised Center 03"_s, AccessorySize::M}},
|
||||
{1104, {"Raised Center 04"_s, AccessorySize::M}},
|
||||
{1105, {"Raised Center 05"_s, AccessorySize::M}},
|
||||
{1106, {"Raised Block 01"_s, AccessorySize::M}},
|
||||
{1107, {"Raised Block 02"_s, AccessorySize::M}},
|
||||
{1108, {"Raised Block 03"_s, AccessorySize::M}},
|
||||
{1109, {"Raised Pointed"_s, AccessorySize::M}},
|
||||
{1110, {"Raised Cover"_s, AccessorySize::M}},
|
||||
{1111, {"Raised Slant 01"_s, AccessorySize::M}},
|
||||
{1112, {"Raised Slant 02"_s, AccessorySize::M}},
|
||||
{1113, {"Raised Slant 03"_s, AccessorySize::M}},
|
||||
{1114, {"Raised Slant 04"_s, AccessorySize::M}},
|
||||
{1115, {"Raised Slant 05"_s, AccessorySize::M}},
|
||||
|
||||
{1151, {"Wide Patch 01"_s, AccessorySize::L}},
|
||||
{1152, {"Wide Patch 02"_s, AccessorySize::L}},
|
||||
{1153, {"Wide Patch 03"_s, AccessorySize::L}},
|
||||
{1154, {"Wide Patch 04"_s, AccessorySize::L}},
|
||||
{1155, {"Wide Patch 05"_s, AccessorySize::L}},
|
||||
|
||||
{1201, {"Pointed Armour 01"_s, AccessorySize::L}},
|
||||
{1202, {"Pointed Armour 02"_s, AccessorySize::L}},
|
||||
{1203, {"Pointed Armour 03"_s, AccessorySize::L}},
|
||||
{1204, {"Pointed Armour 04"_s, AccessorySize::L}},
|
||||
{1205, {"Pointed Armour 05"_s, AccessorySize::L}},
|
||||
{1206, {"Pointed Armour 06"_s, AccessorySize::L}},
|
||||
{1207, {"Pointed Armour 07"_s, AccessorySize::L}},
|
||||
{1208, {"Pointed Armour 08"_s, AccessorySize::L}},
|
||||
{1209, {"Pointed Armour 09"_s, AccessorySize::L}},
|
||||
{1210, {"Pointed Armour 10"_s, AccessorySize::L}},
|
||||
{1211, {"Pointed Armour 11"_s, AccessorySize::L}},
|
||||
{1212, {"Pointed Armour 12"_s, AccessorySize::L}},
|
||||
{1213, {"Pointed Armour 13"_s, AccessorySize::L}},
|
||||
{1214, {"Pointed Armour 14"_s, AccessorySize::L}},
|
||||
{1215, {"Pointed Armour 15"_s, AccessorySize::L}},
|
||||
|
||||
{1251, {"E Limb Cover 01"_s, AccessorySize::L}},
|
||||
{1252, {"E Limb Cover 02"_s, AccessorySize::L}},
|
||||
{1253, {"E Limb Cover 03"_s, AccessorySize::L}},
|
||||
{1254, {"E Limb Cover 04"_s, AccessorySize::L}},
|
||||
{1255, {"E Limb Cover 05"_s, AccessorySize::L}},
|
||||
{1256, {"E Limb Cover 06"_s, AccessorySize::L}},
|
||||
{1257, {"E Limb Cover 07"_s, AccessorySize::L}},
|
||||
{1258, {"E Limb Cover 08"_s, AccessorySize::L}},
|
||||
{1259, {"E Limb Cover 09"_s, AccessorySize::L}},
|
||||
{1260, {"E Limb Cover 10"_s, AccessorySize::L}},
|
||||
|
||||
{1301, {"C Limb Cover 01"_s, AccessorySize::L}},
|
||||
{1302, {"C Limb Cover 02"_s, AccessorySize::L}},
|
||||
{1303, {"C Limb Cover 03"_s, AccessorySize::L}},
|
||||
{1304, {"C Limb Cover 04"_s, AccessorySize::L}},
|
||||
{1305, {"C Limb Cover 05"_s, AccessorySize::L}},
|
||||
{1306, {"C Limb Cover 06"_s, AccessorySize::L}},
|
||||
{1307, {"C Limb Cover 07"_s, AccessorySize::L}},
|
||||
{1308, {"C Limb Cover 08"_s, AccessorySize::L}},
|
||||
{1309, {"C Limb Cover 09"_s, AccessorySize::L}},
|
||||
{1310, {"C Limb Cover 10"_s, AccessorySize::L}},
|
||||
{1311, {"C Limb Cover 11"_s, AccessorySize::L}},
|
||||
{1312, {"C Limb Cover 12"_s, AccessorySize::L}},
|
||||
{1313, {"C Limb Cover 13"_s, AccessorySize::L}},
|
||||
{1314, {"C Limb Cover 14"_s, AccessorySize::L}},
|
||||
{1315, {"C Limb Cover 15"_s, AccessorySize::L}},
|
||||
{1316, {"C Limb Cover 16"_s, AccessorySize::L}},
|
||||
{1317, {"C Limb Cover 17"_s, AccessorySize::L}},
|
||||
{1318, {"C Limb Cover 18"_s, AccessorySize::L}},
|
||||
{1319, {"C Limb Cover 19"_s, AccessorySize::L}},
|
||||
{1320, {"C Limb Cover 20"_s, AccessorySize::L}},
|
||||
|
||||
{1351, {"P Limb Cover 01"_s, AccessorySize::XL}},
|
||||
{1352, {"P Limb Cover 02"_s, AccessorySize::XL}},
|
||||
{1353, {"P Limb Cover 03"_s, AccessorySize::XL}},
|
||||
{1354, {"P Limb Cover 04"_s, AccessorySize::XL}},
|
||||
{1355, {"P Limb Cover 05"_s, AccessorySize::XL}},
|
||||
|
||||
{1401, {"Flat Cover 01"_s, AccessorySize::XL}},
|
||||
{1402, {"Flat Cover 02"_s, AccessorySize::XL}},
|
||||
{1403, {"Flat Cover 03"_s, AccessorySize::XL}},
|
||||
{1404, {"Flat Cover 04"_s, AccessorySize::XL}},
|
||||
{1405, {"Flat Cover 05"_s, AccessorySize::XL}},
|
||||
{1406, {"Flat Cover 06"_s, AccessorySize::XL}},
|
||||
{1407, {"Flat Cover 07"_s, AccessorySize::XL}},
|
||||
{1408, {"Flat Cover 08"_s, AccessorySize::XL}},
|
||||
{1409, {"Flat Cover 09"_s, AccessorySize::XL}},
|
||||
{1410, {"Flat Cover 10"_s, AccessorySize::XL}},
|
||||
|
||||
{1451, {"L Side Opening 01"_s, AccessorySize::XL}},
|
||||
{1452, {"L Side Opening 02"_s, AccessorySize::XL}},
|
||||
{1453, {"L Side Opening 03"_s, AccessorySize::XL}},
|
||||
{1454, {"L Side Opening 04"_s, AccessorySize::XL}},
|
||||
{1455, {"L Side Opening 05"_s, AccessorySize::XL}},
|
||||
{1456, {"L Side Opening 06"_s, AccessorySize::XL}},
|
||||
{1457, {"L Side Opening 07"_s, AccessorySize::XL}},
|
||||
{1458, {"L Side Opening 08"_s, AccessorySize::XL}},
|
||||
{1459, {"L Side Opening 09"_s, AccessorySize::XL}},
|
||||
{1460, {"L Side Opening 10"_s, AccessorySize::XL}},
|
||||
// endregion
|
||||
|
||||
// region Components
|
||||
{2001, {"Disc Padding 01"_s, AccessorySize::M}},
|
||||
{2002, {"Disc Padding 02"_s, AccessorySize::M}},
|
||||
{2003, {"Disc Padding 03"_s, AccessorySize::M}},
|
||||
{2004, {"Disc Padding 04"_s, AccessorySize::M}},
|
||||
{2005, {"Disc Padding 05"_s, AccessorySize::M}},
|
||||
{2006, {"Thin Padding 01"_s, AccessorySize::M}},
|
||||
{2007, {"Thin Padding 02"_s, AccessorySize::M}},
|
||||
{2008, {"Thin Padding 03"_s, AccessorySize::M}},
|
||||
{2009, {"Thin Padding 04"_s, AccessorySize::M}},
|
||||
{2010, {"Thin Padding 05"_s, AccessorySize::M}},
|
||||
{2011, {"Thick Padding 01"_s, AccessorySize::M}},
|
||||
{2012, {"Thick Padding 02"_s, AccessorySize::M}},
|
||||
{2013, {"Thick Padding 03"_s, AccessorySize::M}},
|
||||
{2014, {"Thick Padding 04"_s, AccessorySize::M}},
|
||||
{2015, {"Thick Padding 05"_s, AccessorySize::M}},
|
||||
{2016, {"Thick Padding 06"_s, AccessorySize::M}},
|
||||
{2017, {"Thick Padding 07"_s, AccessorySize::M}},
|
||||
{2018, {"Thick Padding 08"_s, AccessorySize::M}},
|
||||
{2019, {"Thick Padding 09"_s, AccessorySize::M}},
|
||||
{2020, {"Thick Padding 10"_s, AccessorySize::M}},
|
||||
{2021, {"CSide Padding 01"_s, AccessorySize::M}},
|
||||
{2022, {"CSide Padding 02"_s, AccessorySize::M}},
|
||||
{2023, {"CSide Padding 03"_s, AccessorySize::M}},
|
||||
{2024, {"CSide Padding 04"_s, AccessorySize::M}},
|
||||
{2025, {"CSide Padding 05"_s, AccessorySize::M}},
|
||||
|
||||
{2051, {"Container 01"_s, AccessorySize::L}},
|
||||
{2052, {"Container 02"_s, AccessorySize::L}},
|
||||
{2053, {"Container 03"_s, AccessorySize::L}},
|
||||
{2054, {"Container 04"_s, AccessorySize::L}},
|
||||
{2055, {"Container 05"_s, AccessorySize::L}},
|
||||
|
||||
{2101, {"Plating 01"_s, AccessorySize::L}},
|
||||
{2102, {"Plating 02"_s, AccessorySize::L}},
|
||||
{2103, {"Plating 03"_s, AccessorySize::L}},
|
||||
{2104, {"Plating 04"_s, AccessorySize::L}},
|
||||
{2105, {"Plating 05"_s, AccessorySize::L}},
|
||||
|
||||
{2151, {"Complex Base 01"_s, AccessorySize::L}},
|
||||
{2152, {"Complex Base 02"_s, AccessorySize::L}},
|
||||
{2153, {"Complex Base 03"_s, AccessorySize::L}},
|
||||
{2154, {"Complex Base 04"_s, AccessorySize::L}},
|
||||
{2155, {"Complex Base 05"_s, AccessorySize::L}},
|
||||
{2156, {"Complex Base 06"_s, AccessorySize::L}},
|
||||
{2157, {"Complex Base 07"_s, AccessorySize::L}},
|
||||
{2158, {"Complex Base 08"_s, AccessorySize::L}},
|
||||
{2159, {"Complex Base 09"_s, AccessorySize::L}},
|
||||
{2160, {"Complex Base 10"_s, AccessorySize::L}},
|
||||
|
||||
{2201, {"Long Base 01"_s, AccessorySize::XL}},
|
||||
{2202, {"Long Base 02"_s, AccessorySize::XL}},
|
||||
{2203, {"Long Base 03"_s, AccessorySize::XL}},
|
||||
{2204, {"Long Base 04"_s, AccessorySize::XL}},
|
||||
{2205, {"Long Base 05"_s, AccessorySize::XL}},
|
||||
|
||||
{2251, {"Straight Wing 01"_s, AccessorySize::XL}},
|
||||
{2252, {"Straight Wing 02"_s, AccessorySize::XL}},
|
||||
{2253, {"Straight Wing 03"_s, AccessorySize::XL}},
|
||||
{2254, {"Straight Wing 04"_s, AccessorySize::XL}},
|
||||
{2255, {"Straight Wing 05"_s, AccessorySize::XL}},
|
||||
{2256, {"Straight Wing 06"_s, AccessorySize::XL}},
|
||||
{2257, {"Straight Wing 07"_s, AccessorySize::XL}},
|
||||
{2258, {"Straight Wing 08"_s, AccessorySize::XL}},
|
||||
{2259, {"Straight Wing 09"_s, AccessorySize::XL}},
|
||||
{2260, {"Straight Wing 10"_s, AccessorySize::XL}},
|
||||
|
||||
{2301, {"Triangular Wing 01"_s, AccessorySize::XL}},
|
||||
{2302, {"Triangular Wing 02"_s, AccessorySize::XL}},
|
||||
{2303, {"Triangular Wing 03"_s, AccessorySize::XL}},
|
||||
{2304, {"Triangular Wing 04"_s, AccessorySize::XL}},
|
||||
{2305, {"Triangular Wing 05"_s, AccessorySize::XL}},
|
||||
{2306, {"Triangular Wing 06"_s, AccessorySize::XL}},
|
||||
{2307, {"Triangular Wing 07"_s, AccessorySize::XL}},
|
||||
{2308, {"Triangular Wing 08"_s, AccessorySize::XL}},
|
||||
{2309, {"Triangular Wing 09"_s, AccessorySize::XL}},
|
||||
{2310, {"Triangular Wing 10"_s, AccessorySize::XL}},
|
||||
{2311, {"Triangular Wing 11"_s, AccessorySize::L}},
|
||||
{2312, {"Triangular Wing 12"_s, AccessorySize::L}},
|
||||
{2313, {"Triangular Wing 13"_s, AccessorySize::L}},
|
||||
{2314, {"Triangular Wing 14"_s, AccessorySize::L}},
|
||||
{2315, {"Triangular Wing 15"_s, AccessorySize::L}},
|
||||
|
||||
{2351, {"Complex Wing 01"_s, AccessorySize::XL}},
|
||||
{2352, {"Complex Wing 02"_s, AccessorySize::XL}},
|
||||
{2353, {"Complex Wing 03"_s, AccessorySize::XL}},
|
||||
{2354, {"Complex Wing 04"_s, AccessorySize::XL}},
|
||||
{2355, {"Complex Wing 05"_s, AccessorySize::XL}},
|
||||
{2356, {"Complex Wing 06"_s, AccessorySize::L}},
|
||||
{2357, {"Complex Wing 07"_s, AccessorySize::L}},
|
||||
{2358, {"Complex Wing 08"_s, AccessorySize::L}},
|
||||
{2359, {"Complex Wing 09"_s, AccessorySize::L}},
|
||||
{2360, {"Complex Wing 10"_s, AccessorySize::L}},
|
||||
|
||||
{2401, {"Blade 01"_s, AccessorySize::XL}},
|
||||
{2402, {"Blade 02"_s, AccessorySize::XL}},
|
||||
{2403, {"Blade 03"_s, AccessorySize::XL}},
|
||||
{2404, {"Blade 04"_s, AccessorySize::XL}},
|
||||
{2405, {"Blade 05"_s, AccessorySize::XL}},
|
||||
{2406, {"Blade 06"_s, AccessorySize::XL}},
|
||||
{2407, {"Blade 07"_s, AccessorySize::XL}},
|
||||
{2408, {"Blade 08"_s, AccessorySize::XL}},
|
||||
{2409, {"Blade 09"_s, AccessorySize::XL}},
|
||||
{2410, {"Blade 10"_s, AccessorySize::XL}},
|
||||
{2411, {"Blade 11"_s, AccessorySize::XL}},
|
||||
{2412, {"Blade 12"_s, AccessorySize::XL}},
|
||||
{2413, {"Blade 13"_s, AccessorySize::XL}},
|
||||
{2414, {"Blade 14"_s, AccessorySize::XL}},
|
||||
{2415, {"Blade 15"_s, AccessorySize::XL}},
|
||||
|
||||
{2426, {"Curved Blade 01"_s, AccessorySize::XL}},
|
||||
{2427, {"Curved Blade 02"_s, AccessorySize::XL}},
|
||||
{2428, {"Curved Blade 03"_s, AccessorySize::XL}},
|
||||
{2429, {"Curved Blade 04"_s, AccessorySize::XL}},
|
||||
{2430, {"Curved Blade 05"_s, AccessorySize::XL}},
|
||||
{2431, {"Axe Head 01"_s, AccessorySize::XL}},
|
||||
{2432, {"Axe Head 02"_s, AccessorySize::XL}},
|
||||
{2433, {"Axe Head 03"_s, AccessorySize::XL}},
|
||||
{2434, {"Axe Head 04"_s, AccessorySize::XL}},
|
||||
{2435, {"Axe Head 05"_s, AccessorySize::XL}},
|
||||
|
||||
{2451, {"Horn 01"_s, AccessorySize::M}},
|
||||
{2452, {"Horn 02"_s, AccessorySize::M}},
|
||||
{2453, {"Horn 03"_s, AccessorySize::M}},
|
||||
{2454, {"Horn 04"_s, AccessorySize::M}},
|
||||
{2455, {"Horn 05"_s, AccessorySize::M}},
|
||||
{2456, {"Horn 06"_s, AccessorySize::M}},
|
||||
{2457, {"Horn 07"_s, AccessorySize::M}},
|
||||
{2458, {"Horn 08"_s, AccessorySize::M}},
|
||||
{2459, {"Horn 09"_s, AccessorySize::M}},
|
||||
{2460, {"Horn 10"_s, AccessorySize::M}},
|
||||
{2461, {"Horn 11"_s, AccessorySize::M}},
|
||||
{2462, {"Horn 12"_s, AccessorySize::M}},
|
||||
{2463, {"Horn 13"_s, AccessorySize::M}},
|
||||
{2464, {"Horn 14"_s, AccessorySize::M}},
|
||||
{2465, {"Horn 15"_s, AccessorySize::M}},
|
||||
|
||||
{2471, {"Mask"_s, AccessorySize::M}},
|
||||
{2472, {"Droplet"_s, AccessorySize::M}},
|
||||
{2473, {"Thigh"_s, AccessorySize::M}},
|
||||
{2474, {"LegS"_s, AccessorySize::M}},
|
||||
{2475, {"LegTH"_s, AccessorySize::M}},
|
||||
{2476, {"Plume 01"_s, AccessorySize::M}},
|
||||
{2477, {"Plume 02"_s, AccessorySize::M}},
|
||||
{2478, {"Plume 03"_s, AccessorySize::M}},
|
||||
{2479, {"Plume 04"_s, AccessorySize::M}},
|
||||
{2480, {"Plume 05"_s, AccessorySize::M}},
|
||||
|
||||
{2491, {"Tail 01"_s, AccessorySize::XL}},
|
||||
{2492, {"Tail 02"_s, AccessorySize::XL}},
|
||||
{2493, {"Tail 03"_s, AccessorySize::XL}},
|
||||
{2494, {"Tail 04"_s, AccessorySize::XL}},
|
||||
{2495, {"Tail 05"_s, AccessorySize::XL}},
|
||||
|
||||
{2501, {"Finger 01"_s, AccessorySize::M}},
|
||||
{2502, {"Finger 02"_s, AccessorySize::M}},
|
||||
{2503, {"Finger 03"_s, AccessorySize::M}},
|
||||
{2504, {"Finger 04"_s, AccessorySize::M}},
|
||||
{2505, {"Finger 05"_s, AccessorySize::M}},
|
||||
|
||||
{2510, {"Clamp 01"_s, AccessorySize::M}},
|
||||
{2511, {"Clamp 02"_s, AccessorySize::M}},
|
||||
{2512, {"Clamp 03"_s, AccessorySize::M}},
|
||||
{2513, {"Clamp 04"_s, AccessorySize::M}},
|
||||
{2514, {"Clamp 05"_s, AccessorySize::M}},
|
||||
|
||||
{2521, {"Fabric 01"_s, AccessorySize::XL}},
|
||||
{2522, {"Fabric 02"_s, AccessorySize::XL}},
|
||||
{2523, {"Fabric 03"_s, AccessorySize::XL}},
|
||||
{2524, {"Fabric 04"_s, AccessorySize::XL}},
|
||||
{2525, {"Fabric 05"_s, AccessorySize::XL}},
|
||||
|
||||
{2551, {"Energy Barrel 01"_s, AccessorySize::XL}},
|
||||
{2552, {"Energy Barrel 02"_s, AccessorySize::XL}},
|
||||
{2553, {"Energy Barrel 03"_s, AccessorySize::XL}},
|
||||
{2554, {"Energy Barrel 04"_s, AccessorySize::XL}},
|
||||
{2555, {"Energy Barrel 05"_s, AccessorySize::XL}},
|
||||
|
||||
{2601, {"L Bullet Barrel 01"_s, AccessorySize::XL}},
|
||||
{2602, {"L Bullet Barrel 02"_s, AccessorySize::XL}},
|
||||
{2603, {"L Bullet Barrel 03"_s, AccessorySize::XL}},
|
||||
{2604, {"L Bullet Barrel 04"_s, AccessorySize::XL}},
|
||||
{2605, {"L Bullet Barrel 05"_s, AccessorySize::XL}},
|
||||
{2606, {"S Bullet Barrel 01"_s, AccessorySize::XL}},
|
||||
{2607, {"S Bullet Barrel 02"_s, AccessorySize::XL}},
|
||||
{2608, {"S Bullet Barrel 03"_s, AccessorySize::XL}},
|
||||
{2609, {"S Bullet Barrel 04"_s, AccessorySize::XL}},
|
||||
{2610, {"S Bullet Barrel 05"_s, AccessorySize::XL}},
|
||||
{2611, {"B Bullet Barrel 01"_s, AccessorySize::XL}},
|
||||
{2612, {"B Bullet Barrel 02"_s, AccessorySize::XL}},
|
||||
{2613, {"B Bullet Barrel 03"_s, AccessorySize::XL}},
|
||||
{2614, {"B Bullet Barrel 04"_s, AccessorySize::XL}},
|
||||
{2615, {"B Bullet Barrel 05"_s, AccessorySize::XL}},
|
||||
{2616, {"B Bullet Barrel 06"_s, AccessorySize::XL}},
|
||||
{2617, {"B Bullet Barrel 07"_s, AccessorySize::XL}},
|
||||
{2618, {"B Bullet Barrel 08"_s, AccessorySize::XL}},
|
||||
{2619, {"B Bullet Barrel 09"_s, AccessorySize::XL}},
|
||||
{2620, {"B Bullet Barrel 10"_s, AccessorySize::XL}},
|
||||
|
||||
{2651, {"Cylinder Scope 01"_s, AccessorySize::M}},
|
||||
{2652, {"Cylinder Scope 02"_s, AccessorySize::M}},
|
||||
{2653, {"Cylinder Scope 03"_s, AccessorySize::M}},
|
||||
{2654, {"Cylinder Scope 04"_s, AccessorySize::M}},
|
||||
{2655, {"Cylinder Scope 05"_s, AccessorySize::M}},
|
||||
{2656, {"Elec Scope 01"_s, AccessorySize::M}},
|
||||
{2657, {"Elec Scope 02"_s, AccessorySize::M}},
|
||||
{2658, {"Elec Scope 03"_s, AccessorySize::M}},
|
||||
{2659, {"Elec Scope 04"_s, AccessorySize::M}},
|
||||
{2660, {"Elec Scope 05"_s, AccessorySize::M}},
|
||||
{2661, {"Mark Scope 01"_s, AccessorySize::S}},
|
||||
{2662, {"Mark Scope 02"_s, AccessorySize::S}},
|
||||
{2663, {"Mark Scope 03"_s, AccessorySize::S}},
|
||||
{2664, {"Mark Scope 04"_s, AccessorySize::S}},
|
||||
{2665, {"Mark Scope 05"_s, AccessorySize::S}},
|
||||
|
||||
{2701, {"S Single Weaponry"_s, AccessorySize::M}},
|
||||
{2702, {"S Packed Weaponry 01"_s, AccessorySize::M}},
|
||||
{2703, {"S Packed Weaponry 02"_s, AccessorySize::M}},
|
||||
{2704, {"S Packed Weaponry 03"_s, AccessorySize::M}},
|
||||
{2705, {"S Packed Weaponry 04"_s, AccessorySize::M}},
|
||||
{2706, {"L Single Weaponry"_s, AccessorySize::XL}},
|
||||
{2707, {"L Packed Weaponry 01"_s, AccessorySize::XL}},
|
||||
{2708, {"L Packed Weaponry 02"_s, AccessorySize::XL}},
|
||||
{2709, {"L Packed Weaponry 03"_s, AccessorySize::XL}},
|
||||
{2710, {"L Packed Weaponry 04"_s, AccessorySize::XL}},
|
||||
{2711, {"Atk Single Weaponry"_s, AccessorySize::XL}},
|
||||
{2712, {"Atk Packed Weaponry 01"_s, AccessorySize::XL}},
|
||||
{2713, {"Atk Packed Weaponry 02"_s, AccessorySize::XL}},
|
||||
{2714, {"Atk Packed Weaponry 03"_s, AccessorySize::XL}},
|
||||
{2715, {"Atk Packed Weaponry 04"_s, AccessorySize::XL}},
|
||||
|
||||
{2751, {"Vent 01"_s, AccessorySize::M}},
|
||||
{2752, {"Vent 02"_s, AccessorySize::M}},
|
||||
{2753, {"Vent 03"_s, AccessorySize::M}},
|
||||
{2754, {"Vent 04"_s, AccessorySize::M}},
|
||||
{2755, {"Vent 05"_s, AccessorySize::M}},
|
||||
{2756, {"Vent 06"_s, AccessorySize::M}},
|
||||
{2757, {"Vent 07"_s, AccessorySize::M}},
|
||||
{2758, {"Vent 08"_s, AccessorySize::M}},
|
||||
{2759, {"Vent 09"_s, AccessorySize::M}},
|
||||
{2760, {"Vent 10"_s, AccessorySize::M}},
|
||||
|
||||
{2901, {"Complex Construct 01"_s, AccessorySize::L}},
|
||||
{2902, {"Complex Construct 02"_s, AccessorySize::L}},
|
||||
{2903, {"Complex Construct 03"_s, AccessorySize::L}},
|
||||
{2904, {"Complex Construct 04"_s, AccessorySize::L}},
|
||||
{2905, {"Complex Construct 05"_s, AccessorySize::L}},
|
||||
|
||||
{2950, {"Q Mask 01"_s, AccessorySize::M}},
|
||||
{2951, {"Q Mask 02"_s, AccessorySize::M}},
|
||||
{2952, {"Q Mask 03"_s, AccessorySize::M}},
|
||||
{2953, {"Q Mask 04"_s, AccessorySize::M}},
|
||||
{2954, {"Q Mask 05"_s, AccessorySize::M}},
|
||||
// endregion
|
||||
|
||||
// region Connectors
|
||||
{3001, {"Circular Vent 01"_s, AccessorySize::M}},
|
||||
{3002, {"Circular Vent 02"_s, AccessorySize::M}},
|
||||
{3003, {"Circular Vent 03"_s, AccessorySize::M}},
|
||||
{3004, {"Circular Vent 04"_s, AccessorySize::M}},
|
||||
{3005, {"Circular Vent 05"_s, AccessorySize::M}},
|
||||
{3006, {"Circular Vent 06"_s, AccessorySize::M}},
|
||||
{3007, {"Circular Vent 07"_s, AccessorySize::M}},
|
||||
{3008, {"Circular Vent 08"_s, AccessorySize::M}},
|
||||
{3009, {"Circular Vent 09"_s, AccessorySize::M}},
|
||||
{3010, {"Circular Vent 10"_s, AccessorySize::M}},
|
||||
{3011, {"Circular Vent 11"_s, AccessorySize::M}},
|
||||
{3012, {"Circular Vent 12"_s, AccessorySize::M}},
|
||||
{3013, {"Circular Vent 13"_s, AccessorySize::M}},
|
||||
{3014, {"Circular Vent 14"_s, AccessorySize::M}},
|
||||
{3015, {"Circular Vent 15"_s, AccessorySize::M}},
|
||||
|
||||
{3051, {"Reactor 01"_s, AccessorySize::L}},
|
||||
{3052, {"Reactor 02"_s, AccessorySize::L}},
|
||||
{3053, {"Reactor 03"_s, AccessorySize::L}},
|
||||
{3054, {"Reactor 04"_s, AccessorySize::L}},
|
||||
{3055, {"Reactor 05"_s, AccessorySize::L}},
|
||||
|
||||
{3101, {"Connecting Tube 01"_s, AccessorySize::XL}},
|
||||
{3102, {"Connecting Tube 02"_s, AccessorySize::XL}},
|
||||
{3103, {"Connecting Tube 03"_s, AccessorySize::XL}},
|
||||
{3104, {"Connecting Tube 04"_s, AccessorySize::XL}},
|
||||
{3105, {"Connecting Tube 05"_s, AccessorySize::XL}},
|
||||
|
||||
{3151, {"Latch 01"_s, AccessorySize::M}},
|
||||
{3152, {"Latch 02"_s, AccessorySize::M}},
|
||||
{3153, {"Latch 03"_s, AccessorySize::M}},
|
||||
{3154, {"Latch 04"_s, AccessorySize::M}},
|
||||
{3155, {"Latch 05"_s, AccessorySize::M}},
|
||||
{3156, {"Latch 06"_s, AccessorySize::M}},
|
||||
{3157, {"Latch 07"_s, AccessorySize::M}},
|
||||
{3158, {"Latch 08"_s, AccessorySize::M}},
|
||||
{3159, {"Latch 09"_s, AccessorySize::M}},
|
||||
{3160, {"Latch 10"_s, AccessorySize::M}},
|
||||
{3161, {"Latch 11"_s, AccessorySize::M}},
|
||||
{3162, {"Latch 12"_s, AccessorySize::M}},
|
||||
{3163, {"Latch 13"_s, AccessorySize::M}},
|
||||
{3164, {"Latch 14"_s, AccessorySize::M}},
|
||||
{3165, {"Latch 15"_s, AccessorySize::M}},
|
||||
|
||||
{3201, {"Short Connector 01"_s, AccessorySize::M}},
|
||||
{3202, {"Short Connector 02"_s, AccessorySize::M}},
|
||||
{3203, {"Short Connector 03"_s, AccessorySize::M}},
|
||||
{3204, {"Short Connector 04"_s, AccessorySize::M}},
|
||||
{3205, {"Short Connector 05"_s, AccessorySize::M}},
|
||||
{3206, {"Antenna 01"_s, AccessorySize::S}},
|
||||
{3207, {"Antenna 02"_s, AccessorySize::S}},
|
||||
{3208, {"Antenna 03"_s, AccessorySize::S}},
|
||||
{3209, {"Antenna 04"_s, AccessorySize::S}},
|
||||
{3210, {"Antenna 05"_s, AccessorySize::S}},
|
||||
|
||||
{3226, {"Long Connector 01"_s, AccessorySize::XL}},
|
||||
{3227, {"Long Connector 02"_s, AccessorySize::XL}},
|
||||
{3228, {"Long Connector 03"_s, AccessorySize::XL}},
|
||||
{3229, {"Long Connector 04"_s, AccessorySize::XL}},
|
||||
{3230, {"Long Connector 05"_s, AccessorySize::XL}},
|
||||
{3231, {"Long Connector 06"_s, AccessorySize::XL}},
|
||||
{3232, {"Long Connector 07"_s, AccessorySize::XL}},
|
||||
{3233, {"Long Connector 08"_s, AccessorySize::XL}},
|
||||
{3234, {"Long Connector 09"_s, AccessorySize::XL}},
|
||||
{3235, {"Long Connector 10"_s, AccessorySize::XL}},
|
||||
|
||||
{3251, {"Complex Connector 01"_s, AccessorySize::XL}},
|
||||
{3252, {"Complex Connector 02"_s, AccessorySize::XL}},
|
||||
{3253, {"Complex Connector 03"_s, AccessorySize::XL}},
|
||||
{3254, {"Complex Connector 04"_s, AccessorySize::XL}},
|
||||
{3255, {"Complex Connector 05"_s, AccessorySize::XL}},
|
||||
|
||||
{3301, {"Tube Line 01"_s, AccessorySize::L}},
|
||||
{3302, {"Tube Line 02"_s, AccessorySize::L}},
|
||||
{3303, {"Tube Line 03"_s, AccessorySize::L}},
|
||||
{3304, {"Tube Line 04"_s, AccessorySize::XL}},
|
||||
{3305, {"Tube Line 05"_s, AccessorySize::XL}},
|
||||
{3306, {"Tube Line 06"_s, AccessorySize::M}},
|
||||
{3307, {"Tube Line 07"_s, AccessorySize::M}},
|
||||
{3308, {"Tube Line 08"_s, AccessorySize::M}},
|
||||
{3309, {"Tube Line 09"_s, AccessorySize::L}},
|
||||
{3310, {"Tube Line 10"_s, AccessorySize::L}},
|
||||
|
||||
{3351, {"Radar Plate 01"_s, AccessorySize::M}},
|
||||
{3352, {"Radar Plate 02"_s, AccessorySize::M}},
|
||||
{3353, {"Radar Plate 03"_s, AccessorySize::M}},
|
||||
{3354, {"Radar Plate 04"_s, AccessorySize::M}},
|
||||
{3355, {"Radar Plate 05"_s, AccessorySize::M}},
|
||||
{3356, {"Radar Pod 01"_s, AccessorySize::M}},
|
||||
{3357, {"Radar Pod 02"_s, AccessorySize::M}},
|
||||
{3358, {"Radar Pod 03"_s, AccessorySize::M}},
|
||||
{3359, {"Radar Pod 04"_s, AccessorySize::M}},
|
||||
{3360, {"Radar Pod 05"_s, AccessorySize::M}},
|
||||
|
||||
{3401, {"Tri Pod 01"_s, AccessorySize::M}},
|
||||
{3402, {"Tri Pod 02"_s, AccessorySize::M}},
|
||||
{3403, {"Tri Pod 03"_s, AccessorySize::M}},
|
||||
{3404, {"Tri Pod 04"_s, AccessorySize::M}},
|
||||
{3405, {"Tri Pod 05"_s, AccessorySize::M}},
|
||||
{3406, {"Signal Pod 01"_s, AccessorySize::M}},
|
||||
{3407, {"Signal Pod 02"_s, AccessorySize::M}},
|
||||
{3408, {"Signal Pod 03"_s, AccessorySize::M}},
|
||||
{3409, {"Signal Pod 04"_s, AccessorySize::M}},
|
||||
{3410, {"Signal Pod 05"_s, AccessorySize::M}},
|
||||
// endregion
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
using namespace Magnum;
|
||||
|
||||
struct ArmourSet {
|
||||
Containers::StringView name;
|
||||
bool neck_compatible;
|
||||
};
|
||||
|
||||
static const std::map<Int, ArmourSet> armour_sets {
|
||||
{-1, {"<unequipped>"_s, true}},
|
||||
{0, {"Vanguard"_s, true}},
|
||||
{1, {"Assault Mk.I"_s, true}},
|
||||
{2, {"Assault Mk.II"_s, false}},
|
||||
{3, {"Assault Mk.III"_s, false}},
|
||||
{7, {"Titan 001"_s, true}},
|
||||
{8, {"Titan 002"_s, false}},
|
||||
{9, {"Titan 003"_s, false}},
|
||||
{13, {"Blitz X"_s, true}},
|
||||
{14, {"Blitz EX"_s, false}},
|
||||
{15, {"Blitz EXS"_s, false}},
|
||||
{16, {"Kaiser S-R0"_s, true}},
|
||||
{17, {"Kaiser S-R1"_s, false}},
|
||||
{18, {"Kaiser S-R2"_s, false}},
|
||||
{19, {"Hammerfall MG-A"_s, true}},
|
||||
{20, {"Hammerfall MG-S"_s, false}},
|
||||
{21, {"Hammerfall MG-X"_s, false}},
|
||||
{22, {"Panzer S-UC"_s, true}},
|
||||
{23, {"Panzer L-UC"_s, false}},
|
||||
{24, {"Panzer H-UC"_s, false}},
|
||||
{25, {"Axial Core R-Type"_s, true}},
|
||||
{26, {"Axial Core S-Type"_s, false}},
|
||||
{27, {"Axial Core X-Type"_s, false}},
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef c
|
||||
c(Face, "enuArmorSlots::NewEnumerator0"_s, "Face"_s)
|
||||
c(UpperHead, "enuArmorSlots::NewEnumerator1"_s, "Upper head"_s)
|
||||
c(LowerHead, "enuArmorSlots::NewEnumerator2"_s, "Lower head"_s)
|
||||
c(Neck, "enuArmorSlots::NewEnumerator3"_s, "Neck"_s)
|
||||
c(UpperBody, "enuArmorSlots::NewEnumerator4"_s, "Upper body"_s)
|
||||
c(MiddleBody, "enuArmorSlots::NewEnumerator5"_s, "Middle body"_s)
|
||||
c(LowerBody, "enuArmorSlots::NewEnumerator6"_s, "Lower body"_s)
|
||||
c(FrontWaist, "enuArmorSlots::NewEnumerator7"_s, "Front waist"_s)
|
||||
c(LeftFrontSkirt, "enuArmorSlots::NewEnumerator8"_s, "Left front skirt"_s)
|
||||
c(RightFrontSkirt, "enuArmorSlots::NewEnumerator9"_s, "Right front skirt"_s)
|
||||
c(LeftSideSkirt, "enuArmorSlots::NewEnumerator10"_s, "Left side skirt"_s)
|
||||
c(RightSideSkirt, "enuArmorSlots::NewEnumerator11"_s, "Right side skirt"_s)
|
||||
c(LeftBackSkirt, "enuArmorSlots::NewEnumerator12"_s, "Left back skirt"_s)
|
||||
c(RightBackSkirt, "enuArmorSlots::NewEnumerator13"_s, "Right back skirt"_s)
|
||||
c(BackWaist, "enuArmorSlots::NewEnumerator14"_s, "Back waist"_s)
|
||||
c(LeftShoulder, "enuArmorSlots::NewEnumerator15"_s, "Left shoulder"_s)
|
||||
c(RightShoulder, "enuArmorSlots::NewEnumerator16"_s, "Right shoulder"_s)
|
||||
c(LeftUpperArm, "enuArmorSlots::NewEnumerator17"_s, "Left upper arm"_s)
|
||||
c(RightUpperArm, "enuArmorSlots::NewEnumerator18"_s, "Right upper arm"_s)
|
||||
c(LeftElbow, "enuArmorSlots::NewEnumerator19"_s, "Left elbow"_s)
|
||||
c(RightElbow, "enuArmorSlots::NewEnumerator20"_s, "Right elbow"_s)
|
||||
c(LeftLowerArm, "enuArmorSlots::NewEnumerator21"_s, "Left lower arm"_s)
|
||||
c(RightLowerArm, "enuArmorSlots::NewEnumerator22"_s, "Right lower arm"_s)
|
||||
c(Backpack, "enuArmorSlots::NewEnumerator23"_s, "Backpack"_s)
|
||||
c(LeftHand, "enuArmorSlots::NewEnumerator24"_s, "Left hand"_s)
|
||||
c(RightHand, "enuArmorSlots::NewEnumerator25"_s, "Right hand"_s)
|
||||
c(LeftUpperLeg, "enuArmorSlots::NewEnumerator26"_s, "Left upper leg"_s)
|
||||
c(RightUpperLeg, "enuArmorSlots::NewEnumerator27"_s, "Right upper leg"_s)
|
||||
c(LeftKnee, "enuArmorSlots::NewEnumerator28"_s, "Left knee"_s)
|
||||
c(RightKnee, "enuArmorSlots::NewEnumerator29"_s, "Right knee"_s)
|
||||
c(LeftLowerLeg, "enuArmorSlots::NewEnumerator30"_s, "Left lower leg"_s)
|
||||
c(RightLowerLeg, "enuArmorSlots::NewEnumerator31"_s, "Right lower leg"_s)
|
||||
c(LeftAnkle, "enuArmorSlots::NewEnumerator32"_s, "Left ankle"_s)
|
||||
c(RightAnkle, "enuArmorSlots::NewEnumerator33"_s, "Right ankle"_s)
|
||||
c(LeftHeel, "enuArmorSlots::NewEnumerator34"_s, "Left heel"_s)
|
||||
c(RightHeel, "enuArmorSlots::NewEnumerator35"_s, "Right heel"_s)
|
||||
c(LeftFoot, "enuArmorSlots::NewEnumerator36"_s, "Left foot"_s)
|
||||
c(RightFoot, "enuArmorSlots::NewEnumerator37"_s, "Right foot"_s)
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef c
|
||||
c(NotFound, "NotARealValue"_s)
|
||||
c(ActiveOne, "enuBLAttachmentStyle::NewEnumerator0"_s)
|
||||
c(ActiveOnePerSlot, "enuBLAttachmentStyle::NewEnumerator1"_s)
|
||||
c(AllEquipped, "enuBLAttachmentStyle::NewEnumerator2"_s)
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef c
|
||||
c(Auto, "None"_s, "Auto"_s)
|
||||
c(Shoulders, "Shoulder"_s, "Shoulders"_s)
|
||||
c(Body, "Body"_s, "Body"_s)
|
||||
c(Backpack, "Backpack"_s, "Backpack"_s)
|
||||
c(Hip, "Hip"_s, "Hips"_s)
|
||||
c(LowerLegs, "LowerLeg"_s, "Lower legs"_s)
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef c
|
||||
c(Physical, "enuDamageProperty::NewEnumerator0"_s)
|
||||
c(Piercing, "enuDamageProperty::NewEnumerator1"_s)
|
||||
c(Heat, "enuDamageProperty::NewEnumerator2"_s)
|
||||
c(Freeze, "enuDamageProperty::NewEnumerator3"_s)
|
||||
c(Shock, "enuDamageProperty::NewEnumerator4"_s)
|
||||
c(Plasma, "enuDamageProperty::NewEnumerator5"_s)
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef c
|
||||
c(Default, "enuWeaponEffectColorMode::NewEnumerator0"_s)
|
||||
c(Custom, "enuWeaponEffectColorMode::NewEnumerator1"_s)
|
||||
#endif
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -18,30 +18,44 @@
|
|||
|
||||
#include <map>
|
||||
|
||||
static const std::map<Int, const char*> mission_id_map {{
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
using namespace Magnum;
|
||||
|
||||
static const std::map<Int, Containers::StringView> mission_id_map {{
|
||||
// Story missions
|
||||
{0x0064, "Mission 1 - Training"},
|
||||
{0x0065, "Mission 2 - Patrol Operation"},
|
||||
{0x0066, "Mission 3 - Fusion Cells in the Snow"},
|
||||
{0x0067, "Mission 4 - Earning Changes"},
|
||||
{0x0068, "Mission 5 - Unexpected Coordination"},
|
||||
{0x0069, "Mission 6 - Empowering Void"},
|
||||
{0x006A, "Mission 7 - Logisitics Obstacles"},
|
||||
{0x006B, "Mission 8 - Wrath of the Wastelands"},
|
||||
{0x006C, "Mission 9 - Suspicious Originator"},
|
||||
{0x006D, "Mission 10 - Researchers Data Recovery"},
|
||||
{0x006E, "Mission 11 - Tempestuous Sector"},
|
||||
{0x006F, "Mission 12 - Clashes of Metal"},
|
||||
{0x0070, "Mission 13 - The Sandstorm Glutton"},
|
||||
{0x0064, "Mission 1 - Training"_s},
|
||||
{0x0065, "Mission 2 - Patrol Operation"_s},
|
||||
{0x0066, "Mission 3 - Fusion Cells in the Snow"_s},
|
||||
{0x0067, "Mission 4 - Earning Changes"_s},
|
||||
{0x0068, "Mission 5 - Unexpected Coordination"_s},
|
||||
{0x0069, "Mission 6 - Empowering Void"_s},
|
||||
{0x006A, "Mission 7 - Logisitics Obstacles"_s},
|
||||
{0x006B, "Mission 8 - Wrath of the Wastelands"_s},
|
||||
{0x006C, "Mission 9 - Suspicious Originator"_s},
|
||||
{0x006D, "Mission 10 - Researchers Data Recovery"_s},
|
||||
{0x006E, "Mission 11 - Tempestuous Sector"_s},
|
||||
{0x006F, "Mission 12 - Clashes of Metal"_s},
|
||||
{0x0070, "Mission 13 - The Sandstorm Glutton"_s},
|
||||
{0x0071, "Mission 14 - An Icy Investigation"_s},
|
||||
{0x0072, "Mission 15 - Outposts Line of Defense"_s},
|
||||
{0x0073, "Mission 16 - Hidden in the Pass"_s},
|
||||
{0x0074, "Mission 17 - Homebase Security"_s},
|
||||
|
||||
// Hunting grounds
|
||||
{0x00C8, "Hunt 1 - Desert Pathway Safety"},
|
||||
{0x00C9, "Hunt 2 - Snowfield Custodian"},
|
||||
{0x00CA, "Hunt 3 - Abandoned Valley Raid"},
|
||||
{0x00CB, "Hunt 4 - Depths of the Machineries"},
|
||||
{0x00C8, "Hunt 1 - Desert Pathway Safety"_s},
|
||||
{0x00C9, "Hunt 2 - Snowfield Custodian"_s},
|
||||
{0x00CA, "Hunt 3 - Abandoned Valley Raid"_s},
|
||||
{0x00CB, "Hunt 4 - Depths of the Machineries"_s},
|
||||
{0x00CC, "Hunt 5 - Crater Crashers"_s},
|
||||
{0x00CD, "Hunt 6 - Prototype Performance Tests"_s},
|
||||
|
||||
// Challenges
|
||||
{0x012C, "Challenge 1 - Redline Battlefront"},
|
||||
{0x0140, "Challenge 2 - Void Convergence"},
|
||||
{0x0190, "Challenge 3 - Gates of Ascension"}
|
||||
{0x012C, "Challenge 1 - Redline Battlefront"_s},
|
||||
{0x0140, "Challenge 2 - Void Convergence"_s},
|
||||
{0x0190, "Challenge 3 - Gates of Ascension"_s}
|
||||
}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -17,76 +17,98 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
using namespace Magnum;
|
||||
|
||||
struct StoryProgressPoint {
|
||||
Int id;
|
||||
const char* chapter;
|
||||
const char* point;
|
||||
const char* after = "";
|
||||
Containers::StringView chapter;
|
||||
Containers::StringView point;
|
||||
Containers::StringView after = nullptr;
|
||||
};
|
||||
|
||||
static const Corrade::Containers::Array<StoryProgressPoint> story_progress
|
||||
{
|
||||
InPlaceInit,
|
||||
{
|
||||
{0x0000, "Chapter 1", "Chapter start (company isn't named yet)"},
|
||||
{0x0064, "Chapter 1", "First time in the hangar"},
|
||||
{0x0065, "Chapter 1", "After 1st meeting with Quin in mission section"},
|
||||
{0x0066, "Chapter 1", "Talking with Reina and Quin in hangar", "After training"},
|
||||
{0x0067, "Chapter 1", "Returned to hangar", "After training"},
|
||||
{0x0068, "Chapter 1", "Talked with Quin in development section", "After training"},
|
||||
{0x0069, "Chapter 1", "Talked with Waltz in armour section", "After training"},
|
||||
{0x00C8, "Chapter 1", "Talked with Kael in tuning section", "After training"},
|
||||
{0x00C9, "Chapter 1", "Got mission 2 briefing", "After training"},
|
||||
{0x012C, "Chapter 1", "Talking with Reina", "After mission 2"},
|
||||
{0x012D, "Chapter 1", "Returned to hangar", "After mission 2"},
|
||||
{0x012E, "Chapter 1", "Talked with Kael in tuning section", "After mission 2"},
|
||||
{0x012F, "Chapter 1", "Talked with Reina in hangar", "After mission 2"},
|
||||
{0x0130, "Chapter 1", "Got mission 3 briefing", "After mission 2"},
|
||||
{0x0190, "Chapter 1", "Talking with Reina", "After mission 3"},
|
||||
{0x0191, "Chapter 1", "Returned to hangar", "After mission 3"},
|
||||
{0x0192, "Chapter 1", "Talked with Waltz in armour section", "After mission 3"},
|
||||
{0x0193, "Chapter 1", "Got mission 4 briefing", "After mission 3"},
|
||||
{0x01F4, "Chapter 1", "Talking with Reina", "After mission 4"},
|
||||
{0x01F5, "Chapter 1", "Returned to hangar", "After mission 4"},
|
||||
{0x01F6, "Chapter 1", "Talked with Waltz in armour section", "After mission 4"},
|
||||
{0x01F7, "Chapter 1", "Talked with Reina in hangar", "After mission 4"},
|
||||
{0x01F8, "Chapter 1", "Got mission 5 and hunt 1 briefing", "After mission 4"},
|
||||
{0x0258, "Chapter 1", "Meeting Neon and Aine", "After mission 5"},
|
||||
{0x0259, "Chapter 1", "Returned to hangar", "After mission 5"},
|
||||
{0x025A, "Chapter 1", "Got mission 6 briefing", "After mission 5"},
|
||||
{0x02BC, "Chapter 1", "Talking with Reina", "After mission 6"},
|
||||
{0x02BD, "Chapter 1", "Returned to hangar", "After mission 6"},
|
||||
{0x02BE, "Chapter 1", "Got hunt 2 briefing", "After mission 6"},
|
||||
{0x02BF, "Chapter 1", "Met Ellenier", "After mission 6"},
|
||||
{0x02C0, "Chapter 1", "Got mission 7 briefing", "After mission 6"},
|
||||
{0x0320, "Chapter 1", "Talking with Nier", "After mission 7"},
|
||||
{0x0321, "Chapter 1", "Returned to hangar", "After mission 7"},
|
||||
{0x0322, "Chapter 1", "Talked with Quin, Reina, and Nier in development section", "After mission 7"},
|
||||
{0x0323, "Chapter 1", "Got mission 8 briefing", "After mission 7"},
|
||||
{0x0384, "Chapter 1", "Talking with crew in hangar", "After mission 8"},
|
||||
{0x0385, "Chapter 1", "Returned to hangar", "After mission 8"},
|
||||
{0x0386, "Chapter 1", "Got hunt 3 briefing", "After mission 8"},
|
||||
{0x0387, "Chapter 1", "Talked with Reina, Nier, and Quin in development section", "After mission 8"},
|
||||
{0x0000, "Chapter 1"_s, "Chapter start (company isn't named yet)"_s},
|
||||
{0x0064, "Chapter 1"_s, "First time in the hangar"_s},
|
||||
{0x0065, "Chapter 1"_s, "After 1st meeting with Quin in mission section"_s},
|
||||
{0x0066, "Chapter 1"_s, "Talking with Reina and Quin in hangar"_s, "After training"_s},
|
||||
{0x0067, "Chapter 1"_s, "Returned to hangar"_s, "After training"_s},
|
||||
{0x0068, "Chapter 1"_s, "Talked with Quin in development section"_s, "After training"_s},
|
||||
{0x0069, "Chapter 1"_s, "Talked with Waltz in armour section"_s, "After training"_s},
|
||||
{0x00C8, "Chapter 1"_s, "Talked with Kael in tuning section"_s, "After training"_s},
|
||||
{0x00C9, "Chapter 1"_s, "Got mission 2 briefing"_s, "After training"_s},
|
||||
{0x012C, "Chapter 1"_s, "Talking with Reina"_s, "After mission 2"_s},
|
||||
{0x012D, "Chapter 1"_s, "Returned to hangar"_s, "After mission 2"_s},
|
||||
{0x012E, "Chapter 1"_s, "Talked with Kael in tuning section"_s, "After mission 2"_s},
|
||||
{0x012F, "Chapter 1"_s, "Talked with Reina in hangar"_s, "After mission 2"_s},
|
||||
{0x0130, "Chapter 1"_s, "Got mission 3 briefing"_s, "After mission 2"_s},
|
||||
{0x0190, "Chapter 1"_s, "Talking with Reina"_s, "After mission 3"_s},
|
||||
{0x0191, "Chapter 1"_s, "Returned to hangar"_s, "After mission 3"_s},
|
||||
{0x0192, "Chapter 1"_s, "Talked with Waltz in armour section"_s, "After mission 3"_s},
|
||||
{0x0193, "Chapter 1"_s, "Got mission 4 briefing"_s, "After mission 3"_s},
|
||||
{0x01F4, "Chapter 1"_s, "Talking with Reina"_s, "After mission 4"_s},
|
||||
{0x01F5, "Chapter 1"_s, "Returned to hangar"_s, "After mission 4"_s},
|
||||
{0x01F6, "Chapter 1"_s, "Talked with Waltz in armour section"_s, "After mission 4"_s},
|
||||
{0x01F7, "Chapter 1"_s, "Talked with Reina in hangar"_s, "After mission 4"_s},
|
||||
{0x01F8, "Chapter 1"_s, "Got mission 5 and hunt 1 briefing"_s, "After mission 4"_s},
|
||||
{0x0258, "Chapter 1"_s, "Meeting Neon and Aine"_s, "After mission 5"_s},
|
||||
{0x0259, "Chapter 1"_s, "Returned to hangar"_s, "After mission 5"_s},
|
||||
{0x025A, "Chapter 1"_s, "Got mission 6 briefing"_s, "After mission 5"_s},
|
||||
{0x02BC, "Chapter 1"_s, "Talking with Reina"_s, "After mission 6"_s},
|
||||
{0x02BD, "Chapter 1"_s, "Returned to hangar"_s, "After mission 6"_s},
|
||||
{0x02BE, "Chapter 1"_s, "Got hunt 2 briefing"_s, "After mission 6"_s},
|
||||
{0x02BF, "Chapter 1"_s, "Met Ellenier"_s, "After mission 6"_s},
|
||||
{0x02C0, "Chapter 1"_s, "Got mission 7 briefing"_s, "After mission 6"_s},
|
||||
{0x0320, "Chapter 1"_s, "Talking with Nier"_s, "After mission 7"_s},
|
||||
{0x0321, "Chapter 1"_s, "Returned to hangar"_s, "After mission 7"_s},
|
||||
{0x0322, "Chapter 1"_s, "Talked with Quin, Reina, and Nier in development section"_s, "After mission 7"_s},
|
||||
{0x0323, "Chapter 1"_s, "Got mission 8 briefing"_s, "After mission 7"_s},
|
||||
{0x0384, "Chapter 1"_s, "Talking with crew in hangar"_s, "After mission 8"_s},
|
||||
{0x0385, "Chapter 1"_s, "Returned to hangar"_s, "After mission 8"_s},
|
||||
{0x0386, "Chapter 1"_s, "Got hunt 3 briefing"_s, "After mission 8"_s},
|
||||
{0x0387, "Chapter 1"_s, "Talked with Reina, Nier, and Quin in development section"_s, "After mission 8"_s},
|
||||
|
||||
{0x0388, "Chapter 2", "Chapter start"},
|
||||
{0x0389, "Chapter 2", "Got mission 9 briefing"},
|
||||
{0x03E8, "Chapter 2", "Talking with Reina in hangar", "After mission 9"},
|
||||
{0x03E9, "Chapter 2", "Returned to hangar", "After mission 9"},
|
||||
{0x03EA, "Chapter 2", "Talked with crew in armour section", "After mission 9"},
|
||||
{0x03EB, "Chapter 2", "Got mission 10 briefing", "After mission 9"},
|
||||
{0x044C, "Chapter 2", "Talking with Reina in hangar", "After mission 10"},
|
||||
{0x044D, "Chapter 2", "Returned to hangar", "After mission 10"},
|
||||
{0x044E, "Chapter 2", "Got mission 11 briefing", "After mission 10"},
|
||||
{0x04B0, "Chapter 2", "Talking with Reina and Nier in hangar", "After mission 11"},
|
||||
{0x04B1, "Chapter 2", "Returned to hangar", "After mission 11"},
|
||||
{0x04B2, "Chapter 2", "Got mission 12 briefing", "After mission 11"},
|
||||
{0x0514, "Chapter 2", "Talking with Reina and Waltz in hangar", "After mission 12"},
|
||||
{0x0515, "Chapter 2", "Returned to hangar", "After mission 12"},
|
||||
{0x0516, "Chapter 2", "Got hunt 4 and mission 13 briefing", "After mission 12"},
|
||||
{0x0578, "Chapter 2", "Talking with Reina in hangar", "After mission 13"},
|
||||
{0x0579, "Chapter 2", "Returned to hangar", "After mission 13"},
|
||||
{0x057A, "Chapter 2", "Talked with Reina in development section", "After mission 13"},
|
||||
{0x057B, "Chapter 2", "Got briefing for challenges 1, 2, and 3", "After mission 13"},
|
||||
{0x0388, "Chapter 2"_s, "Chapter start"_s},
|
||||
{0x0389, "Chapter 2"_s, "Got mission 9 briefing"_s},
|
||||
{0x03E8, "Chapter 2"_s, "Talking with Reina in hangar"_s, "After mission 9"_s},
|
||||
{0x03E9, "Chapter 2"_s, "Returned to hangar"_s, "After mission 9"_s},
|
||||
{0x03EA, "Chapter 2"_s, "Talked with crew in armour section"_s, "After mission 9"_s},
|
||||
{0x03EB, "Chapter 2"_s, "Got mission 10 briefing"_s, "After mission 9"_s},
|
||||
{0x044C, "Chapter 2"_s, "Talking with Reina in hangar"_s, "After mission 10"_s},
|
||||
{0x044D, "Chapter 2"_s, "Returned to hangar"_s, "After mission 10"_s},
|
||||
{0x044E, "Chapter 2"_s, "Got mission 11 briefing"_s, "After mission 10"_s},
|
||||
{0x04B0, "Chapter 2"_s, "Talking with Reina and Nier in hangar"_s, "After mission 11"_s},
|
||||
{0x04B1, "Chapter 2"_s, "Returned to hangar"_s, "After mission 11"_s},
|
||||
{0x04B2, "Chapter 2"_s, "Got mission 12 briefing"_s, "After mission 11"_s},
|
||||
{0x0514, "Chapter 2"_s, "Talking with Reina and Waltz in hangar"_s, "After mission 12"_s},
|
||||
{0x0515, "Chapter 2"_s, "Returned to hangar"_s, "After mission 12"_s},
|
||||
{0x0516, "Chapter 2"_s, "Got hunt 4 and mission 13 briefing"_s, "After mission 12"_s},
|
||||
|
||||
{0x0578, "Chapter 3"_s, "Chapter start, talking with Reina"_s, "After mission 13"_s},
|
||||
{0x0579, "Chapter 3"_s, "Returned to hangar"_s, "After mission 13"_s},
|
||||
{0x057A, "Chapter 3"_s, "Talked with Reina in development section"_s, "After mission 13"_s},
|
||||
{0x057B, "Chapter 3"_s, "Got briefing for challenges 1, 2, and 3"_s, "After mission 13"_s},
|
||||
{0x057C, "Chapter 3"_s, "Talked with Reina about device"_s, "After mission 13"_s},
|
||||
{0x057D, "Chapter 3"_s, "Got mission 14 briefing"_s, "After mission 13"_s},
|
||||
{0x05DC, "Chapter 3"_s, "Talking with Reina and Nier"_s, "After mission 14"_s},
|
||||
{0x05DD, "Chapter 3"_s, "Returned to hangar"_s, "After mission 14"_s},
|
||||
{0x05DE, "Chapter 3"_s, "Got briefing for mission 15 and hunt 5"_s, "After mission 14"_s},
|
||||
{0x0640, "Chapter 3"_s, "Talking with Nier and Kazu, and Reina"_s, "After mission 15"_s},
|
||||
{0x0641, "Chapter 3"_s, "Returned to hangar"_s, "After mission 15"_s},
|
||||
{0x0642, "Chapter 3"_s, "Talked with Reina and Nier in dev section"_s, "After mission 15"_s},
|
||||
{0x0643, "Chapter 3"_s, "Got briefing for mission 16"_s, "After mission 15"_s},
|
||||
{0x06A4, "Chapter 3"_s, "Talking with Kunai"_s, "After mission 16"_s},
|
||||
{0x06A5, "Chapter 3"_s, "Returned to hangar"_s, "After mission 16"_s},
|
||||
{0x06A6, "Chapter 3"_s, "Got mission 17 briefing"_s, "After mission 16"_s},
|
||||
{0x0708, "Chapter 3"_s, "Debriefing"_s, "After mission 17"_s},
|
||||
{0x070A, "Chapter 3"_s, "Got hunt 6 briefing"_s, "After mission 17"_s},
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
using namespace Magnum;
|
||||
|
||||
extern const std::map<Int, Containers::StringView> style_names
|
||||
#ifdef STYLENAMES_DEFINITION
|
||||
{
|
||||
{0, "Custom Style 1"_s},
|
||||
{1, "Custom Style 2"_s},
|
||||
{2, "Custom Style 3"_s},
|
||||
{3, "Custom Style 4"_s},
|
||||
{4, "Custom Style 5"_s},
|
||||
{5, "Custom Style 6"_s},
|
||||
{6, "Custom Style 7"_s},
|
||||
{7, "Custom Style 8"_s},
|
||||
{8, "Custom Style 9"_s},
|
||||
{9, "Custom Style 10"_s},
|
||||
{10, "Custom Style 11"_s},
|
||||
{11, "Custom Style 12"_s},
|
||||
{12, "Custom Style 13"_s},
|
||||
{13, "Custom Style 14"_s},
|
||||
{14, "Custom Style 15"_s},
|
||||
{15, "Custom Style 16"_s},
|
||||
|
||||
{50, "Global Style 1"_s},
|
||||
{51, "Global Style 2"_s},
|
||||
{52, "Global Style 3"_s},
|
||||
{53, "Global Style 4"_s},
|
||||
{54, "Global Style 5"_s},
|
||||
{55, "Global Style 6"_s},
|
||||
{56, "Global Style 7"_s},
|
||||
{57, "Global Style 8"_s},
|
||||
{58, "Global Style 9"_s},
|
||||
{59, "Global Style 10"_s},
|
||||
{60, "Global Style 11"_s},
|
||||
{61, "Global Style 12"_s},
|
||||
{62, "Global Style 13"_s},
|
||||
{63, "Global Style 14"_s},
|
||||
{64, "Global Style 15"_s},
|
||||
{65, "Global Style 16"_s},
|
||||
|
||||
{100, "Iron"_s},
|
||||
{101, "Silver"_s},
|
||||
{102, "Gold"_s},
|
||||
{103, "Bronze"_s},
|
||||
{104, "Copper"_s},
|
||||
{105, "Nickel"_s},
|
||||
{106, "Cobalt"_s},
|
||||
{107, "Aluminium"_s},
|
||||
{108, "Titanium"_s},
|
||||
{109, "Platinum"_s},
|
||||
{110, "Gun Metal"_s},
|
||||
{111, "White"_s},
|
||||
{112, "White Metal"_s},
|
||||
{113, "White Gloss"_s},
|
||||
{114, "Grey"_s},
|
||||
{115, "Grey Metal"_s},
|
||||
{116, "Grey Gloss"_s},
|
||||
{117, "Dark Grey"_s},
|
||||
{118, "Dark Grey Metal"_s},
|
||||
{119, "Dark Grey Gloss"_s},
|
||||
{120, "Black"_s},
|
||||
{121, "Black Metal"_s},
|
||||
{122, "Black Gloss"_s},
|
||||
{123, "Red"_s},
|
||||
{124, "Red Metal"_s},
|
||||
{125, "Red Gloss"_s},
|
||||
{126, "Dark Red"_s},
|
||||
{127, "Dark Red Metal"_s},
|
||||
{128, "Dark Red Gloss"_s},
|
||||
{129, "Orange"_s},
|
||||
{130, "Orange Metal"_s},
|
||||
{131, "Orange Gloss"_s},
|
||||
{132, "Dark Orange"_s},
|
||||
{133, "Dark Orange Metal"_s},
|
||||
{134, "Dark Orange Gloss"_s},
|
||||
{135, "Yellow"_s},
|
||||
{136, "Yellow Metal"_s},
|
||||
{137, "Yellow Gloss"_s},
|
||||
{138, "Brown"_s},
|
||||
{139, "Brown Metal"_s},
|
||||
{140, "Brown Gloss"_s},
|
||||
{141, "Dark Brown"_s},
|
||||
{142, "Dark Brown Metal"_s},
|
||||
{143, "Dark Brown Gloss"_s},
|
||||
{144, "Leafgreen"_s},
|
||||
{145, "Leafgreen Metal"_s},
|
||||
{146, "Leafgreen Gloss"_s},
|
||||
{147, "Military Green"_s},
|
||||
{148, "Military Green Metal"_s},
|
||||
{149, "Military Green Gloss"_s},
|
||||
{150, "Green"_s},
|
||||
{151, "Green Metal"_s},
|
||||
{152, "Green Gloss"_s},
|
||||
{153, "Dark Green"_s},
|
||||
{154, "Dark Green Metal"_s},
|
||||
{155, "Dark Green Gloss"_s},
|
||||
{156, "Teal"_s},
|
||||
{157, "Teal Metal"_s},
|
||||
{158, "Teal Gloss"_s},
|
||||
{159, "Cyan"_s},
|
||||
{160, "Cyan Metal"_s},
|
||||
{161, "Cyan Gloss"_s},
|
||||
{162, "Blue"_s},
|
||||
{163, "Blue Metal"_s},
|
||||
{164, "Blue Gloss"_s},
|
||||
{165, "Blue Sky"_s},
|
||||
{166, "Blue Sky Metal"_s},
|
||||
{167, "Blue Sky Gloss"_s},
|
||||
{168, "Dark Blue"_s},
|
||||
{169, "Dark Blue Metal"_s},
|
||||
{170, "Dark Blue Gloss"_s},
|
||||
{171, "Purple"_s},
|
||||
{172, "Purple Metal"_s},
|
||||
{173, "Purple Gloss"_s},
|
||||
{174, "Dark Purple"_s},
|
||||
{175, "Dark Purple Metal"_s},
|
||||
{176, "Dark Purple Gloss"_s},
|
||||
{177, "Pink"_s},
|
||||
{178, "Pink Metal"_s},
|
||||
{179, "Pink Gloss"_s},
|
||||
{180, "Rosy Brown"_s},
|
||||
{181, "Rosy Brown Metal"_s},
|
||||
{182, "Rosy Brown Gloss"_s},
|
||||
{183, "Ivory"_s},
|
||||
{184, "Ivory Metal"_s},
|
||||
{185, "Ivory Gloss"_s},
|
||||
{186, "Slate Brown"_s},
|
||||
{187, "Slate Brown Metal"_s},
|
||||
{188, "Slate Brown Gloss"_s},
|
||||
{189, "Slate Green"_s},
|
||||
{190, "Slate Green Metal"_s},
|
||||
{191, "Slate Green Gloss"_s},
|
||||
{192, "Slate Blue"_s},
|
||||
{193, "Slate Blue Metal"_s},
|
||||
{194, "Slate Blue Gloss"_s},
|
||||
{195, "Slate Purple"_s},
|
||||
{196, "Slate Purple Metal"_s},
|
||||
{197, "Slate Purple Gloss"_s},
|
||||
{198, "White Glow"_s},
|
||||
{199, "White Radiance"_s},
|
||||
{200, "Red Glow"_s},
|
||||
{201, "Red Radiance"_s},
|
||||
{202, "Orange Glow"_s},
|
||||
{203, "Orange Radiance"_s},
|
||||
{204, "Yellow Glow"_s},
|
||||
{205, "Yellow Radiance"_s},
|
||||
{206, "Leafgreen Glow"_s},
|
||||
{207, "Leafgreen Radiance"_s},
|
||||
{208, "Green Glow"_s},
|
||||
{209, "Green Radiance"_s},
|
||||
{210, "Teal Glow"_s},
|
||||
{211, "Teal Radiance"_s},
|
||||
{212, "Cyan Glow"_s},
|
||||
{213, "Cyan Radiance"_s},
|
||||
{214, "Blue Glow"_s},
|
||||
{215, "Blue Radiance"_s},
|
||||
{216, "Purple Glow"_s},
|
||||
{217, "Purple Radiance"_s},
|
||||
{218, "Pink Glow"_s},
|
||||
{219, "Pink Radiance"_s},
|
||||
{220, "Grey Camo"_s},
|
||||
{221, "Dark Grey Camo"_s},
|
||||
{222, "Green Camo"_s},
|
||||
{223, "Dark Green Camo"_s},
|
||||
{224, "Brown Camo"_s},
|
||||
{225, "Dark Brown Camo"_s},
|
||||
{226, "Blue Camo"_s},
|
||||
{227, "Dark Blue Camo"_s},
|
||||
}
|
||||
#endif
|
||||
;
|
|
@ -0,0 +1,433 @@
|
|||
#pragma once
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
// region Melee
|
||||
static const std::map<Int, Containers::StringView> melee_grips {
|
||||
{0, "Combat Grip (1H)"_s},
|
||||
{1, "Knuckle Guard Grip (1H)"_s},
|
||||
{2, "Dual Guard Grip (1H)"_s},
|
||||
{3, "Sepal Grip (1H)"_s},
|
||||
{4, "Warrior Grip (1H)"_s},
|
||||
{5, "Guardian Grip (1H)"_s},
|
||||
{6, "Knight Guard Grip (1H)"_s},
|
||||
{7, "Saber Guard Grip (1H)"_s},
|
||||
|
||||
{100, "Combat Side Grip (1H)"_s},
|
||||
{101, "Hollowed Side Grip (1H)"_s},
|
||||
{102, "Pulled Back Side Grip (1H)"_s},
|
||||
{103, "Plated Side Grip (1H)"_s},
|
||||
{104, "Locked Side Grip (1H)"_s},
|
||||
{105, "Longpoint Side Grip (1H)"_s},
|
||||
|
||||
{200, "Combat Dual Grip (1H)"_s},
|
||||
{201, "Hollowed Dual Grip (1H)"_s},
|
||||
{202, "Plated Dual Grip (1H)"_s},
|
||||
|
||||
{400, "Combat Twin Grip (1H)"_s},
|
||||
{401, "Sepal Twin Grip (1H)"_s},
|
||||
{402, "Hollowed Twin Grip (1H)"_s},
|
||||
{403, "Knuckle Guard Twin Grip (1H)"_s},
|
||||
{404, "Arched Twin Grip (1H)"_s},
|
||||
|
||||
{1000, "Combat Knuckle (R/L)"_s},
|
||||
{1001, "Battle Fist (R/L)"_s},
|
||||
{1002, "Guard Knuckle (R/L)"_s},
|
||||
|
||||
{2000, "Combat Polearm (2H)"_s},
|
||||
{2001, "Dual Guard Polearm (2H)"_s},
|
||||
{2002, "Sepal Polearm (2H)"_s},
|
||||
{2003, "Fin Polearm (2H)"_s},
|
||||
{2004, "Arched Polearm (2H)"_s},
|
||||
|
||||
{2100, "Combat Side Polearm (2H)"_s},
|
||||
{2101, "Plated Side Polearm (2H)"_s},
|
||||
{2102, "Locked Side Polearm (2H)"_s},
|
||||
{2103, "Fin Side Polearm (2H)"_s},
|
||||
|
||||
{2200, "Combat Dual Polearm (2H)"_s},
|
||||
|
||||
{2400, "Combat Twin Blade (2H)"_s},
|
||||
{2401, "Guard Twin Blade (2H)"_s},
|
||||
{2402, "Sepal Twin Blade (2H)"_s},
|
||||
{2403, "Fin Twin Blade (2H)"_s},
|
||||
{2404, "Arched Twin Blade (2H)"_s},
|
||||
};
|
||||
|
||||
static const std::map<Int, Containers::StringView> melee_assaulters {
|
||||
{0, "Long Metal Blade"_s},
|
||||
{1, "Long Assault Blade"_s},
|
||||
{2, "Long Fin Blade"_s},
|
||||
{3, "Long Double Blades"_s},
|
||||
{4, "Long Straight Blade"_s},
|
||||
{5, "Long Faceted Blade"_s},
|
||||
{6, "Long Interlocked Blade"_s},
|
||||
{7, "Long Frontbreak Blade"_s},
|
||||
{8, "Long Encased Blade"_s},
|
||||
{9, "Long Flat Gouger"_s},
|
||||
{10, "Long Curved Blade"_s},
|
||||
{11, "Long Broad Blade"_s},
|
||||
|
||||
{20, "Long Combat Edge"_s},
|
||||
{21, "Long Attached Edge"_s},
|
||||
|
||||
{40, "Katana Blade"_s},
|
||||
{41, "Custom Katana Blade"_s},
|
||||
|
||||
{60, "Energy Blade (Motion)"_s},
|
||||
{61, "Powered Blade"_s},
|
||||
|
||||
{100, "Short Metal Blade"_s},
|
||||
{101, "Short Assault Blade"_s},
|
||||
{102, "Short Fin Blade"_s},
|
||||
|
||||
{120, "Short Combat Edge"_s},
|
||||
|
||||
{160, "Short Energy Blade (Motion)"_s},
|
||||
{161, "Short Powered Blade"_s},
|
||||
|
||||
{180, "Triclaw"_s},
|
||||
{181, "Straight Triclaw"_s},
|
||||
{182, "Griphold Claw"_s},
|
||||
{183, "Energy Claw"_s},
|
||||
{184, "Openhold Claw"_s},
|
||||
{185, "Hooktusk Claw"_s},
|
||||
|
||||
{200, "Bracer"_s},
|
||||
{201, "Custom Bracer"_s},
|
||||
|
||||
{210, "Expanded Bracer"_s},
|
||||
{211, "Expanded Custom Bracer"_s},
|
||||
|
||||
{300, "Heavy Smasher"_s},
|
||||
{301, "Heavy Basher"_s},
|
||||
{302, "Heavy Torch Mace"_s},
|
||||
|
||||
{400, "Light Smasher"_s},
|
||||
{401, "Light Basher"_s},
|
||||
{402, "Light Torch Mace"_s},
|
||||
|
||||
{420, "War Hammer"_s},
|
||||
{421, "Great Hammer"_s},
|
||||
{422, "Spiked Hammer"_s},
|
||||
{423, "Broadhead Hammer"_s},
|
||||
|
||||
{440, "Morning Star"_s},
|
||||
{441, "Spike Ball"_s},
|
||||
|
||||
{500, "Combat Lance"_s},
|
||||
{501, "Gouger Lance"_s},
|
||||
|
||||
{510, "Piercer"_s},
|
||||
|
||||
{600, "Short Combat Lance"_s},
|
||||
|
||||
{605, "Short Combat Drill (Motion)"_s},
|
||||
|
||||
{610, "Short Piercer"_s},
|
||||
|
||||
{700, "Combat Axe"_s},
|
||||
{701, "Custom Combat Axe"_s},
|
||||
{702, "Piercing Axe"_s},
|
||||
{703, "Frontbreak Axe"_s},
|
||||
{704, "Maiming Axe"_s},
|
||||
{705, "Delta Axe"_s},
|
||||
|
||||
{800, "Combat Scythe"_s},
|
||||
{801, "Reaper Blade"_s},
|
||||
{802, "Clawtooth Scythe"_s},
|
||||
{803, "Wingpoint Scythe"_s},
|
||||
{804, "Snakebone Scythe"_s},
|
||||
|
||||
{900, "Short Combat Scythe"_s},
|
||||
{901, "Short Reaper Blade"_s},
|
||||
{902, "Short Clawtooth Scythe"_s},
|
||||
{903, "Short Wingpoint Scythe"_s},
|
||||
{904, "Short Snakebone Scythe"_s},
|
||||
};
|
||||
// endregion
|
||||
|
||||
// region Shields
|
||||
static const std::map<Int, Containers::StringView> shield_handles {
|
||||
{0, "Balanced Handle"_s},
|
||||
{1, "Expanded Handle"_s},
|
||||
{2, "Lowguard Handle"_s},
|
||||
{3, "Longblocker Handle"_s},
|
||||
{4, "Winged Handle"_s},
|
||||
{5, "Stopper Handle"_s},
|
||||
{6, "Layered Handle"_s},
|
||||
{7, "Riotguard Handle"_s},
|
||||
{8, "Blitz Handle"_s},
|
||||
{9, "Foldable Handle"_s},
|
||||
{10, "Board Handle"_s},
|
||||
{11, "Knight Handle"_s},
|
||||
{12, "Cargwall Handle"_s},
|
||||
|
||||
{100, "Buckler Handle"_s},
|
||||
{101, "Star Handle"_s},
|
||||
};
|
||||
|
||||
static const std::map<Int, Containers::StringView> shield_shells {
|
||||
{0, "Balanced Shell"_s},
|
||||
{1, "Compass Shell"_s},
|
||||
{2, "Uppoint Shell"_s},
|
||||
{3, "Pointed Shell"_s},
|
||||
{4, "Padded Shell"_s},
|
||||
{5, "Pincer Shell"_s},
|
||||
{6, "Fang Shell"_s},
|
||||
{7, "Holder Shell"_s},
|
||||
{8, "Composite Shell"_s},
|
||||
{9, "Mechanical Shell"_s},
|
||||
{10, "Layered Shell"_s},
|
||||
{11, "Parted Shell"_s},
|
||||
{12, "Tapst Shell"_s},
|
||||
{13, "Sidloc Shell"_s},
|
||||
|
||||
{100, "V-Cross Shell"_s},
|
||||
};
|
||||
// endregion
|
||||
|
||||
// region Bullet Shooters
|
||||
static const std::map<Int, Containers::StringView> bshooter_triggers {
|
||||
{0, "BL-Combat Trigger (1H)"_s},
|
||||
{1, "Light Machine Trigger (1H)"_s},
|
||||
{2, "Tactical Trigger (1H)"_s},
|
||||
{3, "Compact Trigger (1H)"_s},
|
||||
{4, "Longhold Trigger (1H)"_s},
|
||||
{5, "Downhold Trigger (1H)"_s},
|
||||
{6, "Cellblock Trigger (1H)"_s},
|
||||
{99, "Base Trigger (1H)"_s},
|
||||
|
||||
{100, "BL-Machine Trigger (2H)"_s},
|
||||
{101, "BL-Short Trigger (2H)"_s},
|
||||
{102, "Shielded Trigger (2H)"_s},
|
||||
{103, "Platedframe Trigger (2H)"_s},
|
||||
{104, "Sidebox Trigger (2H) (Motion)"_s},
|
||||
{199, "2H Base Trigger (2H)"_s},
|
||||
};
|
||||
|
||||
static const std::map<Int, Containers::StringView> bshooter_barrels {
|
||||
{0, "BL-Combat Barrel (1 shot)"_s},
|
||||
{1, "Shock Absorb Barrel (1 shot) (Motion)"_s},
|
||||
{2, "Muzzlemod Barrel (1 shot)"_s},
|
||||
{3, "Triangular Barrel (1 shot)"_s},
|
||||
{4, "Recoilblock Barrel (1 shot) (Motion)"_s},
|
||||
{97, "Short S Base Barrel (1 shot)"_s},
|
||||
{98, "Medium S Base Barrel (1 shot)"_s},
|
||||
{99, "Long S Base Barrel (1 shot)"_s},
|
||||
|
||||
{100, "Six-Barrel Gatling (Auto) (Motion)"_s},
|
||||
{101, "Modded Six-Barrel Gatling (Auto) (Motion)"_s},
|
||||
{102, "Four-Barrel Gatling (Auto) (Motion)"_s},
|
||||
{103, "Retro Style Gatling (Auto) (Motion)"_s},
|
||||
{197, "Short G Base Barrel (Auto)"_s},
|
||||
{198, "Medium G Base Barrel (Auto)"_s},
|
||||
{199, "Long G Base Barrel (Auto)"_s},
|
||||
|
||||
{200, "Blast Barrel (Spread)"_s},
|
||||
{201, "Wideblast Barrel (Spread) (Motion)"_s},
|
||||
{202, "Pelleter Barrel (Spread) (Motion)"_s},
|
||||
{203, "Lockhold Barrel (Spread) (Motion)"_s},
|
||||
{297, "Short B Base Barrel (Spread)"_s},
|
||||
{298, "Medium B Base Barrel (Spread)"_s},
|
||||
{299, "Long B Base Barrel (Spread)"_s},
|
||||
|
||||
{300, "Propulsive Barrel (Detonate)"_s},
|
||||
{301, "Roundbox Barrel (Detonate)"_s},
|
||||
{302, "ShieldDet Barrel (Detonate)"_s},
|
||||
{303, "RecoilDet Barrel (Detonate) (Motion)"_s},
|
||||
{397, "Short D Base Barrel (Detonate)"_s},
|
||||
{398, "Medium D Base Barrel (Detonate)"_s},
|
||||
{399, "Long D Base Barrel (Detonate)"_s},
|
||||
};
|
||||
// endregion
|
||||
|
||||
//region Energy Shooters
|
||||
static const std::map<Int, Containers::StringView> eshooter_triggers {
|
||||
{0, "EN-Rifle Trigger (1H)"_s},
|
||||
{1, "Underarm Trigger (1H)"_s},
|
||||
{2, "EN-Inverted Trigger (1H)"_s},
|
||||
{3, "EN-Submachine Trigger (1H) (Motion)"_s},
|
||||
{4, "EN-Needler Trigger (1H)"_s},
|
||||
{5, "Angular Trigger (1H)"_s},
|
||||
{6, "Exposed Trigger (1H)"_s},
|
||||
{99, "Base EnTrigger (1H)"_s},
|
||||
|
||||
{100, "EN-Combat Trigger (2H)"_s},
|
||||
{101, "EN-Alternate Trigger (2H)"_s},
|
||||
{102, "Framed Trigger (2H) (Motion)"_s},
|
||||
{103, "Stabilised Trigger (2H)"_s},
|
||||
{104, "EN-Heavy Trigger (2H)"_s},
|
||||
{199, "2H Base EnTrigger (2H)"_s},
|
||||
};
|
||||
|
||||
static const std::map<Int, Containers::StringView> eshooter_busters {
|
||||
{0, "EN-Combat Buster (1 shot)"_s},
|
||||
{1, "Delta Cycler (1 shot) (Motion)"_s},
|
||||
{2, "EN-Longbarrel Buster (1 shot)"_s},
|
||||
{3, "Kinetic Buster (1 shot) (Motion)"_s},
|
||||
{97, "Short S Base Buster (1 shot)"_s},
|
||||
{98, "Medium S Base Buster (1 shot)"_s},
|
||||
{99, "Long S Base Buster (1 shot)"_s},
|
||||
|
||||
{100, "EN-Rifle Buster (Auto)"_s},
|
||||
{101, "EN-Focus Buster (Auto)"_s},
|
||||
{102, "Machinist Buster (Auto)"_s},
|
||||
{103, "EN-Precision Buster (Auto) (Motion)"_s},
|
||||
{197, "Short A Base Buster (Auto)"_s},
|
||||
{198, "Medium A Base Buster (Auto)"_s},
|
||||
{199, "Long A Base Buster (Auto)"_s},
|
||||
|
||||
{200, "Railcharge Buster (Ray) (Motion)"_s},
|
||||
{201, "Clawcharge Buster (Ray)"_s},
|
||||
{202, "Twizelcharge Buster (Ray)"_s},
|
||||
{203, "Deltacharge Buster (Ray)"_s},
|
||||
{297, "Short R Base Buster (Ray)"_s},
|
||||
{298, "Medium R Base Buster (Ray)"_s},
|
||||
{299, "Long R Base Buster (Ray)"_s},
|
||||
|
||||
{300, "Subsonic Buster (Wave)"_s},
|
||||
{301, "Amplifier Buster (Wave) (Motion)"_s},
|
||||
{302, "Cyclonwave Buster (Wave)"_s},
|
||||
{303, "Warhorn Buster (Wave) (Motion)"_s},
|
||||
{397, "Short W Base Buster (Wave)"_s},
|
||||
{398, "Medium W Base Buster (Wave)"_s},
|
||||
{399, "Long W Base Buster (Wave)"_s},
|
||||
};
|
||||
// endregion
|
||||
|
||||
// region Bullet Launchers
|
||||
static const std::map<Int, Containers::StringView> blauncher_pods {
|
||||
{0, "BL-Delta Pack Launcher (Missile x12)"_s},
|
||||
{1, "BL-Twin Pack Launcher (Missile x12)"_s},
|
||||
{2, "Detector Launcher (Missile x12)"_s},
|
||||
{3, "BL-Triplet Pack Launcher (Missile x12)"_s},
|
||||
{4, "Shielded Launcher (Missile x12)"_s},
|
||||
{99, "H Base Pod (Missile x12)"_s},
|
||||
|
||||
{100, "Warhead Pod (Nuke x2)"_s},
|
||||
{101, "Warhead Launcher (Nuke x2)"_s},
|
||||
{102, "Triangular Warhead Pod (Nuke x2)"_s},
|
||||
{103, "Expanded Warhead Pod (Nuke x2)"_s},
|
||||
{104, "Shielded Warhead Pod (Nuke x2)"_s},
|
||||
{199, "N Base Pod (Nuke x2)"_s},
|
||||
|
||||
{200, "Widepack Launcher (Salvo x24)"_s},
|
||||
{201, "Covered Launcher (Salvo x24)"_s},
|
||||
{202, "Double Delta Launcher (Salvo x24)"_s},
|
||||
{203, "Hexagonal Launcher (Salvo x24)"_s},
|
||||
{204, "Shielded Six Launcher (Salvo x24)"_s},
|
||||
{299, "S Base Pod (Salvo x24)"_s},
|
||||
|
||||
{300, "Sentinel Cluster Pod (Cluster x40)"_s},
|
||||
{301, "Pincer Cluster Pod (Cluster x40)"_s},
|
||||
{302, "Elliptical Cluster Pod (Cluster x40)"_s},
|
||||
{303, "Sawed Cluster Pod (Cluster x40)"_s},
|
||||
{304, "Pentagonal Cluster Pod (Cluster x40)"_s},
|
||||
{399, "C Base Pod (Cluster x40)"_s},
|
||||
};
|
||||
|
||||
static const std::map<Int, Containers::StringView> blauncher_projectiles {
|
||||
{0, "Flathead Missile"_s},
|
||||
{1, "Warhead Missile"_s},
|
||||
{2, "Pointhead Missile"_s},
|
||||
{3, "Marker Missile"_s},
|
||||
{4, "ArB Missile"_s},
|
||||
};
|
||||
// endregion
|
||||
|
||||
// region Energy Launchers
|
||||
static const std::map<Int, Containers::StringView> elauncher_generators {
|
||||
{0, "Fly Unit"_s},
|
||||
{1, "Assault Unit (Motion)"_s},
|
||||
{2, "Falcon Unit"_s},
|
||||
{3, "Drake Unit (Motion)"_s},
|
||||
{4, "Kingfisher Unit"_s},
|
||||
{5, "Tri-Edge Unit"_s},
|
||||
{6, "Flatline Unit"_s},
|
||||
{7, "Boost Unit"_s},
|
||||
{8, "Sparrow Unit"_s},
|
||||
{9, "Guarded Unit"_s},
|
||||
{10, "Sailtail Unit"_s},
|
||||
{11, "Tri-Covered Unit"_s},
|
||||
{12, "Pointy Unit"_s},
|
||||
{13, "Scope-Like Unit"_s},
|
||||
{14, "Rotating Unit (Motion)"_s},
|
||||
{15, "Clamper Unit"_s},
|
||||
{16, "Quadsat Unit"_s},
|
||||
{17, "Squ-Rotating Unit (Motion)"_s},
|
||||
{18, "Bloom Unit"_s},
|
||||
{19, "Edge-Rotating Unit (Motion)"_s},
|
||||
{20, "Shipend Unit"_s},
|
||||
{21, "Revwing Unit"_s},
|
||||
{22, "Viper Unit"_s},
|
||||
{23, "EX Unit"_s},
|
||||
{24, "Aery Unit"_s},
|
||||
{25, "Carrier Unit"_s},
|
||||
{26, "Compartment Unit"_s},
|
||||
{27, "Flatedge Unit"_s},
|
||||
{99, "Base Generator"},
|
||||
};
|
||||
|
||||
static const std::map<Int, Containers::StringView> elauncher_pods {
|
||||
{0, "EN-Dual Claw Launcher (Echo) (Motion)"_s},
|
||||
{1, "EN-Assault Launcher (Echo)"_s},
|
||||
{2, "EN-Tactical Launcher (Echo)"_s},
|
||||
{3, "EN-Force Focus Launcher (Echo) (Motion)"_s},
|
||||
{4, "EN-Needler Launcher (Echo)"_s},
|
||||
{5, "Spark Launcher (Echo)"_s},
|
||||
{6, "Pinpoint Launcher (Echo)"_s},
|
||||
{99, "E Base EPod (Echo)"_s},
|
||||
|
||||
{100, "Raystream Launcher (Beam)"_s},
|
||||
{101, "Perpetum Launcher (Beam)"_s},
|
||||
{102, "Scorcher Launcher (Beam)"_s},
|
||||
{103, "Concentrator Launcher (Beam)"_s},
|
||||
{104, "Crosshair Launcher (Beam)"_s},
|
||||
{105, "Powerlined Launcher (Beam)"_s},
|
||||
{106, "Attached Launcher (Beam)"_s},
|
||||
{199, "B Base EPod (Beam)"_s},
|
||||
|
||||
{200, "Hilt Launcher (Slash) (Motion)"_s},
|
||||
{201, "Underangle Launcher (Slash)"_s},
|
||||
{202, "Crossblade Launcher (Slash)"_s},
|
||||
{203, "Deltablade Launcher (Slash) (Motion)"_s},
|
||||
{204, "Spike Launcher (Slash)"_s},
|
||||
{205, "Tri-Pronged Launcher (Slash) (Motion)"_s},
|
||||
{206, "Heavyblade Launcher (Slash)"_s},
|
||||
{299, "S Base EPod (Slash)"_s},
|
||||
|
||||
{300, "Covering Launcher (Photon)"_s},
|
||||
{301, "Boxhead Launcher (Photon)"_s},
|
||||
{302, "Stabilised Launcher (Photon)"_s},
|
||||
{303, "Flatline Launcher (Photon)"_s},
|
||||
{304, "Shelled Launcher (Photon)"_s},
|
||||
{305, "Widearm Launcher (Photon)"_s},
|
||||
{306, "Wingspan Launcher (Photon)"_s},
|
||||
{399, "P Base EPod (Photon)"_s},
|
||||
};
|
||||
// endregion
|
|
@ -0,0 +1,24 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifdef c
|
||||
c(Melee, "enuWeaponTypes::NewEnumerator0"_s, "Melee weapon"_s)
|
||||
c(BulletShooter, "enuWeaponTypes::NewEnumerator1"_s, "Bullet shooter"_s)
|
||||
c(EnergyShooter, "enuWeaponTypes::NewEnumerator2"_s, "Energy shooter"_s)
|
||||
c(BulletLauncher, "enuWeaponTypes::NewEnumerator3"_s, "Bullet launcher"_s)
|
||||
c(EnergyLauncher, "enuWeaponTypes::NewEnumerator4"_s, "Energy launcher"_s)
|
||||
c(Shield, "enuWeaponTypes::NewEnumerator5"_s, "Shield"_s)
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Vector3.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
struct Accessory {
|
||||
Int attachIndex = -1;
|
||||
Int id = -1;
|
||||
Containers::StaticArray<2, Int> styles{ValueInit};
|
||||
Vector3 relativePosition{0.0f};
|
||||
Vector3 relativePositionOffset{0.0f};
|
||||
Vector3 relativeRotation{0.0f};
|
||||
Vector3 relativeRotationOffset{0.0f};
|
||||
Vector3 localScale{1.0f};
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "Decal.h"
|
||||
#include "Accessory.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
enum class ArmourSlot {
|
||||
#define c(enumerator, enumstr, name) enumerator,
|
||||
#include "../Maps/ArmourSlots.hpp"
|
||||
#undef c
|
||||
};
|
||||
|
||||
struct ArmourPart {
|
||||
ArmourSlot slot = ArmourSlot::Face;
|
||||
Int id = 0;
|
||||
Containers::StaticArray<4, Int> styles{ValueInit};
|
||||
Containers::Array<Decal> decals;
|
||||
Containers::Array<Accessory> accessories;
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Vector3.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
enum class BulletLauncherAttachmentStyle {
|
||||
#define c(enumerator, enumstr) enumerator,
|
||||
#include "../Maps/BulletLauncherAttachmentStyles.hpp"
|
||||
#undef c
|
||||
};
|
||||
|
||||
enum class BulletLauncherSocket {
|
||||
#define c(enumerator, enumstr, name) enumerator,
|
||||
#include "../Maps/BulletLauncherSockets.hpp"
|
||||
#undef c
|
||||
};
|
||||
|
||||
struct BulletLauncherAttachment {
|
||||
BulletLauncherSocket socket = BulletLauncherSocket::Auto;
|
||||
Vector3 relativeLocation;
|
||||
Vector3 offsetLocation;
|
||||
Vector3 relativeRotation;
|
||||
Vector3 offsetRotation;
|
||||
Vector3 relativeScale;
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -16,36 +16,25 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Color.h>
|
||||
#include <Magnum/Math/Vector2.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
enum class GameState : UnsignedByte {
|
||||
Unknown, NotRunning, Running
|
||||
};
|
||||
|
||||
class MassBuilderManager {
|
||||
public:
|
||||
MassBuilderManager();
|
||||
|
||||
auto ready() const -> bool;
|
||||
auto lastError() -> std::string const&;
|
||||
|
||||
auto saveDirectory() -> std::string const&;
|
||||
|
||||
void checkGameState();
|
||||
auto gameState() -> GameState;
|
||||
|
||||
private:
|
||||
auto findSaveDirectory() -> bool;
|
||||
|
||||
bool _ready = false;
|
||||
|
||||
std::string _lastError = "";
|
||||
|
||||
std::string _saveDirectory = "";
|
||||
|
||||
GameState _gameState = GameState::Unknown;
|
||||
struct CustomStyle {
|
||||
Containers::String name;
|
||||
Color4 colour{0.0f};
|
||||
Float metallic = 0.5f;
|
||||
Float gloss = 0.5f;
|
||||
bool glow = false;
|
||||
|
||||
Int patternId = 0;
|
||||
Float opacity = 0.5f;
|
||||
Vector2 offset{0.5f};
|
||||
Float rotation = 0.0f;
|
||||
Float scale = 0.5f;
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Color.h>
|
||||
#include <Magnum/Math/Vector2.h>
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
struct Decal {
|
||||
Int id = -1;
|
||||
Color4 colour{0.0f};
|
||||
Vector3 position{0.0f};
|
||||
Vector3 uAxis{0.0f};
|
||||
Vector3 vAxis{0.0f};
|
||||
Vector2 offset{0.5f};
|
||||
Float scale = 0.5f;
|
||||
Float rotation = 0.0f;
|
||||
bool flip = false;
|
||||
bool wrap = false;
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
struct Joints {
|
||||
Float neck = 0.0f;
|
||||
Float body = 0.0f;
|
||||
Float shoulders = 0.0f;
|
||||
Float hips = 0.0f;
|
||||
Float upperArms = 0.0f;
|
||||
Float lowerArms = 0.0f;
|
||||
Float upperLegs = 0.0f;
|
||||
Float lowerLegs = 0.0f;
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -14,138 +14,369 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Containers/Pair.h>
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/BoolProperty.h"
|
||||
#include "../UESaveFile/Types/ColourStructProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
#include "../UESaveFile/Types/StringProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
|
||||
constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\x0c\0\0\0StrProperty";
|
||||
constexpr char steamid_locator[] = "Account\0\x0c\0\0\0StrProperty";
|
||||
Mass::Mass(Containers::StringView path) {
|
||||
auto split = Utility::Path::split(path);
|
||||
_folder = split.first();
|
||||
_filename = split.second();
|
||||
|
||||
std::string Mass::_lastError;
|
||||
|
||||
Mass::Mass(const std::string& filename) {
|
||||
_filename = filename;
|
||||
|
||||
if(!Utility::Directory::exists(_filename)) {
|
||||
_lastError = "The file " + _filename + " couldn't be found.";
|
||||
return;
|
||||
refreshValues();
|
||||
}
|
||||
|
||||
auto mmap = Utility::Directory::mapRead(_filename);
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
|
||||
|
||||
if(iter != mmap.end()) {
|
||||
_name = std::string{iter + 70};
|
||||
_state = MassState::Valid;
|
||||
}
|
||||
else {
|
||||
_lastError = "The name couldn't be found in " + filename;
|
||||
_state = MassState::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::lastError() -> std::string const& {
|
||||
auto Mass::lastError() -> Containers::StringView {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
auto Mass::getNameFromFile(const std::string& filename) -> std::string {
|
||||
if(!Utility::Directory::exists(filename)) {
|
||||
_lastError = "The file " + filename + " couldn't be found.";
|
||||
return "";
|
||||
auto Mass::getNameFromFile(Containers::StringView path) -> Containers::Optional<Containers::String> {
|
||||
if(!Utility::Path::exists(path)) {
|
||||
LOG_ERROR_FORMAT("{} couldn't be found.", path);
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
std::string name = "";
|
||||
UESaveFile mass{path};
|
||||
|
||||
auto mmap = Utility::Directory::mapRead(filename);
|
||||
if(!mass.valid()) {
|
||||
LOG_ERROR_FORMAT("{} is invalid: {}", path, mass.lastError());
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
|
||||
auto unit_data = mass.at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
|
||||
if(iter != mmap.end()) {
|
||||
name = std::string{iter + 70};
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, path);
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
auto name_prop = unit_data->at<StringProperty>(MASS_NAME);
|
||||
|
||||
if(!name_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, path);
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
return {name_prop->value};
|
||||
}
|
||||
|
||||
void Mass::refreshValues() {
|
||||
LOG_INFO_FORMAT("Refreshing values for {}.", _filename);
|
||||
|
||||
logger().lockMutex();
|
||||
logger().indent();
|
||||
logger().unlockMutex();
|
||||
|
||||
Containers::ScopeGuard indent_guard{[]{
|
||||
logger().lockMutex();
|
||||
logger().unindent();
|
||||
logger().unlockMutex();
|
||||
}};
|
||||
|
||||
LOG_INFO("Checking if file exists.");
|
||||
if(!Utility::Path::exists(Utility::Path::join(_folder, _filename))) {
|
||||
LOG_WARNING_FORMAT("{} doesn't exist in {}.", _filename, _folder);
|
||||
_state = State::Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_mass) {
|
||||
LOG_INFO("Reading the GVAS save.");
|
||||
_mass.emplace(Utility::Path::join(_folder, _filename));
|
||||
if(!_mass->valid()) {
|
||||
LOG_ERROR(_mass->lastError());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_lastError = "The name couldn't be found in " + filename;
|
||||
LOG_INFO("Reloading the GVAS data.");
|
||||
if(!_mass->reloadData()) {
|
||||
LOG_ERROR(_mass->lastError());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
LOG_INFO("Checking the save file type.");
|
||||
if(_mass->saveType() != "/Game/Core/Save/bpSaveGameUnit.bpSaveGameUnit_C"_s) {
|
||||
LOG_ERROR_FORMAT("{} is not a valid unit save.", _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto Mass::filename() -> std::string const&{
|
||||
LOG_INFO("Getting the unit data.");
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Reading the M.A.S.S. name.");
|
||||
auto name_prop = unit_data->at<StringProperty>(MASS_NAME);
|
||||
|
||||
if(!name_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename);
|
||||
_name = Containers::NullOpt;
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
_name = {name_prop->value};
|
||||
|
||||
getJointSliders();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getFrameStyles();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getEyeFlareColour();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getFrameCustomStyles();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getArmourParts();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getBulletLauncherAttachments();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getArmourCustomStyles();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getMeleeWeapons();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getShields();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getBulletShooters();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getEnergyShooters();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getBulletLaunchers();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getEnergyLaunchers();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getGlobalStyles();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getTuning();
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Getting the associated account.");
|
||||
auto account_prop = _mass->at<StringProperty>("Account"_s);
|
||||
if(!account_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ACCOUNT, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
_account = account_prop->value;
|
||||
|
||||
_state = State::Valid;
|
||||
}
|
||||
|
||||
auto Mass::filename() -> Containers::StringView {
|
||||
return _filename;
|
||||
}
|
||||
|
||||
auto Mass::name() -> std::string const&{
|
||||
return _name;
|
||||
auto Mass::name() -> Containers::StringView {
|
||||
CORRADE_INTERNAL_ASSERT(_name);
|
||||
return *_name;
|
||||
}
|
||||
|
||||
auto Mass::getName() -> std::string const& {
|
||||
if(!Utility::Directory::exists(_filename)) {
|
||||
_lastError = "The file " + _filename + " couldn't be found.";
|
||||
_state = MassState::Empty;
|
||||
return _name = "";
|
||||
}
|
||||
auto Mass::setName(Containers::StringView new_name) -> bool {
|
||||
_name = {new_name};
|
||||
|
||||
auto mmap = Utility::Directory::mapRead(_filename);
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
|
||||
|
||||
if(iter != mmap.end()) {
|
||||
_state = MassState::Valid;
|
||||
return _name = std::string{iter + 70};
|
||||
}
|
||||
else {
|
||||
_lastError = "The name couldn't be found in " + _filename;
|
||||
_state = MassState::Invalid;
|
||||
return _name = "";
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::state() -> MassState {
|
||||
return _state;
|
||||
}
|
||||
|
||||
auto Mass::updateSteamId(const std::string& steam_id) -> bool {
|
||||
if(!Utility::Directory::exists(_filename)) {
|
||||
_lastError = "The file " + _filename + " couldn't be found.";
|
||||
_state = MassState::Empty;
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
Utility::Directory::copy(_filename, _filename + ".tmp");
|
||||
auto name_prop = unit_data->at<StringProperty>("Name_45_A037C5D54E53456407BDF091344529BB"_s);
|
||||
|
||||
{
|
||||
auto mmap = Utility::Directory::map(_filename + ".tmp");
|
||||
|
||||
auto iter = std::search(mmap.begin(), mmap.end(), &steamid_locator[0], &steamid_locator[23]);
|
||||
|
||||
if(iter == mmap.end()) {
|
||||
_lastError = "The M.A.S.S. file at " + _filename + " seems to be corrupt.";
|
||||
Utility::Directory::rm(_filename + ".tmp");
|
||||
if(!name_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
iter += 37;
|
||||
name_prop->value = new_name;
|
||||
|
||||
if(std::strncmp(iter, steam_id.c_str(), steam_id.length()) != 0) {
|
||||
for(int i = 0; i < 17; ++i) {
|
||||
*(iter + i) = steam_id[i];
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Utility::Directory::exists(_filename)) {
|
||||
Utility::Directory::rm(_filename);
|
||||
}
|
||||
|
||||
Utility::Directory::move(_filename + ".tmp", _filename);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mass::state() -> State {
|
||||
return _state;
|
||||
}
|
||||
|
||||
auto Mass::dirty() const -> bool {
|
||||
return _dirty;
|
||||
}
|
||||
|
||||
void Mass::setDirty(bool dirty) {
|
||||
_dirty = dirty;
|
||||
}
|
||||
|
||||
void Mass::getTuning() {
|
||||
getTuningCategory(MASS_ENGINE, _tuning.engineId,
|
||||
MASS_GEARS, _tuning.gearIds);
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getTuningCategory(MASS_OS, _tuning.osId,
|
||||
MASS_MODULES, _tuning.moduleIds);
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getTuningCategory(MASS_ARCHITECT, _tuning.archId,
|
||||
MASS_TECHS, _tuning.techIds);
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::engine() -> Int& {
|
||||
return _tuning.engineId;
|
||||
}
|
||||
|
||||
auto Mass::gears() -> Containers::ArrayView<Int> {
|
||||
return _tuning.gearIds;
|
||||
}
|
||||
|
||||
auto Mass::os() -> Int& {
|
||||
return _tuning.osId;
|
||||
}
|
||||
|
||||
auto Mass::modules() -> Containers::ArrayView<Int> {
|
||||
return _tuning.moduleIds;
|
||||
}
|
||||
|
||||
auto Mass::architecture() -> Int& {
|
||||
return _tuning.archId;
|
||||
}
|
||||
|
||||
auto Mass::techs() -> Containers::ArrayView<Int> {
|
||||
return _tuning.techIds;
|
||||
}
|
||||
|
||||
auto Mass::account() -> Containers::StringView {
|
||||
return _account;
|
||||
}
|
||||
|
||||
auto Mass::updateAccount(Containers::StringView new_account) -> bool {
|
||||
_account = new_account;
|
||||
|
||||
auto account = _mass->at<StringProperty>(MASS_ACCOUNT);
|
||||
if(!account) {
|
||||
_lastError = "Couldn't find the " MASS_ACCOUNT " property."_s;
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
account->value = new_account;
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mass::getTuningCategory(Containers::StringView big_node_prop_name, Int& big_node_id,
|
||||
Containers::StringView small_nodes_prop_name, Containers::ArrayView<Int> small_nodes_ids)
|
||||
{
|
||||
LOG_INFO_FORMAT("Getting tuning data ({}, {}).", big_node_prop_name, small_nodes_prop_name);
|
||||
|
||||
auto node_id = _mass->at<IntProperty>(big_node_prop_name);
|
||||
if(!node_id) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", big_node_prop_name, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
big_node_id = node_id->value;
|
||||
|
||||
auto node_ids = _mass->at<ArrayProperty>(small_nodes_prop_name);
|
||||
if(!node_ids) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", small_nodes_prop_name, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(node_ids->items.size() != small_nodes_ids.size()) {
|
||||
LOG_ERROR_FORMAT("Node ID arrays are not of the same size. Expected {}, got {} instead.",
|
||||
small_nodes_ids.size(), node_ids->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
for(UnsignedInt i = 0; i < small_nodes_ids.size(); i++) {
|
||||
auto small_node_id = node_ids->at<IntProperty>(i);
|
||||
CORRADE_INTERNAL_ASSERT(small_node_id);
|
||||
small_nodes_ids[i] = small_node_id->value;
|
||||
}
|
||||
}
|
||||
|
|
188
src/Mass/Mass.h
188
src/Mass/Mass.h
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -16,19 +16,40 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
#include <Corrade/Containers/Pointer.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Color.h>
|
||||
#include <Magnum/Math/Vector2.h>
|
||||
#include <Magnum/Math/Vector3.h>
|
||||
|
||||
#include "Joints.h"
|
||||
#include "BulletLauncherAttachment.h"
|
||||
#include "CustomStyle.h"
|
||||
#include "Decal.h"
|
||||
#include "Accessory.h"
|
||||
#include "ArmourPart.h"
|
||||
#include "WeaponPart.h"
|
||||
#include "Weapon.h"
|
||||
|
||||
#include "../UESaveFile/UESaveFile.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
enum class MassState : UnsignedByte {
|
||||
Empty, Invalid, Valid
|
||||
};
|
||||
struct ArrayProperty;
|
||||
|
||||
class Mass {
|
||||
public:
|
||||
explicit Mass(const std::string& filename);
|
||||
enum class State : UnsignedByte {
|
||||
Empty, Invalid, Valid
|
||||
};
|
||||
|
||||
explicit Mass(Containers::StringView path);
|
||||
|
||||
Mass(const Mass&) = delete;
|
||||
Mass& operator=(const Mass&) = delete;
|
||||
|
@ -36,23 +57,156 @@ class Mass {
|
|||
Mass(Mass&&) = default;
|
||||
Mass& operator=(Mass&&) = default;
|
||||
|
||||
static auto lastError() -> std::string const&;
|
||||
auto lastError() -> Containers::StringView;
|
||||
|
||||
static auto getNameFromFile(const std::string& filename) -> std::string;
|
||||
static auto getNameFromFile(Containers::StringView path) -> Containers::Optional<Containers::String>;
|
||||
|
||||
auto filename() -> std::string const&;
|
||||
void refreshValues();
|
||||
|
||||
auto name() -> std::string const&;
|
||||
auto getName() -> std::string const&;
|
||||
auto filename() -> Containers::StringView;
|
||||
|
||||
auto state() -> MassState;
|
||||
auto name() -> Containers::StringView;
|
||||
auto setName(Containers::StringView new_name) -> bool;
|
||||
|
||||
auto updateSteamId(const std::string& steam_id) -> bool;
|
||||
auto state() -> State;
|
||||
|
||||
auto dirty() const -> bool;
|
||||
void setDirty(bool dirty = true);
|
||||
|
||||
auto jointSliders() -> Joints&;
|
||||
void getJointSliders();
|
||||
auto writeJointSliders() -> bool;
|
||||
|
||||
auto frameStyles() -> Containers::ArrayView<Int>;
|
||||
void getFrameStyles();
|
||||
auto writeFrameStyles() -> bool;
|
||||
|
||||
auto eyeFlareColour() -> Color4&;
|
||||
void getEyeFlareColour();
|
||||
auto writeEyeFlareColour() -> bool;
|
||||
|
||||
auto frameCustomStyles() -> Containers::ArrayView<CustomStyle>;
|
||||
void getFrameCustomStyles();
|
||||
auto writeFrameCustomStyle(UnsignedLong index) -> bool;
|
||||
|
||||
auto armourParts() -> Containers::ArrayView<ArmourPart>;
|
||||
void getArmourParts();
|
||||
auto writeArmourPart(ArmourSlot slot) -> bool;
|
||||
|
||||
auto bulletLauncherAttachmentStyle() -> BulletLauncherAttachmentStyle&;
|
||||
auto bulletLauncherAttachments() -> Containers::ArrayView<BulletLauncherAttachment>;
|
||||
void getBulletLauncherAttachments();
|
||||
auto writeBulletLauncherAttachments() -> bool;
|
||||
|
||||
auto armourCustomStyles() -> Containers::ArrayView<CustomStyle>;
|
||||
void getArmourCustomStyles();
|
||||
auto writeArmourCustomStyle(UnsignedLong index) -> bool;
|
||||
|
||||
auto meleeWeapons() -> Containers::ArrayView<Weapon>;
|
||||
void getMeleeWeapons();
|
||||
auto writeMeleeWeapons() -> bool;
|
||||
|
||||
auto shields() -> Containers::ArrayView<Weapon>;
|
||||
void getShields();
|
||||
auto writeShields() -> bool;
|
||||
|
||||
auto bulletShooters() -> Containers::ArrayView<Weapon>;
|
||||
void getBulletShooters();
|
||||
auto writeBulletShooters() -> bool;
|
||||
|
||||
auto energyShooters() -> Containers::ArrayView<Weapon>;
|
||||
void getEnergyShooters();
|
||||
auto writeEnergyShooters() -> bool;
|
||||
|
||||
auto bulletLaunchers() -> Containers::ArrayView<Weapon>;
|
||||
void getBulletLaunchers();
|
||||
auto writeBulletLaunchers() -> bool;
|
||||
|
||||
auto energyLaunchers() -> Containers::ArrayView<Weapon>;
|
||||
void getEnergyLaunchers();
|
||||
auto writeEnergyLaunchers() -> bool;
|
||||
|
||||
auto globalStyles() -> Containers::ArrayView<CustomStyle>;
|
||||
void getGlobalStyles();
|
||||
auto writeGlobalStyle(UnsignedLong index) -> bool;
|
||||
|
||||
void getTuning();
|
||||
|
||||
auto engine() -> Int&;
|
||||
auto gears() -> Containers::ArrayView<Int>;
|
||||
|
||||
auto os() -> Int&;
|
||||
auto modules() -> Containers::ArrayView<Int>;
|
||||
|
||||
auto architecture() -> Int&;
|
||||
auto techs() -> Containers::ArrayView<Int>;
|
||||
|
||||
auto account() -> Containers::StringView;
|
||||
auto updateAccount(Containers::StringView new_account) -> bool;
|
||||
|
||||
private:
|
||||
static std::string _lastError;
|
||||
void getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayProperty* style_array);
|
||||
auto writeCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool;
|
||||
|
||||
std::string _filename = "";
|
||||
std::string _name = "";
|
||||
MassState _state = MassState::Empty;
|
||||
void getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array);
|
||||
void writeDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array);
|
||||
|
||||
void getAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accessory_array);
|
||||
void writeAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accs_array);
|
||||
|
||||
void getWeaponType(Containers::StringView prop_name, Containers::ArrayView<Weapon> weapon_array);
|
||||
auto writeWeaponType(Containers::StringView prop_name, Containers::ArrayView<Weapon> weapon_array) -> bool;
|
||||
|
||||
void getTuningCategory(Containers::StringView big_node_prop_name, Int& big_node_id,
|
||||
Containers::StringView small_nodes_prop_name, Containers::ArrayView<Int> small_nodes_ids);
|
||||
|
||||
Containers::Optional<UESaveFile> _mass;
|
||||
|
||||
Containers::String _lastError;
|
||||
|
||||
Containers::String _folder;
|
||||
Containers::String _filename;
|
||||
State _state = State::Empty;
|
||||
|
||||
bool _dirty = false;
|
||||
|
||||
Containers::Optional<Containers::String> _name = Containers::NullOpt;
|
||||
|
||||
struct {
|
||||
Joints joints{};
|
||||
Containers::StaticArray<4, Int> styles{ValueInit};
|
||||
Color4 eyeFlare{0.0f};
|
||||
Containers::StaticArray<16, CustomStyle> customStyles;
|
||||
} _frame;
|
||||
|
||||
struct {
|
||||
Containers::StaticArray<38, ArmourPart> parts;
|
||||
Containers::StaticArray<16, CustomStyle> customStyles;
|
||||
BulletLauncherAttachmentStyle blAttachmentStyle = BulletLauncherAttachmentStyle::NotFound;
|
||||
Containers::StaticArray<4, BulletLauncherAttachment> blAttachment;
|
||||
} _armour;
|
||||
|
||||
struct {
|
||||
Containers::StaticArray<8, Weapon> melee;
|
||||
Containers::StaticArray<1, Weapon> shields;
|
||||
Containers::StaticArray<4, Weapon> bulletShooters;
|
||||
Containers::StaticArray<4, Weapon> energyShooters;
|
||||
Containers::StaticArray<4, Weapon> bulletLaunchers;
|
||||
Containers::StaticArray<4, Weapon> energyLaunchers;
|
||||
} _weapons;
|
||||
|
||||
Containers::Array<CustomStyle> _globalStyles;
|
||||
|
||||
struct {
|
||||
Int engineId;
|
||||
Containers::StaticArray<7, Int> gearIds;
|
||||
|
||||
Int osId;
|
||||
Containers::StaticArray<7, Int> moduleIds;
|
||||
|
||||
Int archId;
|
||||
Containers::StaticArray<7, Int> techIds;
|
||||
} _tuning;
|
||||
|
||||
Containers::String _account;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/ByteProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
#include "../UESaveFile/Types/StringProperty.h"
|
||||
#include "../UESaveFile/Types/VectorStructProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
auto Mass::armourParts() -> Containers::ArrayView<ArmourPart> {
|
||||
return _armour.parts;
|
||||
}
|
||||
|
||||
void Mass::getArmourParts() {
|
||||
LOG_INFO("Getting armour parts.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto armour_array = unit_data->at<ArrayProperty>(MASS_ARMOUR_PARTS);
|
||||
if(!armour_array) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ARMOUR_PARTS, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(armour_array->items.size() != _armour.parts.size()) {
|
||||
LOG_ERROR_FORMAT("Armour part arrays are not of the same size. Expected {}, got {} instead.",
|
||||
_armour.parts.size(), armour_array->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
for(UnsignedInt i = 0; i < armour_array->items.size(); i++) {
|
||||
auto part_prop = armour_array->at<GenericStructProperty>(i);
|
||||
auto& part = _armour.parts[i];
|
||||
|
||||
auto& armour_slot = part_prop->at<ByteProperty>(MASS_ARMOUR_SLOT)->enumValue;
|
||||
#define c(enumerator, strenum, name) if(armour_slot == (strenum)) { part.slot = ArmourSlot::enumerator; } else
|
||||
#include "../Maps/ArmourSlots.hpp"
|
||||
#undef c
|
||||
{
|
||||
LOG_ERROR_FORMAT("Invalid armour slot enumerator {}.", armour_slot);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
part.id = part_prop->at<IntProperty>(MASS_ARMOUR_ID)->value;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>(MASS_ARMOUR_STYLES);
|
||||
if(!part_styles) {
|
||||
LOG_ERROR_FORMAT("Part styles not found for part number {}.", i);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(part_styles->items.size() != part.styles.size()) {
|
||||
LOG_ERROR_FORMAT("Armour part style arrays are not of the same size. Expected {}, got {} instead.",
|
||||
part.styles.size(), part_styles->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
for(UnsignedInt j = 0; j < part_styles->items.size(); j++) {
|
||||
part.styles[j] = part_styles->at<IntProperty>(j)->value;
|
||||
}
|
||||
|
||||
auto decals_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_DECALS);
|
||||
if(!decals_array) {
|
||||
LOG_ERROR_FORMAT("Part decals not found for part number {}.", i);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
part.decals = Containers::Array<Decal>{decals_array->items.size()};
|
||||
|
||||
getDecals(part.decals, decals_array);
|
||||
|
||||
auto accs_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_ACCESSORIES);
|
||||
if(!accs_array) {
|
||||
LOG_WARNING_FORMAT("Part accessories not found for part number {}.", i);
|
||||
part.accessories = Containers::Array<Accessory>{};
|
||||
continue;
|
||||
}
|
||||
|
||||
if(part.accessories.size() != accs_array->items.size()) {
|
||||
part.accessories = Containers::Array<Accessory>{accs_array->items.size()};
|
||||
}
|
||||
|
||||
getAccessories(part.accessories, accs_array);
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::writeArmourPart(ArmourSlot slot) -> bool {
|
||||
LOG_INFO_FORMAT("Writing armour part in slot {}.", static_cast<int>(slot));
|
||||
|
||||
auto& part = *std::find_if(_armour.parts.begin(), _armour.parts.end(), [&slot](const ArmourPart& part){ return slot == part.slot; });
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "Couldn't find the unit data in " + _filename + ".";
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto armour_array = unit_data->at<ArrayProperty>(MASS_ARMOUR_PARTS);
|
||||
if(!armour_array) {
|
||||
_lastError = "Couldn't find the armour part array in " + _filename + ".";
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
Containers::StringView slot_str = nullptr;
|
||||
switch(slot) {
|
||||
#define c(enumerator, strenum, name) case ArmourSlot::enumerator: \
|
||||
slot_str = strenum; \
|
||||
break;
|
||||
#include "../Maps/ArmourSlots.hpp"
|
||||
#undef c
|
||||
}
|
||||
|
||||
GenericStructProperty* part_prop = nullptr;
|
||||
|
||||
for(UnsignedInt i = 0; i < armour_array->items.size(); i++) {
|
||||
part_prop = armour_array->at<GenericStructProperty>(i);
|
||||
if(slot_str == part_prop->at<ByteProperty>(MASS_ARMOUR_SLOT)->enumValue) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
part_prop = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!part_prop) {
|
||||
auto prefix = "Couldn't find the armour part for slot "_s;
|
||||
switch(slot) {
|
||||
#define c(enumerator, strenum, name) case ArmourSlot::enumerator: \
|
||||
_lastError = prefix + "ArmourSlot::" #enumerator "."_s; \
|
||||
break;
|
||||
#include "../Maps/ArmourSlots.hpp"
|
||||
#undef c
|
||||
}
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
part_prop->at<IntProperty>(MASS_ARMOUR_ID)->value = part.id;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>(MASS_ARMOUR_STYLES);
|
||||
for(UnsignedInt i = 0; i < part.styles.size(); i++) {
|
||||
part_styles->at<IntProperty>(i)->value = part.styles[i];
|
||||
}
|
||||
|
||||
auto decals_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_DECALS);
|
||||
writeDecals(part.decals, decals_array);
|
||||
|
||||
if(part.accessories.size() != 0) {
|
||||
auto accs_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_ACCESSORIES);
|
||||
writeAccessories(part.accessories, accs_array);
|
||||
}
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mass::bulletLauncherAttachmentStyle() -> BulletLauncherAttachmentStyle& {
|
||||
return _armour.blAttachmentStyle;
|
||||
}
|
||||
|
||||
auto Mass::bulletLauncherAttachments() -> Containers::ArrayView<BulletLauncherAttachment> {
|
||||
return _armour.blAttachment;
|
||||
}
|
||||
|
||||
void Mass::getBulletLauncherAttachments() {
|
||||
LOG_INFO("Getting the bullet launcher attachment data.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto attach_style_prop = unit_data->at<ByteProperty>(MASS_BL_ATTACHMENT_STYLE);
|
||||
auto attach_array = unit_data->at<ArrayProperty>(MASS_BL_ATTACHMENTS);
|
||||
|
||||
if(!attach_style_prop && !attach_array) {
|
||||
LOG_WARNING_FORMAT("No bullet launcher attachment data found in {}.", _filename);
|
||||
_armour.blAttachmentStyle = BulletLauncherAttachmentStyle::NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
if(attach_style_prop && !attach_array) {
|
||||
LOG_WARNING_FORMAT("No bullet launcher attachments found in {}.", _filename);
|
||||
_armour.blAttachmentStyle = BulletLauncherAttachmentStyle::NotFound;
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(attach_array->items.size() == _weapons.bulletLaunchers.size() &&
|
||||
attach_array->items.size() == _armour.blAttachment.size())
|
||||
{
|
||||
for(UnsignedInt i = 0; i < attach_array->items.size(); i++) {
|
||||
auto attachment_prop = attach_array->at<GenericStructProperty>(i);
|
||||
auto& attachment = _armour.blAttachment[i];
|
||||
|
||||
Containers::StringView socket = attachment_prop->at<StringProperty>(MASS_BL_ATTACHMENT_SOCKET)->value;
|
||||
#define c(enumerator, strenum, name) if(socket == (strenum)) { attachment.socket = BulletLauncherSocket::enumerator; } else
|
||||
#include "../Maps/BulletLauncherSockets.hpp"
|
||||
#undef c
|
||||
{
|
||||
LOG_ERROR_FORMAT("Invalid attachment socket {}.", socket);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto rel_loc_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_RELLOC);
|
||||
attachment.relativeLocation = Vector3{rel_loc_prop->x, rel_loc_prop->y, rel_loc_prop->z};
|
||||
auto off_loc_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_OFFLOC);
|
||||
attachment.offsetLocation = Vector3{off_loc_prop->x, off_loc_prop->y, off_loc_prop->z};
|
||||
auto rel_rot_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_RELROT);
|
||||
attachment.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z};
|
||||
auto off_rot_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_OFFROT);
|
||||
attachment.offsetRotation = Vector3{off_rot_prop->x, off_rot_prop->y, off_rot_prop->z};
|
||||
auto rel_scale_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_RELSCALE);
|
||||
attachment.relativeScale = Vector3{rel_scale_prop->x, rel_scale_prop->y, rel_scale_prop->z};
|
||||
}
|
||||
}
|
||||
|
||||
if(attach_style_prop) {
|
||||
Containers::StringView attach_style = attach_style_prop->enumValue;
|
||||
#define c(enumerator, strenum) if(attach_style == (strenum)) { _armour.blAttachmentStyle = BulletLauncherAttachmentStyle::enumerator; } else
|
||||
#include "../Maps/BulletLauncherAttachmentStyles.hpp"
|
||||
#undef c
|
||||
{
|
||||
LOG_ERROR_FORMAT("Invalid attachment style {}.", attach_style);
|
||||
_state = State::Invalid;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_armour.blAttachmentStyle = BulletLauncherAttachmentStyle::ActiveOne;
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::writeBulletLauncherAttachments() -> bool {
|
||||
LOG_INFO("Writing bullet launcher attachments.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data in " + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto attach_style_prop = unit_data->at<ByteProperty>(MASS_BL_ATTACHMENT_STYLE);
|
||||
auto attach_array = unit_data->at<ArrayProperty>(MASS_BL_ATTACHMENTS);
|
||||
|
||||
if(!attach_style_prop && !attach_array) {
|
||||
_lastError = "No attachment properties to write to in " + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_armour.blAttachmentStyle = BulletLauncherAttachmentStyle::NotFound;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(attach_style_prop && !attach_array) {
|
||||
_lastError = "Couldn't find the attachments in " + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_armour.blAttachmentStyle = BulletLauncherAttachmentStyle::NotFound;
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(attach_array->items.size() == _weapons.bulletLaunchers.size() &&
|
||||
attach_array->items.size() == _armour.blAttachment.size())
|
||||
{
|
||||
for(UnsignedInt i = 0; i < attach_array->items.size(); i++) {
|
||||
auto attachment_prop = attach_array->at<GenericStructProperty>(i);
|
||||
auto& attachment = _armour.blAttachment[i];
|
||||
|
||||
auto& socket = attachment_prop->at<StringProperty>(MASS_BL_ATTACHMENT_SOCKET)->value;
|
||||
switch(attachment.socket) {
|
||||
#define c(enumerator, strenum, name) case BulletLauncherSocket::enumerator: socket = strenum; break;
|
||||
#include "../Maps/BulletLauncherSockets.hpp"
|
||||
#undef c
|
||||
default:
|
||||
_lastError = "Invalid socket type."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto rel_loc_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_RELLOC);
|
||||
rel_loc_prop->x = attachment.relativeLocation.x();
|
||||
rel_loc_prop->y = attachment.relativeLocation.y();
|
||||
rel_loc_prop->z = attachment.relativeLocation.z();
|
||||
auto off_loc_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_OFFLOC);
|
||||
off_loc_prop->x = attachment.offsetLocation.x();
|
||||
off_loc_prop->y = attachment.offsetLocation.y();
|
||||
off_loc_prop->z = attachment.offsetLocation.z();
|
||||
auto rel_rot_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_RELROT);
|
||||
rel_rot_prop->x = attachment.relativeRotation.x();
|
||||
rel_rot_prop->y = attachment.relativeRotation.y();
|
||||
rel_rot_prop->z = attachment.relativeRotation.z();
|
||||
auto off_rot_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_OFFROT);
|
||||
off_rot_prop->x = attachment.offsetRotation.x();
|
||||
off_rot_prop->y = attachment.offsetRotation.y();
|
||||
off_rot_prop->z = attachment.offsetRotation.z();
|
||||
auto rel_scale_prop = attachment_prop->at<VectorStructProperty>(MASS_BL_ATTACHMENT_RELSCALE);
|
||||
rel_scale_prop->x = attachment.relativeScale.x();
|
||||
rel_scale_prop->y = attachment.relativeScale.y();
|
||||
rel_scale_prop->z = attachment.relativeScale.z();
|
||||
}
|
||||
}
|
||||
|
||||
if(!attach_style_prop) {
|
||||
attach_style_prop = new ByteProperty;
|
||||
attach_style_prop->name.emplace(MASS_BL_ATTACHMENT_STYLE);
|
||||
attach_style_prop->enumType = "enuBLAttachmentStyle"_s;
|
||||
ByteProperty::ptr prop{attach_style_prop};
|
||||
arrayAppend(unit_data->properties, std::move(prop));
|
||||
}
|
||||
|
||||
auto& attach_style = attach_style_prop->enumValue;
|
||||
switch(_armour.blAttachmentStyle) {
|
||||
#define c(enumerator, strenum) case BulletLauncherAttachmentStyle::enumerator: \
|
||||
attach_style = strenum; \
|
||||
break;
|
||||
#include "../Maps/BulletLauncherAttachmentStyles.hpp"
|
||||
#undef c
|
||||
default:
|
||||
_lastError = "Unknown BL attachment style.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mass::armourCustomStyles() -> Containers::ArrayView<CustomStyle> {
|
||||
return _armour.customStyles;
|
||||
}
|
||||
|
||||
void Mass::getArmourCustomStyles() {
|
||||
LOG_INFO("Getting the custom armour styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto armour_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_ARMOUR_STYLES);
|
||||
if(!armour_styles) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_CUSTOM_ARMOUR_STYLES, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(armour_styles->items.size() != _armour.customStyles.size()) {
|
||||
LOG_ERROR_FORMAT("Custom armour style arrays are not of the same size. Expected {}, got {} instead.",
|
||||
_armour.customStyles.size(), armour_styles->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
getCustomStyles(_armour.customStyles, armour_styles);
|
||||
}
|
||||
|
||||
auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool {
|
||||
LOG_INFO_FORMAT("Writing custom armour style {}.", index);
|
||||
|
||||
if(index > _armour.customStyles.size()) {
|
||||
_lastError = "Style index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "Couldn't find unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto armour_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_ARMOUR_STYLES);
|
||||
if(!armour_styles) {
|
||||
_lastError = "Couldn't find armour custom styles in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
return writeCustomStyle(_armour.customStyles[index], index, armour_styles);
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/BoolProperty.h"
|
||||
#include "../UESaveFile/Types/ColourStructProperty.h"
|
||||
#include "../UESaveFile/Types/FloatProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/RotatorStructProperty.h"
|
||||
#include "../UESaveFile/Types/VectorStructProperty.h"
|
||||
#include "../UESaveFile/Types/Vector2DStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
void Mass::getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array) {
|
||||
for(UnsignedInt i = 0; i < decal_array->items.size(); i++) {
|
||||
auto decal_prop = decal_array->at<GenericStructProperty>(i);
|
||||
CORRADE_INTERNAL_ASSERT(decal_prop);
|
||||
auto& decal = decals[i];
|
||||
|
||||
decal.id = decal_prop->at<IntProperty>(MASS_DECAL_ID)->value;
|
||||
auto colour_prop = decal_prop->at<ColourStructProperty>(MASS_DECAL_COLOUR);
|
||||
decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a};
|
||||
auto pos_prop = decal_prop->at<VectorStructProperty>(MASS_DECAL_POSITION);
|
||||
decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z};
|
||||
auto u_prop = decal_prop->at<VectorStructProperty>(MASS_DECAL_UAXIS);
|
||||
decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z};
|
||||
auto v_prop = decal_prop->at<VectorStructProperty>(MASS_DECAL_VAXIS);
|
||||
decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z};
|
||||
auto offset_prop = decal_prop->at<Vector2DStructProperty>(MASS_DECAL_OFFSET);
|
||||
decal.offset = Vector2{offset_prop->x, offset_prop->y};
|
||||
decal.scale = decal_prop->at<FloatProperty>(MASS_DECAL_SCALE)->value;
|
||||
decal.rotation = decal_prop->at<FloatProperty>(MASS_DECAL_ROTATION)->value;
|
||||
decal.flip = decal_prop->at<BoolProperty>(MASS_DECAL_FLIP)->value;
|
||||
decal.wrap = decal_prop->at<BoolProperty>(MASS_DECAL_WRAP)->value;
|
||||
}
|
||||
}
|
||||
|
||||
void Mass::writeDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array) {
|
||||
for(UnsignedInt i = 0; i < decal_array->items.size(); i++) {
|
||||
auto decal_prop = decal_array->at<GenericStructProperty>(i);
|
||||
CORRADE_INTERNAL_ASSERT(decal_prop);
|
||||
auto& decal = decals[i];
|
||||
|
||||
decal_prop->at<IntProperty>(MASS_DECAL_ID)->value = decal.id;
|
||||
auto colour_prop = decal_prop->at<ColourStructProperty>(MASS_DECAL_COLOUR);
|
||||
colour_prop->r = decal.colour.r();
|
||||
colour_prop->g = decal.colour.g();
|
||||
colour_prop->b = decal.colour.b();
|
||||
colour_prop->a = decal.colour.a();
|
||||
auto pos_prop = decal_prop->at<VectorStructProperty>(MASS_DECAL_POSITION);
|
||||
pos_prop->x = decal.position.x();
|
||||
pos_prop->y = decal.position.y();
|
||||
pos_prop->z = decal.position.z();
|
||||
auto u_prop = decal_prop->at<VectorStructProperty>(MASS_DECAL_UAXIS);
|
||||
u_prop->x = decal.uAxis.x();
|
||||
u_prop->y = decal.uAxis.y();
|
||||
u_prop->z = decal.uAxis.z();
|
||||
auto v_prop = decal_prop->at<VectorStructProperty>(MASS_DECAL_VAXIS);
|
||||
v_prop->x = decal.vAxis.x();
|
||||
v_prop->y = decal.vAxis.y();
|
||||
v_prop->z = decal.vAxis.z();
|
||||
auto offset_prop = decal_prop->at<Vector2DStructProperty>(MASS_DECAL_OFFSET);
|
||||
offset_prop->x = decal.offset.x();
|
||||
offset_prop->y = decal.offset.y();
|
||||
decal_prop->at<FloatProperty>(MASS_DECAL_SCALE)->value = decal.scale;
|
||||
decal_prop->at<FloatProperty>(MASS_DECAL_ROTATION)->value = decal.rotation;
|
||||
decal_prop->at<BoolProperty>(MASS_DECAL_FLIP)->value = decal.flip;
|
||||
decal_prop->at<BoolProperty>(MASS_DECAL_WRAP)->value = decal.wrap;
|
||||
}
|
||||
}
|
||||
|
||||
void Mass::getAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accessory_array) {
|
||||
for(UnsignedInt i = 0; i < accessory_array->items.size(); i++) {
|
||||
auto acc_prop = accessory_array->at<GenericStructProperty>(i);
|
||||
CORRADE_INTERNAL_ASSERT(acc_prop);
|
||||
auto& accessory = accessories[i];
|
||||
|
||||
accessory.attachIndex = acc_prop->at<IntProperty>(MASS_ACCESSORY_ATTACH_INDEX)->value;
|
||||
accessory.id = acc_prop->at<IntProperty>(MASS_ACCESSORY_ID)->value;
|
||||
auto acc_styles = acc_prop->at<ArrayProperty>(MASS_ACCESSORY_STYLES);
|
||||
for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) {
|
||||
accessory.styles[j] = acc_styles->at<IntProperty>(j)->value;
|
||||
}
|
||||
auto rel_pos_prop = acc_prop->at<VectorStructProperty>(MASS_ACCESSORY_RELPOS);
|
||||
accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z};
|
||||
auto rel_pos_offset_prop = acc_prop->at<VectorStructProperty>(MASS_ACCESSORY_OFFPOS);
|
||||
accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z};
|
||||
auto rel_rot_prop = acc_prop->at<RotatorStructProperty>(MASS_ACCESSORY_RELROT);
|
||||
accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z};
|
||||
auto rel_rot_offset_prop = acc_prop->at<RotatorStructProperty>(MASS_ACCESSORY_OFFROT);
|
||||
accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z};
|
||||
auto local_scale_prop = acc_prop->at<VectorStructProperty>(MASS_ACCESSORY_SCALE);
|
||||
accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z};
|
||||
}
|
||||
}
|
||||
|
||||
void Mass::writeAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accs_array) {
|
||||
for(UnsignedInt i = 0; i < accs_array->items.size(); i++) {
|
||||
auto acc_prop = accs_array->at<GenericStructProperty>(i);
|
||||
CORRADE_INTERNAL_ASSERT(acc_prop);
|
||||
auto& accessory = accessories[i];
|
||||
|
||||
acc_prop->at<IntProperty>(MASS_ACCESSORY_ATTACH_INDEX)->value = accessory.attachIndex;
|
||||
acc_prop->at<IntProperty>(MASS_ACCESSORY_ID)->value = accessory.id;
|
||||
auto acc_styles = acc_prop->at<ArrayProperty>(MASS_ACCESSORY_STYLES);
|
||||
for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) {
|
||||
acc_styles->at<IntProperty>(j)->value = accessory.styles[j];
|
||||
}
|
||||
auto rel_pos_prop = acc_prop->at<VectorStructProperty>(MASS_ACCESSORY_RELPOS);
|
||||
rel_pos_prop->x = accessory.relativePosition.x();
|
||||
rel_pos_prop->y = accessory.relativePosition.y();
|
||||
rel_pos_prop->z = accessory.relativePosition.z();
|
||||
auto rel_pos_offset_prop = acc_prop->at<VectorStructProperty>(MASS_ACCESSORY_OFFPOS);
|
||||
rel_pos_offset_prop->x = accessory.relativePositionOffset.x();
|
||||
rel_pos_offset_prop->y = accessory.relativePositionOffset.y();
|
||||
rel_pos_offset_prop->z = accessory.relativePositionOffset.z();
|
||||
auto rel_rot_prop = acc_prop->at<RotatorStructProperty>(MASS_ACCESSORY_RELROT);
|
||||
rel_rot_prop->x = accessory.relativeRotation.x();
|
||||
rel_rot_prop->y = accessory.relativeRotation.y();
|
||||
rel_rot_prop->z = accessory.relativeRotation.z();
|
||||
auto rel_rot_offset_prop = acc_prop->at<RotatorStructProperty>(MASS_ACCESSORY_OFFROT);
|
||||
rel_rot_offset_prop->x = accessory.relativeRotationOffset.x();
|
||||
rel_rot_offset_prop->y = accessory.relativeRotationOffset.y();
|
||||
rel_rot_offset_prop->z = accessory.relativeRotationOffset.z();
|
||||
auto local_scale_prop = acc_prop->at<VectorStructProperty>(MASS_ACCESSORY_SCALE);
|
||||
local_scale_prop->x = accessory.localScale.x();
|
||||
local_scale_prop->y = accessory.localScale.y();
|
||||
local_scale_prop->z = accessory.localScale.z();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/ColourStructProperty.h"
|
||||
#include "../UESaveFile/Types/FloatProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
auto Mass::jointSliders() -> Joints& {
|
||||
return _frame.joints;
|
||||
}
|
||||
|
||||
void Mass::getJointSliders() {
|
||||
LOG_INFO("Getting joint sliders.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto length = frame_prop->at<FloatProperty>(MASS_JOINT_NECK);
|
||||
_frame.joints.neck = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_BODY);
|
||||
_frame.joints.body = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_SHOULDER);
|
||||
_frame.joints.shoulders = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_HIP);
|
||||
_frame.joints.hips = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_ARM_UPPER);
|
||||
_frame.joints.upperArms = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_ARM_LOWER);
|
||||
_frame.joints.lowerArms = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_LEG_UPPER);
|
||||
_frame.joints.upperLegs = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_LEG_LOWER);
|
||||
_frame.joints.lowerLegs = (length ? length->value : 0.0f);
|
||||
}
|
||||
|
||||
auto Mass::writeJointSliders() -> bool {
|
||||
LOG_INFO("Writing joint sliders");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
|
||||
if(!frame_prop) {
|
||||
_lastError = "No frame data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
Containers::Array<UnrealPropertyBase::ptr> temp;
|
||||
|
||||
auto length = frame_prop->atMove<FloatProperty>(MASS_JOINT_NECK);
|
||||
if(_frame.joints.neck != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_NECK);
|
||||
}
|
||||
length->value = _frame.joints.neck;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_BODY);
|
||||
if(_frame.joints.body != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_BODY);
|
||||
}
|
||||
length->value = _frame.joints.body;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_SHOULDER);
|
||||
if(_frame.joints.shoulders != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_SHOULDER);
|
||||
}
|
||||
length->value = _frame.joints.shoulders;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_ARM_UPPER);
|
||||
if(_frame.joints.upperArms != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_ARM_UPPER);
|
||||
}
|
||||
length->value = _frame.joints.upperArms;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_ARM_LOWER);
|
||||
if(_frame.joints.lowerArms != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_ARM_LOWER);
|
||||
}
|
||||
length->value = _frame.joints.lowerArms;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_HIP);
|
||||
if(_frame.joints.hips != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_HIP);
|
||||
}
|
||||
length->value = _frame.joints.hips;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_LEG_UPPER);
|
||||
if(_frame.joints.upperLegs != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_LEG_UPPER);
|
||||
}
|
||||
length->value = _frame.joints.upperLegs;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_LEG_LOWER);
|
||||
if(_frame.joints.lowerLegs != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace(MASS_JOINT_LEG_LOWER);
|
||||
}
|
||||
length->value = _frame.joints.lowerLegs;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 3]));
|
||||
arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 2]));
|
||||
arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 1]));
|
||||
|
||||
frame_prop->properties = std::move(temp);
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mass::frameStyles() -> Containers::ArrayView<Int> {
|
||||
return _frame.styles;
|
||||
}
|
||||
|
||||
void Mass::getFrameStyles() {
|
||||
LOG_INFO("Getting frame styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_styles = frame_prop->at<ArrayProperty>(MASS_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME_STYLES, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(frame_styles->items.size() != _frame.styles.size()) {
|
||||
LOG_ERROR_FORMAT("Frame style arrays are not of the same size. Expected {}, got {} instead.",
|
||||
_frame.styles.size(), frame_styles->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) {
|
||||
_frame.styles[i] = frame_styles->at<IntProperty>(i)->value;
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::writeFrameStyles() -> bool {
|
||||
LOG_INFO("Writing frame styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame) {
|
||||
_lastError = "No frame data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame_styles = frame->at<ArrayProperty>(MASS_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
_lastError = "No frame styles in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) {
|
||||
frame_styles->at<IntProperty>(i)->value = _frame.styles[i];
|
||||
}
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mass::eyeFlareColour() -> Color4& {
|
||||
return _frame.eyeFlare;
|
||||
}
|
||||
|
||||
void Mass::getEyeFlareColour() {
|
||||
LOG_INFO("Getting the eye flare colour.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto eye_flare_prop = frame_prop->at<ColourStructProperty>(MASS_EYE_FLARE);
|
||||
if(!eye_flare_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_EYE_FLARE, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
_frame.eyeFlare = Color4{eye_flare_prop->r, eye_flare_prop->g, eye_flare_prop->b, eye_flare_prop->a};
|
||||
}
|
||||
|
||||
auto Mass::writeEyeFlareColour() -> bool {
|
||||
LOG_INFO("Writing the eye flare colour.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame) {
|
||||
_lastError = "No frame data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto eye_flare_prop = frame->at<ColourStructProperty>(MASS_EYE_FLARE);
|
||||
if(!eye_flare_prop) {
|
||||
_lastError = "No eye flare property in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
eye_flare_prop->r = _frame.eyeFlare.r();
|
||||
eye_flare_prop->g = _frame.eyeFlare.g();
|
||||
eye_flare_prop->b = _frame.eyeFlare.b();
|
||||
eye_flare_prop->a = _frame.eyeFlare.a();
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Mass::frameCustomStyles() -> Containers::ArrayView<CustomStyle> {
|
||||
return _frame.customStyles;
|
||||
}
|
||||
|
||||
void Mass::getFrameCustomStyles() {
|
||||
LOG_INFO("Getting the frame's custom styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_CUSTOM_FRAME_STYLES, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(frame_styles->items.size() != _frame.customStyles.size()) {
|
||||
LOG_ERROR_FORMAT("Frame custom style arrays are not of the same size. Expected {}, got {} instead.",
|
||||
_frame.customStyles.size(), frame_styles->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
getCustomStyles(_frame.customStyles, frame_styles);
|
||||
}
|
||||
|
||||
auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool {
|
||||
LOG_INFO_FORMAT("Writing frame custom style number {}.", index);
|
||||
|
||||
if(index > _frame.customStyles.size()) {
|
||||
_lastError = "Style index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
_lastError = "No frame styles in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
return writeCustomStyle(_frame.customStyles[index], index, frame_styles);
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/ColourStructProperty.h"
|
||||
#include "../UESaveFile/Types/FloatProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
#include "../UESaveFile/Types/StringProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
auto Mass::globalStyles() -> Containers::ArrayView<CustomStyle> {
|
||||
return _globalStyles;
|
||||
}
|
||||
|
||||
void Mass::getGlobalStyles() {
|
||||
LOG_INFO("Getting global styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto global_styles = unit_data->at<ArrayProperty>(MASS_GLOBAL_STYLES);
|
||||
if(!global_styles) {
|
||||
LOG_WARNING_FORMAT("Couldn't find global styles in {}.", _filename);
|
||||
_globalStyles = Containers::Array<CustomStyle>{0};
|
||||
return;
|
||||
}
|
||||
|
||||
if(global_styles->items.size() != _globalStyles.size()) {
|
||||
_globalStyles = Containers::Array<CustomStyle>{global_styles->items.size()};
|
||||
}
|
||||
|
||||
getCustomStyles(_globalStyles, global_styles);
|
||||
}
|
||||
|
||||
auto Mass::writeGlobalStyle(UnsignedLong index) -> bool {
|
||||
LOG_INFO_FORMAT("Writing global style number {}.", index);
|
||||
|
||||
if(index > _globalStyles.size()) {
|
||||
_lastError = "Global style index out of range"_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data found in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto global_styles = unit_data->at<ArrayProperty>(MASS_GLOBAL_STYLES);
|
||||
if(!global_styles) {
|
||||
_lastError = "No global styles found in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
return writeCustomStyle(_globalStyles[index], index, global_styles);
|
||||
}
|
||||
|
||||
void Mass::getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayProperty* style_array) {
|
||||
for(UnsignedInt i = 0; i < style_array->items.size(); i++) {
|
||||
auto style_prop = style_array->at<GenericStructProperty>(i);
|
||||
auto& style = styles[i];
|
||||
|
||||
style.name = style_prop->at<StringProperty>(MASS_STYLE_NAME)->value;
|
||||
auto colour_prop = style_prop->at<ColourStructProperty>(MASS_STYLE_COLOUR);
|
||||
style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a};
|
||||
style.metallic = style_prop->at<FloatProperty>(MASS_STYLE_METALLIC)->value;
|
||||
style.gloss = style_prop->at<FloatProperty>(MASS_STYLE_GLOSS)->value;
|
||||
style.glow = colour_prop->a != 0.0f;
|
||||
|
||||
style.patternId = style_prop->at<IntProperty>(MASS_STYLE_PATTERN_ID)->value;
|
||||
style.opacity = style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OPACITY)->value;
|
||||
style.offset = Vector2{
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OFFSETX)->value,
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OFFSETY)->value
|
||||
};
|
||||
style.rotation = style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_ROTATION)->value;
|
||||
style.scale = style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_SCALE)->value;
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::writeCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool {
|
||||
if(!style_array) {
|
||||
_lastError = "style_array is null."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto style_prop = style_array->at<GenericStructProperty>(index);
|
||||
if(!style_prop) {
|
||||
_lastError = "Style index is out of range in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
style_prop->at<StringProperty>(MASS_STYLE_NAME)->value = style.name;
|
||||
auto colour_prop = style_prop->at<ColourStructProperty>(MASS_STYLE_COLOUR);
|
||||
colour_prop->r = style.colour.r();
|
||||
colour_prop->g = style.colour.g();
|
||||
colour_prop->b = style.colour.b();
|
||||
colour_prop->a = style.glow ? 1.0f : 0.0f;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_METALLIC)->value = style.metallic;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_GLOSS)->value = style.gloss;
|
||||
|
||||
style_prop->at<IntProperty>(MASS_STYLE_PATTERN_ID)->value = style.patternId;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OPACITY)->value = style.opacity;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OFFSETX)->value = style.offset.x();
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OFFSETY)->value = style.offset.y();
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_ROTATION)->value = style.rotation;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_SCALE)->value = style.scale;
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,360 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/BoolProperty.h"
|
||||
#include "../UESaveFile/Types/ByteProperty.h"
|
||||
#include "../UESaveFile/Types/ColourStructProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
#include "../UESaveFile/Types/StringProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
auto Mass::meleeWeapons() -> Containers::ArrayView<Weapon> {
|
||||
return _weapons.melee;
|
||||
}
|
||||
|
||||
void Mass::getMeleeWeapons() {
|
||||
LOG_INFO("Getting melee weapons.");
|
||||
getWeaponType(MASS_WEAPONS_MELEE, _weapons.melee);
|
||||
}
|
||||
|
||||
auto Mass::writeMeleeWeapons() -> bool {
|
||||
LOG_INFO("Writing melee weapons.");
|
||||
return writeWeaponType(MASS_WEAPONS_MELEE, _weapons.melee);
|
||||
}
|
||||
|
||||
auto Mass::shields() -> Containers::ArrayView<Weapon> {
|
||||
return _weapons.shields;
|
||||
}
|
||||
|
||||
void Mass::getShields() {
|
||||
LOG_INFO("Getting shields.");
|
||||
getWeaponType(MASS_WEAPONS_SHIELD, _weapons.shields);
|
||||
}
|
||||
|
||||
auto Mass::writeShields() -> bool {
|
||||
LOG_INFO("Writing shields.");
|
||||
return writeWeaponType(MASS_WEAPONS_SHIELD, _weapons.shields);
|
||||
}
|
||||
|
||||
auto Mass::bulletShooters() -> Containers::ArrayView<Weapon> {
|
||||
return _weapons.bulletShooters;
|
||||
}
|
||||
|
||||
void Mass::getBulletShooters() {
|
||||
LOG_INFO("Getting bullet shooters.");
|
||||
getWeaponType(MASS_WEAPONS_BSHOOTER, _weapons.bulletShooters);
|
||||
}
|
||||
|
||||
auto Mass::writeBulletShooters() -> bool {
|
||||
LOG_INFO("Writing bullet shooters.");
|
||||
return writeWeaponType(MASS_WEAPONS_BSHOOTER, _weapons.bulletShooters);
|
||||
}
|
||||
|
||||
auto Mass::energyShooters() -> Containers::ArrayView<Weapon> {
|
||||
return _weapons.energyShooters;
|
||||
}
|
||||
|
||||
void Mass::getEnergyShooters() {
|
||||
LOG_INFO("Getting energy shooters.");
|
||||
getWeaponType(MASS_WEAPONS_ESHOOTER, _weapons.energyShooters);
|
||||
}
|
||||
|
||||
auto Mass::writeEnergyShooters() -> bool {
|
||||
LOG_INFO("Writing energy shooters.");
|
||||
return writeWeaponType(MASS_WEAPONS_ESHOOTER, _weapons.energyShooters);
|
||||
}
|
||||
|
||||
auto Mass::bulletLaunchers() -> Containers::ArrayView<Weapon> {
|
||||
return _weapons.bulletLaunchers;
|
||||
}
|
||||
|
||||
void Mass::getBulletLaunchers() {
|
||||
LOG_INFO("Getting bullet launchers.");
|
||||
getWeaponType(MASS_WEAPONS_BLAUNCHER, _weapons.bulletLaunchers);
|
||||
}
|
||||
|
||||
auto Mass::writeBulletLaunchers() -> bool {
|
||||
LOG_INFO("Writing bullet launchers.");
|
||||
return writeWeaponType(MASS_WEAPONS_BLAUNCHER, _weapons.bulletLaunchers);
|
||||
}
|
||||
|
||||
auto Mass::energyLaunchers() -> Containers::ArrayView<Weapon> {
|
||||
return _weapons.energyLaunchers;
|
||||
}
|
||||
|
||||
void Mass::getEnergyLaunchers() {
|
||||
LOG_INFO("Getting energy launchers.");
|
||||
getWeaponType(MASS_WEAPONS_ELAUNCHER, _weapons.energyLaunchers);
|
||||
}
|
||||
|
||||
auto Mass::writeEnergyLaunchers() -> bool {
|
||||
LOG_INFO("Writing energy launchers.");
|
||||
return writeWeaponType(MASS_WEAPONS_ELAUNCHER, _weapons.energyLaunchers);
|
||||
}
|
||||
|
||||
void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView<Weapon> weapon_array) {
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto prop = unit_data->at<ArrayProperty>(prop_name);
|
||||
if(!prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", prop_name, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(prop->items.size() != weapon_array.size()) {
|
||||
LOG_ERROR_FORMAT("Weapon arrays are not of the same size. Expected {}, got {} instead.",
|
||||
weapon_array.size(), prop->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
for(UnsignedInt i = 0; i < weapon_array.size(); i++) {
|
||||
auto weapon_prop = prop->at<GenericStructProperty>(i);
|
||||
auto& weapon = weapon_array[i];
|
||||
|
||||
weapon.name = weapon_prop->at<StringProperty>(MASS_WEAPON_NAME)->value;
|
||||
auto& weapon_type = weapon_prop->at<ByteProperty>(MASS_WEAPON_TYPE)->enumValue;
|
||||
#define c(enumerator, strenum, name) if(weapon_type == (strenum)) { weapon.type = WeaponType::enumerator; } else
|
||||
#include "../Maps/WeaponTypes.hpp"
|
||||
#undef c
|
||||
{
|
||||
LOG_ERROR_FORMAT("Invalid weapon type {} in {}.", weapon_type, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto parts_prop = weapon_prop->at<ArrayProperty>(MASS_WEAPON_ELEMENT);
|
||||
weapon.parts = Containers::Array<WeaponPart>{ValueInit, parts_prop->items.size()};
|
||||
|
||||
for(UnsignedInt j = 0; j < parts_prop->items.size(); j++) {
|
||||
auto part_prop = parts_prop->at<GenericStructProperty>(j);
|
||||
auto& part = weapon.parts[j];
|
||||
|
||||
part.id = part_prop->at<IntProperty>(MASS_WEAPON_PART_ID)->value;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_STYLES);
|
||||
for(UnsignedInt k = 0; k < part_styles->items.size(); k++) {
|
||||
part.styles[k] = part_styles->at<IntProperty>(k)->value;
|
||||
}
|
||||
|
||||
auto part_decals = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_DECALS);
|
||||
if(part_decals->items.size() != part.decals.size()) {
|
||||
part.decals = Containers::Array<Decal>{part_decals->items.size()};
|
||||
}
|
||||
|
||||
getDecals(part.decals, part_decals);
|
||||
|
||||
auto part_accs = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_ACCESSORIES);
|
||||
if(!part_accs) {
|
||||
part.accessories = Containers::Array<Accessory>{0};
|
||||
continue;
|
||||
}
|
||||
|
||||
if(part_accs->items.size() != part.accessories.size()) {
|
||||
part.accessories = Containers::Array<Accessory>{part_accs->items.size()};
|
||||
}
|
||||
getAccessories(part.accessories, part_accs);
|
||||
}
|
||||
|
||||
auto custom_styles = weapon_prop->at<ArrayProperty>(MASS_CUSTOM_WEAPON_STYLES);
|
||||
if(!custom_styles) {
|
||||
LOG_ERROR_FORMAT("Can't find weapon custom styles in {}", _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(custom_styles->items.size() != weapon.customStyles.size()) {
|
||||
LOG_ERROR_FORMAT("Custom weapon style arrays are not of the same size. Expected {}, got {} instead.",
|
||||
weapon.customStyles.size(), custom_styles->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
getCustomStyles(weapon.customStyles, custom_styles);
|
||||
|
||||
weapon.attached = weapon_prop->at<BoolProperty>(MASS_WEAPON_ATTACH)->value;
|
||||
auto& damage_type = weapon_prop->at<ByteProperty>(MASS_WEAPON_DAMAGE_TYPE)->enumValue;
|
||||
#define c(enumerator, strenum) if(damage_type == (strenum)) { weapon.damageType = DamageType::enumerator; } else
|
||||
#include "../Maps/DamageTypes.hpp"
|
||||
#undef c
|
||||
{
|
||||
LOG_ERROR_FORMAT("Invalid damage type {} in {}.", damage_type, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
weapon.dualWield = weapon_prop->at<BoolProperty>(MASS_WEAPON_DUAL_WIELD)->value;
|
||||
auto& effect_colour_mode = weapon_prop->at<ByteProperty>(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue;
|
||||
#define c(enumerator, strenum) if(effect_colour_mode == (strenum)) { weapon.effectColourMode = EffectColourMode::enumerator; } else
|
||||
#include "../Maps/EffectColourModes.hpp"
|
||||
#undef c
|
||||
{
|
||||
LOG_ERROR_FORMAT("Invalid effect colour mode {} in {}.", effect_colour_mode, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
auto effect_colour = weapon_prop->at<ColourStructProperty>(MASS_WEAPON_COLOUR_EFX);
|
||||
weapon.effectColour = Color4{effect_colour->r, effect_colour->g, effect_colour->b, effect_colour->a};
|
||||
}
|
||||
}
|
||||
|
||||
auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayView<Weapon> weapon_array) -> bool {
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto prop = unit_data->at<ArrayProperty>(prop_name);
|
||||
if(!prop) {
|
||||
_lastError = prop_name + " not found in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(prop->items.size() != weapon_array.size()) {
|
||||
_lastError = Utility::format("Weapon arrays are not of the same size. Expected {}, got {} instead.",
|
||||
weapon_array.size(), prop->items.size());
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
for(UnsignedInt i = 0; i < weapon_array.size(); i++) {
|
||||
auto weapon_prop = prop->at<GenericStructProperty>(i);
|
||||
auto& weapon = weapon_array[i];
|
||||
|
||||
weapon_prop->at<StringProperty>(MASS_WEAPON_NAME)->value = weapon.name;
|
||||
switch(weapon.type) {
|
||||
#define c(enumerator, strenum, name) case WeaponType::enumerator: weapon_prop->at<ByteProperty>(MASS_WEAPON_TYPE)->enumValue = strenum; break;
|
||||
#include "../Maps/WeaponTypes.hpp"
|
||||
#undef c
|
||||
default:
|
||||
_lastError = Utility::format("Invalid weapon type at index {}.", i);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto parts_prop = weapon_prop->at<ArrayProperty>(MASS_WEAPON_ELEMENT);
|
||||
if(parts_prop->items.size() != weapon.parts.size()) {
|
||||
_lastError = Utility::format("Weapon part arrays are not of the same size. Expected {}, got {} instead.",
|
||||
weapon.parts.size(), parts_prop->items.size());
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
for(UnsignedInt j = 0; j < parts_prop->items.size(); j++) {
|
||||
auto part_prop = parts_prop->at<GenericStructProperty>(j);
|
||||
auto& part = weapon.parts[j];
|
||||
|
||||
part_prop->at<IntProperty>(MASS_WEAPON_PART_ID)->value = part.id;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_STYLES);
|
||||
for(UnsignedInt k = 0; k < part_styles->items.size(); k++) {
|
||||
part_styles->at<IntProperty>(k)->value = part.styles[k];
|
||||
}
|
||||
|
||||
auto part_decals = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_DECALS);
|
||||
writeDecals(part.decals, part_decals);
|
||||
|
||||
auto part_accs = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_ACCESSORIES);
|
||||
if(!part_accs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(part_accs->items.size() != part.accessories.size()) {
|
||||
_lastError = Utility::format("Part accessory arrays are not of the same size. Expected {}, got {} instead.",
|
||||
part.accessories.size(), part_accs->items.size());
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
writeAccessories(part.accessories, part_accs);
|
||||
}
|
||||
|
||||
auto custom_styles = weapon_prop->at<ArrayProperty>(MASS_CUSTOM_WEAPON_STYLES);
|
||||
if(!custom_styles) {
|
||||
_lastError = "No custom styles found for weapon."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(custom_styles->items.size() != weapon.customStyles.size()) {
|
||||
_lastError = Utility::format("Custom style arrays are not of the same size. Expected {}, got {} instead.",
|
||||
weapon.customStyles.size(), custom_styles->items.size());
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
for(UnsignedInt j = 0; j < weapon.customStyles.size(); j++) {
|
||||
writeCustomStyle(weapon.customStyles[j], j, custom_styles);
|
||||
}
|
||||
|
||||
weapon_prop->at<BoolProperty>(MASS_WEAPON_ATTACH)->value = weapon.attached;
|
||||
switch(weapon.damageType) {
|
||||
#define c(enumerator, strenum) case DamageType::enumerator: weapon_prop->at<ByteProperty>(MASS_WEAPON_DAMAGE_TYPE)->enumValue = strenum; break;
|
||||
#include "../Maps/DamageTypes.hpp"
|
||||
#undef c
|
||||
default:
|
||||
_lastError = Utility::format("Invalid damage type at index {}.", i);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
weapon_prop->at<BoolProperty>(MASS_WEAPON_DUAL_WIELD)->value = weapon.dualWield;
|
||||
switch(weapon.effectColourMode) {
|
||||
#define c(enumerator, enumstr) case EffectColourMode::enumerator: \
|
||||
weapon_prop->at<ByteProperty>(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue = enumstr; \
|
||||
break;
|
||||
#include "../Maps/EffectColourModes.hpp"
|
||||
#undef c
|
||||
default:
|
||||
_lastError = Utility::format("Invalid damage type at index {}.", i);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
auto effect_colour = weapon_prop->at<ColourStructProperty>(MASS_WEAPON_COLOUR_EFX);
|
||||
effect_colour->r = weapon.effectColour.r();
|
||||
effect_colour->g = weapon.effectColour.g();
|
||||
effect_colour->b = weapon.effectColour.b();
|
||||
effect_colour->a = weapon.effectColour.a();
|
||||
}
|
||||
|
||||
if(!_mass->saveToFile()) {
|
||||
_lastError = _mass->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
|
||||
#define MASS_UNIT_DATA "UnitData"
|
||||
#define MASS_NAME "Name_45_A037C5D54E53456407BDF091344529BB"
|
||||
#define MASS_ACCOUNT "Account"
|
||||
#define MASS_GLOBAL_STYLES "GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"
|
||||
|
||||
// Frame stuff
|
||||
#define MASS_FRAME "Frame_3_F92B0F6A44A15088AF7F41B9FF290653"
|
||||
#define MASS_JOINT_NECK "NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"
|
||||
#define MASS_JOINT_BODY "BodyLength_7_C16287754CBA96C93BAE36A5C154996A"
|
||||
#define MASS_JOINT_SHOULDER "ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"
|
||||
#define MASS_JOINT_ARM_UPPER "ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"
|
||||
#define MASS_JOINT_ARM_LOWER "ArmLowerLength_12_ACD0F02745C28882619376926292FB36"
|
||||
#define MASS_JOINT_HIP "HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"
|
||||
#define MASS_JOINT_LEG_UPPER "LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"
|
||||
#define MASS_JOINT_LEG_LOWER "LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"
|
||||
#define MASS_FRAME_STYLES "Styles_32_00A3B3284B37F1E7819458844A20EB48"
|
||||
#define MASS_EYE_FLARE "EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"
|
||||
#define MASS_CUSTOM_FRAME_STYLES "FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"
|
||||
|
||||
// Armour stuff
|
||||
#define MASS_ARMOUR_PARTS "Armor_10_12E266C44116DDAF57E99ABB575A4B3C"
|
||||
#define MASS_ARMOUR_SLOT "Slot_3_408BA56F4C9605C7E805CF91B642249C"
|
||||
#define MASS_ARMOUR_ID "ID_5_ACD101864D3481DE96EDACACC09BDD25"
|
||||
#define MASS_ARMOUR_STYLES "Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"
|
||||
#define MASS_ARMOUR_DECALS "Decals_42_F358794A4F18497970F56BA9627D3603"
|
||||
#define MASS_ARMOUR_ACCESSORIES "Accessories_52_D902DD4241FA0050C2529596255153F3"
|
||||
#define MASS_CUSTOM_ARMOUR_STYLES "ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"
|
||||
|
||||
// Weapon stuff
|
||||
#define MASS_WEAPONS_MELEE "WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7"
|
||||
#define MASS_WEAPONS_SHIELD "Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E"
|
||||
#define MASS_WEAPONS_BSHOOTER "WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED"
|
||||
#define MASS_WEAPONS_ESHOOTER "WeaponES_37_1A295D544528623880A0B1AC2C7DEE99"
|
||||
#define MASS_WEAPONS_BLAUNCHER "WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E"
|
||||
#define MASS_WEAPONS_ELAUNCHER "WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995"
|
||||
#define MASS_WEAPON_NAME "Name_13_7BF0D31F4E50C50C47231BB36A485D92"
|
||||
#define MASS_WEAPON_TYPE "Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976"
|
||||
#define MASS_WEAPON_ELEMENT "Element_6_8E4617CC4B2C1F1490435599784EC6E0"
|
||||
#define MASS_CUSTOM_WEAPON_STYLES "Styles_10_8C3C82444B986AD7A99595AD4985912D"
|
||||
#define MASS_WEAPON_ATTACH "Attach_15_D00AABBD4AD6A04778D56D81E51927B3"
|
||||
#define MASS_WEAPON_DAMAGE_TYPE "DamageType_18_E1FFA53540591A9087EC698117A65C83"
|
||||
#define MASS_WEAPON_DUAL_WIELD "DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222"
|
||||
#define MASS_WEAPON_COLOUR_EFX_MODE "ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014"
|
||||
#define MASS_WEAPON_COLOUR_EFX "ColorEfx_26_D921B62946C493E487455A831F4520AC"
|
||||
|
||||
// Weapon part stuff
|
||||
#define MASS_WEAPON_PART_ID "ID_2_A74D75434308158E5926178822DD28EE"
|
||||
#define MASS_WEAPON_PART_STYLES "Styles_17_994C97C34A90667BE5B716BFD0B97588"
|
||||
#define MASS_WEAPON_PART_DECALS "Decals_13_8B81112B453D7230C0CDE982185E14F1"
|
||||
#define MASS_WEAPON_PART_ACCESSORIES "Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"
|
||||
|
||||
// BL attachment stuff
|
||||
#define MASS_BL_ATTACHMENT_STYLE "WeaponBLAttachmentStyle_65_5943FCE8406F18D2C3F69285EB23A699"
|
||||
#define MASS_BL_ATTACHMENTS "WeaponBLAttachment_61_442D08F547510A4CEE1501BBAF297BA0"
|
||||
#define MASS_BL_ATTACHMENT_SOCKET "Socket_9_B9DBF30D4A1F0032A2BE2F8B342B35A9"
|
||||
#define MASS_BL_ATTACHMENT_RELLOC "RelativeLocation_10_2F6E75DF4C40622658340E9A22D38B02"
|
||||
#define MASS_BL_ATTACHMENT_OFFLOC "OffsetLocation_11_F42B3DA3436948FF85752DB33722382F"
|
||||
#define MASS_BL_ATTACHMENT_RELROT "RelativeRotation_12_578140464621245132CFF2A2AD85E735"
|
||||
#define MASS_BL_ATTACHMENT_OFFROT "OffsetRotation_13_B5980BCD47905D842D1490A1A520B064"
|
||||
#define MASS_BL_ATTACHMENT_RELSCALE "RelativeScale_16_37BC80EF42699F79533F7AA7B3094E38"
|
||||
|
||||
// Style stuff
|
||||
#define MASS_STYLE_NAME "Name_27_1532115A46EF2B2FA283908DF561A86B"
|
||||
#define MASS_STYLE_COLOUR "Color_5_F0D383DF40474C9464AE48A0984A212E"
|
||||
#define MASS_STYLE_METALLIC "Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9"
|
||||
#define MASS_STYLE_GLOSS "Gloss_11_9769599842CC275A401C4282A236E240"
|
||||
#define MASS_STYLE_PATTERN_ID "PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311"
|
||||
#define MASS_STYLE_PATTERN_OPACITY "Opacity_30_53BD060B4DFCA1C92302D6A0F7831131"
|
||||
#define MASS_STYLE_PATTERN_OFFSETX "OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48"
|
||||
#define MASS_STYLE_PATTERN_OFFSETY "OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F"
|
||||
#define MASS_STYLE_PATTERN_ROTATION "Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D"
|
||||
#define MASS_STYLE_PATTERN_SCALE "Scale_26_19DF0708409262183E1247B317137671"
|
||||
|
||||
// Decal stuff
|
||||
#define MASS_DECAL_ID "ID_3_694C0B35404D8A3168AEC89026BC8CF9"
|
||||
#define MASS_DECAL_COLOUR "Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"
|
||||
#define MASS_DECAL_POSITION "Position_41_022C8FE84E1AAFE587261E88F2C72250"
|
||||
#define MASS_DECAL_UAXIS "UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"
|
||||
#define MASS_DECAL_VAXIS "VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"
|
||||
#define MASS_DECAL_OFFSET "Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"
|
||||
#define MASS_DECAL_SCALE "Scale_32_959D1C2747AFD8D62808468235CBBA40"
|
||||
#define MASS_DECAL_ROTATION "Rotation_27_12D7C314493D203D5C2326A03C5F910F"
|
||||
#define MASS_DECAL_FLIP "Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1"
|
||||
#define MASS_DECAL_WRAP "Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62"
|
||||
|
||||
// Accessory stuff
|
||||
#define MASS_ACCESSORY_ATTACH_INDEX "AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20"
|
||||
#define MASS_ACCESSORY_ID "ID_4_5757B32647BAE263266259B8A7DFFFC1"
|
||||
#define MASS_ACCESSORY_STYLES "Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"
|
||||
#define MASS_ACCESSORY_RELPOS "RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"
|
||||
#define MASS_ACCESSORY_OFFPOS "RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"
|
||||
#define MASS_ACCESSORY_RELROT "RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"
|
||||
#define MASS_ACCESSORY_OFFROT "RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"
|
||||
#define MASS_ACCESSORY_SCALE "LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"
|
||||
|
||||
// Tuning stuff
|
||||
#define MASS_ENGINE "Engine"
|
||||
#define MASS_GEARS "Gears"
|
||||
#define MASS_OS "OS"
|
||||
#define MASS_MODULES "Modules"
|
||||
#define MASS_ARCHITECT "Architect"
|
||||
#define MASS_TECHS "Techs"
|
|
@ -0,0 +1,49 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Weapon.h"
|
||||
|
||||
Weapon::Weapon(const Weapon& other) {
|
||||
name = other.name;
|
||||
type = other.type;
|
||||
parts = Containers::Array<WeaponPart>{other.parts.size()};
|
||||
for(UnsignedInt i = 0; i < parts.size(); i++) {
|
||||
parts[i] = other.parts[i];
|
||||
}
|
||||
customStyles = other.customStyles;
|
||||
attached = other.attached;
|
||||
damageType = other.damageType;
|
||||
dualWield = other.dualWield;
|
||||
effectColourMode = other.effectColourMode;
|
||||
effectColour = other.effectColour;
|
||||
}
|
||||
|
||||
Weapon& Weapon::operator=(const Weapon& other) {
|
||||
name = other.name;
|
||||
type = other.type;
|
||||
parts = Containers::Array<WeaponPart>{other.parts.size()};
|
||||
for(UnsignedInt i = 0; i < parts.size(); i++) {
|
||||
parts[i] = other.parts[i];
|
||||
}
|
||||
customStyles = other.customStyles;
|
||||
attached = other.attached;
|
||||
damageType = other.damageType;
|
||||
dualWield = other.dualWield;
|
||||
effectColourMode = other.effectColourMode;
|
||||
effectColour = other.effectColour;
|
||||
|
||||
return *this;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Color.h>
|
||||
|
||||
#include "WeaponPart.h"
|
||||
#include "CustomStyle.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
#define c(enumerator, ...) enumerator,
|
||||
enum class WeaponType {
|
||||
#include "../Maps/WeaponTypes.hpp"
|
||||
};
|
||||
|
||||
enum class DamageType {
|
||||
#include "../Maps/DamageTypes.hpp"
|
||||
};
|
||||
|
||||
enum class EffectColourMode {
|
||||
#include "../Maps/EffectColourModes.hpp"
|
||||
};
|
||||
#undef c
|
||||
|
||||
struct Weapon {
|
||||
Weapon() = default;
|
||||
|
||||
Weapon(const Weapon& other);
|
||||
Weapon& operator=(const Weapon& other);
|
||||
|
||||
Weapon(Weapon&& other) = default;
|
||||
Weapon& operator=(Weapon&& other) = default;
|
||||
|
||||
Containers::String name;
|
||||
WeaponType type = WeaponType::Melee;
|
||||
Containers::Array<WeaponPart> parts;
|
||||
Containers::StaticArray<16, CustomStyle> customStyles{ValueInit};
|
||||
bool attached = false;
|
||||
DamageType damageType = DamageType::Physical;
|
||||
bool dualWield = false;
|
||||
EffectColourMode effectColourMode = EffectColourMode::Default;
|
||||
Color4 effectColour{0.0f};
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "Decal.h"
|
||||
#include "Accessory.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
struct WeaponPart {
|
||||
WeaponPart() = default;
|
||||
|
||||
WeaponPart(const WeaponPart& other) {
|
||||
id = other.id;
|
||||
styles = other.styles;
|
||||
decals = Containers::Array<Decal>{other.decals.size()};
|
||||
for(UnsignedInt i = 0; i < decals.size(); i++) {
|
||||
decals[i] = other.decals[i];
|
||||
}
|
||||
accessories = Containers::Array<Accessory>{other.accessories.size()};
|
||||
for(UnsignedInt i = 0; i < accessories.size(); i++) {
|
||||
accessories[i] = other.accessories[i];
|
||||
}
|
||||
}
|
||||
WeaponPart& operator=(const WeaponPart& other) {
|
||||
id = other.id;
|
||||
styles = other.styles;
|
||||
decals = Containers::Array<Decal>{other.decals.size()};
|
||||
for(UnsignedInt i = 0; i < decals.size(); i++) {
|
||||
decals[i] = other.decals[i];
|
||||
}
|
||||
accessories = Containers::Array<Accessory>{other.accessories.size()};
|
||||
for(UnsignedInt i = 0; i < accessories.size(); i++) {
|
||||
accessories[i] = other.accessories[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
WeaponPart(WeaponPart&& other) = default;
|
||||
WeaponPart& operator=(WeaponPart&& other) = default;
|
||||
|
||||
Int id = 0;
|
||||
Containers::StaticArray<4, Int> styles{ValueInit};
|
||||
Containers::Array<Decal> decals{};
|
||||
Containers::Array<Accessory> accessories{};
|
||||
};
|
|
@ -1,87 +0,0 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "MassBuilderManager.h"
|
||||
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Utility/Unicode.h>
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
MassBuilderManager::MassBuilderManager() {
|
||||
_ready = findSaveDirectory();
|
||||
}
|
||||
|
||||
auto MassBuilderManager::ready() const -> bool {
|
||||
return _ready;
|
||||
}
|
||||
|
||||
auto MassBuilderManager::lastError() -> std::string const& {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
auto MassBuilderManager::saveDirectory() -> std::string const& {
|
||||
return _saveDirectory;
|
||||
}
|
||||
|
||||
void MassBuilderManager::checkGameState() {
|
||||
WTS_PROCESS_INFOW* process_infos = nullptr;
|
||||
unsigned long process_count = 0;
|
||||
|
||||
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
|
||||
Containers::ScopeGuard guard{process_infos, WTSFreeMemory};
|
||||
|
||||
for(unsigned long i = 0; i < process_count; ++i) {
|
||||
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
|
||||
_gameState = GameState::Running;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
_gameState = GameState::NotRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_gameState = GameState::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
auto MassBuilderManager::gameState() -> GameState {
|
||||
return _gameState;
|
||||
}
|
||||
|
||||
auto MassBuilderManager::findSaveDirectory() -> bool {
|
||||
wchar_t* localappdata_path;
|
||||
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
|
||||
if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK)
|
||||
{
|
||||
_lastError = "SHGetKnownFolderPath() failed in MassBuilderManager::findSaveDirectory()";
|
||||
return false;
|
||||
}
|
||||
|
||||
_saveDirectory = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder");
|
||||
|
||||
if(!Utility::Directory::exists(_saveDirectory)) {
|
||||
_lastError = _saveDirectory + " wasn't found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -16,214 +16,237 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Utility/FormatStl.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "MassManager.h"
|
||||
|
||||
static const std::string empty_string = "";
|
||||
using namespace Containers::Literals;
|
||||
|
||||
MassManager::MassManager(const std::string& save_path, const std::string& steam_id, bool demo):
|
||||
_stagingAreaDirectory{Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging")}
|
||||
MassManager::MassManager(Containers::StringView save_path, Containers::StringView account, bool demo,
|
||||
Containers::StringView staging_dir):
|
||||
_saveDirectory{save_path}, _account{account}, _demo{demo}, _stagingAreaDirectory{staging_dir}
|
||||
{
|
||||
_saveDirectory = save_path;
|
||||
_steamId = steam_id;
|
||||
_demo = demo;
|
||||
|
||||
Containers::arrayReserve(_hangars, 32);
|
||||
|
||||
std::string mass_filename = "";
|
||||
for(int i = 0; i < 32; i++) {
|
||||
mass_filename = Utility::Directory::join(_saveDirectory, Utility::formatString("{}Unit{:.2d}{}.sav", demo ? "Demo" : "", i, _steamId));
|
||||
Containers::arrayAppend(_hangars, Mass{mass_filename});
|
||||
}
|
||||
|
||||
if(!Utility::Directory::exists(_stagingAreaDirectory)) {
|
||||
Utility::Directory::mkpath(_stagingAreaDirectory);
|
||||
Containers::String mass_filename = "";
|
||||
for(UnsignedInt i = 0; i < _hangars.size(); i++) {
|
||||
mass_filename = Utility::Path::join(_saveDirectory,
|
||||
Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account));
|
||||
new(&_hangars[i]) Mass{mass_filename};
|
||||
}
|
||||
|
||||
refreshStagedMasses();
|
||||
}
|
||||
|
||||
auto MassManager::saveDirectory() -> std::string const& {
|
||||
return _saveDirectory;
|
||||
}
|
||||
|
||||
auto MassManager::stagingAreaDirectory() -> std::string const& {
|
||||
return _stagingAreaDirectory;
|
||||
}
|
||||
|
||||
auto MassManager::lastError() -> std::string const& {
|
||||
auto MassManager::lastError() -> Containers::StringView {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
auto MassManager::massName(int hangar) -> std::string const& {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
return _hangars[hangar].name();
|
||||
}
|
||||
|
||||
auto MassManager::massState(int hangar) -> MassState {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
return MassState::Empty;
|
||||
}
|
||||
|
||||
return _hangars[hangar].state();
|
||||
}
|
||||
|
||||
void MassManager::refreshHangar(int hangar) {
|
||||
auto MassManager::hangar(Int hangar) -> Mass& {
|
||||
return _hangars[hangar];
|
||||
}
|
||||
|
||||
void MassManager::refreshHangar(Int hangar) {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar index out of range.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string mass_filename =
|
||||
Utility::Directory::join(_saveDirectory, Utility::formatString("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _steamId));
|
||||
Containers::String mass_filename =
|
||||
Utility::Path::join(_saveDirectory,
|
||||
Utility::format("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _account));
|
||||
_hangars[hangar] = Mass{mass_filename};
|
||||
}
|
||||
|
||||
auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool {
|
||||
auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bool {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar out of range in MassManager::importMass()";
|
||||
_lastError = "Hangar index out of range.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = _stagedMasses.find(staged_fn);
|
||||
auto it = _stagedMasses.find(Containers::String::nullTerminatedView(staged_fn));
|
||||
|
||||
if(it == _stagedMasses.end()) {
|
||||
_lastError = "Couldn't find " + staged_fn + " in the staged M.A.S.S.es.";
|
||||
_lastError = "Couldn't find "_s + staged_fn + " in the staged M.A.S.S.es."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string source = Utility::Directory::join(_stagingAreaDirectory, staged_fn);
|
||||
Utility::Directory::copy(source, source + ".tmp");
|
||||
Containers::String source = Utility::Path::join(_stagingAreaDirectory, staged_fn);
|
||||
Utility::Path::copy(source, source + ".tmp"_s);
|
||||
|
||||
if(!Mass{source + ".tmp"}.updateSteamId(_steamId))
|
||||
{
|
||||
_lastError = "The M.A.S.S. file at " + source + " seems to be corrupt.";
|
||||
Utility::Directory::rm(source + ".tmp");
|
||||
Mass mass{source + ".tmp"_s};
|
||||
if(!mass.updateAccount(_account)) {
|
||||
_lastError = mass.lastError();
|
||||
Utility::Path::remove(source + ".tmp"_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Utility::Directory::exists(_hangars[hangar].filename())) {
|
||||
Utility::Directory::rm(_hangars[hangar].filename());
|
||||
}
|
||||
|
||||
if(!Utility::Directory::move(source + ".tmp", _hangars[hangar].filename())) {
|
||||
_lastError = Utility::formatString("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1);
|
||||
Containers::String dest = Utility::Path::join(_saveDirectory, _hangars[hangar].filename());
|
||||
|
||||
if(Utility::Path::exists(dest)) {
|
||||
Utility::Path::remove(dest);
|
||||
}
|
||||
|
||||
if(!Utility::Path::move(source + ".tmp"_s, dest)) {
|
||||
_lastError = Utility::format("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::exportMass(int hangar) -> bool {
|
||||
auto MassManager::exportMass(Int hangar) -> bool {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar out of range in MassManager::exportMass()";
|
||||
_lastError = "Hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_hangars[hangar].state() != MassState::Valid) {
|
||||
_lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar + 1);
|
||||
if(_hangars[hangar].state() != Mass::State::Valid) {
|
||||
_lastError = Utility::format("There is no valid data to export in hangar {:.2d}", hangar + 1);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename());
|
||||
std::string dest = Utility::Directory::join(_stagingAreaDirectory,
|
||||
Utility::formatString("{}_{}.sav", _hangars[hangar].name(), _steamId));
|
||||
Containers::String source = Utility::Path::join(_saveDirectory, _hangars[hangar].filename());
|
||||
Containers::String dest = Utility::Path::join(_stagingAreaDirectory,
|
||||
Utility::format("{}_{}.sav", _hangars[hangar].name(), _account));
|
||||
|
||||
if(!Utility::Directory::copy(source, dest)) {
|
||||
_lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
|
||||
if(!Utility::Path::copy(source, dest)) {
|
||||
_lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::moveMass(int source, int destination) -> bool {
|
||||
auto MassManager::moveMass(Int source, Int destination) -> bool {
|
||||
if(source < 0 || source >= 32) {
|
||||
_lastError = "Source hangar out of range.";
|
||||
_lastError = "Source hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(destination < 0 || destination >= 32) {
|
||||
_lastError = "Destination hangar out of range.";
|
||||
_lastError = "Destination hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string source_file = _hangars[source].filename();
|
||||
std::string dest_file = _hangars[destination].filename();
|
||||
MassState dest_state = _hangars[destination].state();
|
||||
Containers::String source_file = Utility::Path::join(_saveDirectory, _hangars[source].filename());
|
||||
Containers::String dest_file = Utility::Path::join(_saveDirectory, _hangars[destination].filename());
|
||||
Mass::State dest_state = _hangars[destination].state();
|
||||
|
||||
switch(dest_state) {
|
||||
case MassState::Empty:
|
||||
case Mass::State::Empty:
|
||||
break;
|
||||
case MassState::Invalid:
|
||||
Utility::Directory::rm(dest_file);
|
||||
case Mass::State::Invalid:
|
||||
Utility::Path::remove(dest_file);
|
||||
break;
|
||||
case MassState::Valid:
|
||||
Utility::Directory::move(dest_file, dest_file + ".tmp");
|
||||
case Mass::State::Valid:
|
||||
Utility::Path::move(dest_file, dest_file + ".tmp"_s);
|
||||
break;
|
||||
}
|
||||
|
||||
Utility::Directory::move(source_file, dest_file);
|
||||
Utility::Path::move(source_file, dest_file);
|
||||
|
||||
if(dest_state == MassState::Valid) {
|
||||
Utility::Directory::move(dest_file + ".tmp", source_file);
|
||||
if(dest_state == Mass::State::Valid) {
|
||||
Utility::Path::move(dest_file + ".tmp"_s, source_file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::deleteMass(int hangar) -> bool {
|
||||
auto MassManager::deleteMass(Int hangar) -> bool {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar out of bounds";
|
||||
_lastError = "Hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Utility::Directory::rm(_hangars[hangar].filename())) {
|
||||
_lastError = "Deletion failed. Maybe the file was already deleted, or it's locked by another application.";
|
||||
if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _hangars[hangar].filename()))) {
|
||||
_lastError = Utility::format("Deletion failed: {}", std::strerror(errno));
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto MassManager::stagedMasses() -> std::map<std::string, std::string> const& {
|
||||
auto MassManager::stagedMasses() -> std::map<Containers::String, Containers::String> const& {
|
||||
return _stagedMasses;
|
||||
}
|
||||
|
||||
void MassManager::refreshStagedMasses() {
|
||||
_stagedMasses.clear();
|
||||
|
||||
using Utility::Directory::Flag;
|
||||
std::vector<std::string> file_list = Utility::Directory::list(_stagingAreaDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
|
||||
using Utility::Path::ListFlag;
|
||||
auto file_list = Utility::Path::list(_stagingAreaDirectory,
|
||||
ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
|
||||
|
||||
auto iter = std::remove_if(file_list.begin(), file_list.end(), [](std::string& file){
|
||||
return !Utility::String::endsWith(file, ".sav");
|
||||
if(!file_list) {
|
||||
LOG_ERROR_FORMAT("{} couldn't be opened.", _stagingAreaDirectory);
|
||||
return;
|
||||
}
|
||||
|
||||
auto iter = std::remove_if(file_list->begin(), file_list->end(), [](Containers::StringView file){
|
||||
return !file.hasSuffix(".sav"_s);
|
||||
});
|
||||
|
||||
file_list.erase(iter, file_list.end());
|
||||
auto list_view = file_list->exceptSuffix(file_list->end() - iter);
|
||||
|
||||
for(const std::string& file : file_list) {
|
||||
std::string name = Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file));
|
||||
LOG_INFO("Scanning for staged M.A.S.S.es...");
|
||||
for(Containers::StringView file : list_view) {
|
||||
auto name = Mass::getNameFromFile(Utility::Path::join(_stagingAreaDirectory, file));
|
||||
|
||||
if(!name.empty()) {
|
||||
_stagedMasses[file] = name;
|
||||
if(name) {
|
||||
LOG_INFO_FORMAT("Found staged M.A.S.S.: {}", *name);
|
||||
_stagedMasses[file] = *name;
|
||||
}
|
||||
else {
|
||||
LOG_WARNING_FORMAT("Skipped {}.", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto MassManager::deleteStagedMass(const std::string& filename) -> bool {
|
||||
void MassManager::refreshStagedMass(Containers::StringView filename) {
|
||||
LOG_INFO_FORMAT("Refreshing staged unit with filename {}.", filename);
|
||||
|
||||
bool file_exists = Utility::Path::exists(Utility::Path::join(_stagingAreaDirectory, filename));
|
||||
auto it = _stagedMasses.find(filename);
|
||||
|
||||
if(file_exists) {
|
||||
auto name = Mass::getNameFromFile(Utility::Path::join(_stagingAreaDirectory, filename));
|
||||
if(name) {
|
||||
_stagedMasses[filename] = *name;
|
||||
}
|
||||
else if(it != _stagedMasses.cend()) {
|
||||
_stagedMasses.erase(it);
|
||||
}
|
||||
}
|
||||
else if(it != _stagedMasses.cend()) {
|
||||
_stagedMasses.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
auto MassManager::deleteStagedMass(Containers::StringView filename) -> bool {
|
||||
if(_stagedMasses.find(filename) == _stagedMasses.cend()) {
|
||||
_lastError = "The file " + filename + " couldn't be found in the list of staged M.A.S.S.es.";
|
||||
_lastError = "The file "_s + filename + " couldn't be found in the list of staged M.A.S.S.es."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Utility::Directory::rm(Utility::Directory::join(_stagingAreaDirectory, filename))) {
|
||||
_lastError = "The file " + filename + " couldn't be deleted for unknown reasons.";
|
||||
if(!Utility::Path::remove(Utility::Path::join(_stagingAreaDirectory, filename))) {
|
||||
_lastError = filename + " couldn't be deleted: " + std::strerror(errno);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -17,9 +17,10 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <Corrade/Containers/GrowableArray.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "../Mass/Mass.h"
|
||||
|
||||
|
@ -27,38 +28,35 @@ using namespace Corrade;
|
|||
|
||||
class MassManager {
|
||||
public:
|
||||
MassManager(const std::string& save_path, const std::string& steam_id, bool demo);
|
||||
MassManager(Containers::StringView save_path, Containers::StringView account, bool demo, Containers::StringView staging_dir);
|
||||
|
||||
auto saveDirectory() -> std::string const&;
|
||||
auto stagingAreaDirectory() -> std::string const&;
|
||||
auto lastError() -> Containers::StringView;
|
||||
|
||||
auto lastError() -> std::string const&;
|
||||
|
||||
auto massName(int hangar) -> std::string const&;
|
||||
auto massState(int hangar) -> MassState;
|
||||
auto hangar(int hangar) -> Mass&;
|
||||
|
||||
void refreshHangar(int hangar);
|
||||
|
||||
auto importMass(const std::string& staged_fn, int hangar) -> bool;
|
||||
auto importMass(Containers::StringView staged_fn, int hangar) -> bool;
|
||||
auto exportMass(int hangar) -> bool;
|
||||
|
||||
auto moveMass(int source, int destination) -> bool;
|
||||
auto deleteMass(int hangar) -> bool;
|
||||
|
||||
auto stagedMasses() -> std::map<std::string, std::string> const&;
|
||||
auto stagedMasses() -> std::map<Containers::String, Containers::String> const&;
|
||||
void refreshStagedMasses();
|
||||
auto deleteStagedMass(const std::string& filename) -> bool;
|
||||
void refreshStagedMass(Containers::StringView filename);
|
||||
auto deleteStagedMass(Containers::StringView filename) -> bool;
|
||||
|
||||
private:
|
||||
std::string _saveDirectory;
|
||||
std::string _steamId;
|
||||
Containers::StringView _saveDirectory;
|
||||
Containers::StringView _account;
|
||||
bool _demo;
|
||||
|
||||
std::string _lastError = "";
|
||||
Containers::String _lastError;
|
||||
|
||||
Containers::Array<Mass> _hangars;
|
||||
Containers::StaticArray<32, Mass> _hangars{NoInit};
|
||||
|
||||
const std::string _stagingAreaDirectory;
|
||||
Containers::StringView _stagingAreaDirectory;
|
||||
|
||||
std::map<std::string, std::string> _stagedMasses;
|
||||
std::map<Containers::String, Containers::String> _stagedMasses;
|
||||
};
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
constexpr char company_name_locator[] = "CompanyName\0\f\0\0\0StrProperty";
|
||||
constexpr char active_slot_locator[] = "ActiveFrameSlot\0\f\0\0\0IntProperty";
|
||||
constexpr char credits_locator[] = "Credit\0\f\0\0\0IntProperty";
|
||||
constexpr char story_progress_locator[] = "StoryProgress\0\f\0\0\0IntProperty";
|
||||
constexpr char last_mission_id_locator[] = "LastMissionID\0\f\0\0\0IntProperty";
|
||||
|
||||
constexpr char verse_steel_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x00\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char undinium_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x01\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char necrium_alloy_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x02\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char lunarite_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x03\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char asterite_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x04\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
|
||||
constexpr char ednil_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0a\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char nuflalt_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0b\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char aurelene_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\f\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char soldus_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0d\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char synthesized_n_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0e\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
|
||||
constexpr char alcarbonite_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x14\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char keriphene_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x15\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char nitinol_cm_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x16\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char quarkium_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x17\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char alterene_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x18\x35\f\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
|
||||
constexpr char mixed_composition_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa0\xbb\x0d\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char void_residue_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa1\xbb\x0d\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char muscular_construction_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa2\xbb\x0d\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char mineral_exoskeletology_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa3\xbb\x0d\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
constexpr char carbonized_skin_locator[] =
|
||||
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa4\xbb\x0d\0,\0\0\0"
|
||||
"Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty";
|
||||
|
||||
constexpr char engine_inventory_locator[] = "InventoryEngine\0\x0e\0\0\0ArrayProperty";
|
||||
constexpr char gear_inventory_locator[] = "InventoryGear\0\x0e\0\0\0ArrayProperty";
|
||||
constexpr char os_inventory_locator[] = "InventoryOS\0\x0e\0\0\0ArrayProperty";
|
||||
constexpr char module_inventory_locator[] = "InventoryModule\0\x0e\0\0\0ArrayProperty";
|
||||
constexpr char arch_inventory_locator[] = "InventoryArchitect\0\x0e\0\0\0ArrayProperty";
|
||||
constexpr char tech_inventory_locator[] = "InventoryTech\0\x0e\0\0\0ArrayProperty";
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -16,10 +16,16 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <Corrade/Containers/String.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
|
||||
#include "../UESaveFile/UESaveFile.h"
|
||||
|
||||
#include "ResourceIDs.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
enum class ProfileType : UnsignedByte {
|
||||
|
@ -29,158 +35,152 @@ enum class ProfileType : UnsignedByte {
|
|||
|
||||
class Profile {
|
||||
public:
|
||||
explicit Profile(const std::string& path);
|
||||
explicit Profile(Containers::StringView path);
|
||||
|
||||
auto valid() const -> bool;
|
||||
|
||||
auto lastError() const -> std::string const&;
|
||||
auto lastError() const -> Containers::StringView;
|
||||
|
||||
auto filename() const -> std::string const&;
|
||||
auto filename() const -> Containers::StringView;
|
||||
|
||||
auto type() const -> ProfileType;
|
||||
auto isDemo() const -> bool;
|
||||
|
||||
auto steamId() const -> std::string const&;
|
||||
auto account() const -> Containers::StringView;
|
||||
|
||||
void refreshValues();
|
||||
|
||||
auto companyName() const -> std::string const&;
|
||||
auto getCompanyName() -> std::string const&;
|
||||
auto renameCompany(const std::string& new_name) -> bool;
|
||||
auto companyName() const -> Containers::StringView;
|
||||
auto renameCompany(Containers::StringView new_name) -> bool;
|
||||
|
||||
auto activeFrameSlot() const -> Int;
|
||||
auto getActiveFrameSlot() -> Int;
|
||||
|
||||
auto credits() const -> Int;
|
||||
auto getCredits() -> Int;
|
||||
auto setCredits(Int credits) -> bool;
|
||||
|
||||
auto storyProgress() const -> Int;
|
||||
auto getStoryProgress() -> Int;
|
||||
auto setStoryProgress(Int progress) -> bool;
|
||||
|
||||
auto lastMissionId() const -> Int;
|
||||
auto getLastMissionId() -> Int;
|
||||
|
||||
auto verseSteel() const -> Int;
|
||||
auto getVerseSteel() -> Int;
|
||||
auto setVerseSteel(Int amount) -> bool;
|
||||
|
||||
auto undinium() const -> Int;
|
||||
auto getUndinium() -> Int;
|
||||
auto setUndinium(Int amount) -> bool;
|
||||
|
||||
auto necriumAlloy() const -> Int;
|
||||
auto getNecriumAlloy() -> Int;
|
||||
auto setNecriumAlloy(Int amount) -> bool;
|
||||
|
||||
auto lunarite() const -> Int;
|
||||
auto getLunarite() -> Int;
|
||||
auto setLunarite(Int amount) -> bool;
|
||||
|
||||
auto asterite() const -> Int;
|
||||
auto getAsterite() -> Int;
|
||||
auto setAsterite(Int amount) -> bool;
|
||||
|
||||
Int halliteFragma() const;
|
||||
bool setHalliteFragma(Int amount);
|
||||
|
||||
auto ednil() const -> Int;
|
||||
auto getEdnil() -> Int;
|
||||
auto setEdnil(Int amount) -> bool;
|
||||
|
||||
auto nuflalt() const -> Int;
|
||||
auto getNuflalt() -> Int;
|
||||
auto setNuflalt(Int amount) -> bool;
|
||||
|
||||
auto aurelene() const -> Int;
|
||||
auto getAurelene() -> Int;
|
||||
auto setAurelene(Int amount) -> bool;
|
||||
|
||||
auto soldus() const -> Int;
|
||||
auto getSoldus() -> Int;
|
||||
auto setSoldus(Int amount) -> bool;
|
||||
|
||||
auto synthesizedN() const -> Int;
|
||||
auto getSynthesizedN() -> Int;
|
||||
auto setSynthesizedN(Int amount) -> bool;
|
||||
auto synthesisedN() const -> Int;
|
||||
auto setSynthesisedN(Int amount) -> bool;
|
||||
|
||||
Int nanoc() const;
|
||||
bool setNanoc(Int amount);
|
||||
|
||||
auto alcarbonite() const -> Int;
|
||||
auto getAlcarbonite() -> Int;
|
||||
auto setAlcarbonite(Int amount) -> bool;
|
||||
|
||||
auto keriphene() const -> Int;
|
||||
auto getKeriphene() -> Int;
|
||||
auto setKeriphene(Int amount) -> bool;
|
||||
|
||||
auto nitinolCM() const -> Int;
|
||||
auto getNitinolCM() -> Int;
|
||||
auto setNitinolCM(Int amount) -> bool;
|
||||
|
||||
auto quarkium() const -> Int;
|
||||
auto getQuarkium() -> Int;
|
||||
auto setQuarkium(Int amount) -> bool;
|
||||
|
||||
auto alterene() const -> Int;
|
||||
auto getAlterene() -> Int;
|
||||
auto setAlterene(Int amount) -> bool;
|
||||
|
||||
Int cosmium() const;
|
||||
bool setCosmium(Int amount);
|
||||
|
||||
auto mixedComposition() const -> Int;
|
||||
auto getMixedComposition() -> Int;
|
||||
auto setMixedComposition(Int amount) -> bool;
|
||||
|
||||
auto voidResidue() const -> Int;
|
||||
auto getVoidResidue() -> Int;
|
||||
auto setVoidResidue(Int amount) -> bool;
|
||||
|
||||
auto muscularConstruction() const -> Int;
|
||||
auto getMuscularConstruction() -> Int;
|
||||
auto setMuscularConstruction(Int amount) -> bool;
|
||||
|
||||
auto mineralExoskeletology() const -> Int;
|
||||
auto getMineralExoskeletology() -> Int;
|
||||
auto setMineralExoskeletology(Int amount) -> bool;
|
||||
|
||||
auto carbonizedSkin() const -> Int;
|
||||
auto getCarbonizedSkin() -> Int;
|
||||
auto setCarbonizedSkin(Int amount) -> bool;
|
||||
auto carbonisedSkin() const -> Int;
|
||||
auto setCarbonisedSkin(Int amount) -> bool;
|
||||
|
||||
Int isolatedVoidParticle() const;
|
||||
bool setIsolatedVoidParticle(Int amount);
|
||||
|
||||
private:
|
||||
std::string _profileDirectory;
|
||||
std::string _filename;
|
||||
auto getResource(Containers::StringView container, MaterialID id) -> Int;
|
||||
auto setResource(Containers::StringView container, MaterialID id, Int amount) -> bool;
|
||||
|
||||
Containers::String _filename;
|
||||
|
||||
ProfileType _type;
|
||||
|
||||
std::string _steamId;
|
||||
UESaveFile _profile;
|
||||
|
||||
Containers::String _name;
|
||||
Int _activeFrameSlot = 0;
|
||||
Int _credits = 0;
|
||||
Int _storyProgress = 0;
|
||||
Int _lastMissionId = 0;
|
||||
|
||||
Int _verseSteel = 0;
|
||||
Int _undinium = 0;
|
||||
Int _necriumAlloy = 0;
|
||||
Int _lunarite = 0;
|
||||
Int _asterite = 0;
|
||||
Int _halliteFragma = 0;
|
||||
|
||||
Int _ednil = 0;
|
||||
Int _nuflalt = 0;
|
||||
Int _aurelene = 0;
|
||||
Int _soldus = 0;
|
||||
Int _synthesisedN = 0;
|
||||
Int _nanoc = 0;
|
||||
|
||||
Int _alcarbonite = 0;
|
||||
Int _keriphene = 0;
|
||||
Int _nitinolCM = 0;
|
||||
Int _quarkium = 0;
|
||||
Int _alterene = 0;
|
||||
Int _cosmium = 0;
|
||||
|
||||
Int _mixedComposition = 0;
|
||||
Int _voidResidue = 0;
|
||||
Int _muscularConstruction = 0;
|
||||
Int _mineralExoskeletology = 0;
|
||||
Int _carbonisedSkin = 0;
|
||||
Int _isolatedVoidParticle = 0;
|
||||
|
||||
Containers::String _account;
|
||||
|
||||
bool _valid = false;
|
||||
std::string _lastError;
|
||||
|
||||
std::string _companyName;
|
||||
|
||||
Int _activeFrameSlot = 0;
|
||||
|
||||
Int _credits;
|
||||
|
||||
Int _storyProgress;
|
||||
|
||||
Int _lastMissionId;
|
||||
|
||||
Int _verseSteel;
|
||||
Int _undinium;
|
||||
Int _necriumAlloy;
|
||||
Int _lunarite;
|
||||
Int _asterite;
|
||||
Int _ednil;
|
||||
Int _nuflalt;
|
||||
Int _aurelene;
|
||||
Int _soldus;
|
||||
Int _synthesizedN;
|
||||
Int _alcarbonite;
|
||||
Int _keriphene;
|
||||
Int _nitinolCM;
|
||||
Int _quarkium;
|
||||
Int _alterene;
|
||||
|
||||
Int _mixedComposition;
|
||||
Int _voidResidue;
|
||||
Int _muscularConstruction;
|
||||
Int _mineralExoskeletology;
|
||||
Int _carbonizedSkin;
|
||||
Containers::String _lastError;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#define PROFILE_NAME "CompanyName"
|
||||
#define PROFILE_ACTIVE_FRAME_SLOT "ActiveFrameSlot"
|
||||
#define PROFILE_CREDITS "Credit"
|
||||
#define PROFILE_STORY_PROGRESS "StoryProgress"
|
||||
#define PROFILE_LAST_MISSION_ID "LastMissionID"
|
||||
#define PROFILE_MATERIAL "ResourceMaterial"
|
||||
#define PROFILE_QUARK_DATA "ResourceQuarkData"
|
||||
#define PROFILE_ACCOUNT "Account"
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Magnum;
|
||||
|
||||
enum MaterialID : Int {
|
||||
VerseSteel = 0xC3500,
|
||||
Undinium = 0xC3501,
|
||||
NecriumAlloy = 0xC3502,
|
||||
Lunarite = 0xC3503,
|
||||
Asterite = 0xC3504,
|
||||
HalliteFragma = 0xC3505,
|
||||
|
||||
Ednil = 0xC350A,
|
||||
Nuflalt = 0xC350B,
|
||||
Aurelene = 0xC350C,
|
||||
Soldus = 0xC350D,
|
||||
SynthesisedN = 0xC350E,
|
||||
Nanoc = 0xC350F,
|
||||
|
||||
Alcarbonite = 0xC3514,
|
||||
Keriphene = 0xC3515,
|
||||
NitinolCM = 0xC3516,
|
||||
Quarkium = 0xC3517,
|
||||
Alterene = 0xC3518,
|
||||
Cosmium = 0xC3519,
|
||||
|
||||
MixedComposition = 0xDBBA0,
|
||||
VoidResidue = 0xDBBA1,
|
||||
MuscularConstruction = 0xDBBA2,
|
||||
MineralExoskeletology = 0xDBBA3,
|
||||
CarbonisedSkin = 0xDBBA4,
|
||||
IsolatedVoidParticle = 0xDBBA5,
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -18,35 +18,25 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Utility/FormatStl.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
|
||||
#include <zip.h>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "ProfileManager.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
|
||||
ProfileManager::ProfileManager(const std::string& base_path):
|
||||
_backupsDirectory{Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups")}
|
||||
ProfileManager::ProfileManager(Containers::StringView save_dir, Containers::StringView backup_dir):
|
||||
_saveDirectory{save_dir},
|
||||
_backupsDirectory{backup_dir}
|
||||
{
|
||||
_saveDirectory = Utility::Directory::join(base_path, "Saved/SaveGames");
|
||||
|
||||
if(!Utility::Directory::exists(_backupsDirectory)) {
|
||||
Utility::Directory::mkpath(_backupsDirectory);
|
||||
}
|
||||
|
||||
if(!Utility::Directory::exists(_saveDirectory)) {
|
||||
_lastError = "Couldn't find the profile directory. Make sure you played enough of the game.";
|
||||
return;
|
||||
}
|
||||
|
||||
_ready = refreshProfiles();
|
||||
}
|
||||
|
||||
|
@ -54,49 +44,49 @@ auto ProfileManager::ready() const -> bool {
|
|||
return _ready;
|
||||
}
|
||||
|
||||
auto ProfileManager::lastError() -> std::string const& {
|
||||
auto ProfileManager::lastError() -> Containers::StringView {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
auto ProfileManager::saveDirectory() -> std::string const& {
|
||||
return _saveDirectory;
|
||||
}
|
||||
|
||||
auto ProfileManager::backupsDirectory() -> std::string const& {
|
||||
return _backupsDirectory;
|
||||
}
|
||||
|
||||
auto ProfileManager::profiles() -> std::vector<Profile> const& {
|
||||
auto ProfileManager::profiles() -> Containers::ArrayView<Profile> {
|
||||
return _profiles;
|
||||
}
|
||||
|
||||
auto ProfileManager::refreshProfiles() -> bool {
|
||||
_profiles.clear();
|
||||
LOG_INFO("Refreshing profiles.");
|
||||
|
||||
using Utility::Directory::Flag;
|
||||
std::vector<std::string> files = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
|
||||
_profiles = Containers::Array<Profile>{};
|
||||
|
||||
auto predicate = [](const std::string& file)->bool{
|
||||
std::regex regex("(Demo)?Profile[0-9]{17}\\.sav", std::regex::nosubs);
|
||||
std::cmatch m;
|
||||
return !std::regex_match(file.c_str(), m, regex);
|
||||
using Utility::Path::ListFlag;
|
||||
auto files = Utility::Path::list(_saveDirectory,
|
||||
ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
|
||||
|
||||
if(!files) {
|
||||
_lastError = _saveDirectory + " can't be opened.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto predicate = [](Containers::StringView file)->bool{
|
||||
return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav"));
|
||||
};
|
||||
|
||||
files.erase(std::remove_if(files.begin(), files.end(), predicate), files.end());
|
||||
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||
|
||||
for(const std::string& file : files) {
|
||||
Profile profile{Utility::Directory::join(_saveDirectory, file)};
|
||||
for(const auto& file : files_view) {
|
||||
Profile profile{Utility::Path::join(_saveDirectory, file)};
|
||||
|
||||
if(!profile.valid()) {
|
||||
Utility::Warning{} << "Profile" << file.c_str() << "is invalid:" << profile.lastError().c_str();
|
||||
LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
_profiles.push_back(std::move(profile));
|
||||
arrayAppend(_profiles, std::move(profile));
|
||||
}
|
||||
|
||||
if(_profiles.empty()) {
|
||||
_lastError = "No profiles were found.";
|
||||
if(_profiles.isEmpty()) {
|
||||
_lastError = "No valid profiles were found."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -104,28 +94,34 @@ auto ProfileManager::refreshProfiles() -> bool {
|
|||
}
|
||||
|
||||
auto ProfileManager::getProfile(std::size_t index) -> Profile* {
|
||||
return &(_profiles.at(index));
|
||||
return index <= _profiles.size() ? &(_profiles[index]) : nullptr;
|
||||
}
|
||||
|
||||
auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> bool {
|
||||
if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename()))) {
|
||||
_lastError = Utility::formatString("Couldn't delete {} (filename: {}).",
|
||||
_profiles.at(index).companyName(),
|
||||
_profiles.at(index).filename());
|
||||
if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _profiles[index].filename()))) {
|
||||
_lastError = Utility::format("Couldn't delete {} (filename: {}).",
|
||||
_profiles[index].companyName(),
|
||||
_profiles[index].filename());
|
||||
LOG_ERROR(_lastError);
|
||||
refreshProfiles();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(delete_builds) {
|
||||
for(UnsignedByte i = 0; i < 32; ++i) {
|
||||
std::string filename = Utility::formatString("{}Unit{:.2d}{}.sav",
|
||||
_profiles.at(index).type() == ProfileType::Demo ? "Demo": "",
|
||||
i, _profiles.at(index).steamId());
|
||||
Utility::Directory::rm(Utility::Directory::join(_saveDirectory, filename));
|
||||
auto filename = Utility::format("{}Unit{:.2d}{}.sav",
|
||||
_profiles[index].type() == ProfileType::Demo ? "Demo": "",
|
||||
i, _profiles[index].account());
|
||||
Utility::Path::remove(Utility::Path::join(_saveDirectory, filename));
|
||||
}
|
||||
}
|
||||
|
||||
_profiles.erase(_profiles.cbegin() + index);
|
||||
auto file = _profiles[index].filename();
|
||||
auto it = std::remove_if(_profiles.begin(), _profiles.end(), [&file](Profile& profile){ return profile.filename() == file; });
|
||||
|
||||
if(it != _profiles.end()) {
|
||||
arrayRemoveSuffix(_profiles, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -133,59 +129,62 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo
|
|||
auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> bool {
|
||||
std::time_t timestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
std::tm* time = std::localtime(×tamp);
|
||||
auto& profile = _profiles[index];
|
||||
|
||||
std::string filename = Utility::formatString("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup",
|
||||
Utility::String::replaceAll(_profiles.at(index).companyName(), " ", "_"),
|
||||
auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.backup.mbst",
|
||||
Utility::String::replaceAll(profile.companyName().data(), " ", "_").c_str(),
|
||||
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
|
||||
time->tm_hour, time->tm_min, time->tm_sec);
|
||||
|
||||
int error_code = 0;
|
||||
zip_error_t error;
|
||||
zip_t* zip = zip_open(Utility::Directory::join(_backupsDirectory, filename).c_str(), ZIP_CREATE|ZIP_TRUNCATE, &error_code);
|
||||
zip_t* zip = zip_open(Utility::Path::join(_backupsDirectory, filename).data(), ZIP_CREATE|ZIP_TRUNCATE, &error_code);
|
||||
if(zip == nullptr) {
|
||||
zip_error_init_with_code(&error, error_code);
|
||||
_lastError = zip_error_strerror(&error);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
zip_source_t* profile_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename())).c_str(), 0, 0);
|
||||
zip_source_t* profile_source = zip_source_file(zip, Utility::Path::toNativeSeparators(Utility::Path::join(_saveDirectory, profile.filename())).data(), 0, 0);
|
||||
if(profile_source == nullptr) {
|
||||
_lastError = zip_strerror(zip);
|
||||
LOG_ERROR(_lastError);
|
||||
zip_source_free(profile_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(zip_file_add(zip, _profiles.at(index).filename().c_str(), profile_source, ZIP_FL_ENC_UTF_8) == -1) {
|
||||
if(zip_file_add(zip, profile.filename().data(), profile_source, ZIP_FL_ENC_UTF_8) == -1) {
|
||||
_lastError = zip_strerror(zip);
|
||||
LOG_ERROR(_lastError);
|
||||
zip_source_free(profile_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string comment = Utility::String::join({_profiles.at(index).companyName(),
|
||||
_profiles.at(index).type() == ProfileType::Demo ? "demo" : "full",
|
||||
Utility::formatString("{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
|
||||
auto comment = Utility::format("{}|{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
|
||||
profile.companyName(),
|
||||
profile.isDemo() ? "demo"_s : "full"_s,
|
||||
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
|
||||
time->tm_hour, time->tm_min, time->tm_sec)
|
||||
}, '|');
|
||||
zip_set_archive_comment(zip, comment.c_str(), comment.length());
|
||||
time->tm_hour, time->tm_min, time->tm_sec);
|
||||
zip_set_archive_comment(zip, comment.data(), comment.size());
|
||||
|
||||
if(backup_builds) {
|
||||
for(UnsignedByte i = 0; i < 32; ++i) {
|
||||
std::string build_filename = Utility::formatString("{}Unit{:.2d}{}.sav",
|
||||
_profiles.at(index).type() == ProfileType::Demo ? "Demo": "",
|
||||
i, _profiles.at(index).steamId());
|
||||
auto build_filename = Utility::format("{}Unit{:.2d}{}.sav",
|
||||
profile.isDemo() ? "Demo"_s : ""_s, i,
|
||||
profile.account());
|
||||
|
||||
if(!Utility::Directory::exists(Utility::Directory::join(_saveDirectory, build_filename))) {
|
||||
if(!Utility::Path::exists(Utility::Path::join(_saveDirectory, build_filename))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
zip_source_t* build_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, build_filename)).c_str(), 0, 0);
|
||||
zip_source_t* build_source = zip_source_file(zip, Utility::Path::toNativeSeparators(Utility::Path::join(_saveDirectory, build_filename)).data(), 0, 0);
|
||||
if(build_source == nullptr) {
|
||||
zip_source_free(build_source);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(zip_file_add(zip, build_filename.c_str(), build_source, ZIP_FL_ENC_UTF_8) == -1) {
|
||||
if(zip_file_add(zip, build_filename.data(), build_source, ZIP_FL_ENC_UTF_8) == -1) {
|
||||
zip_source_free(build_source);
|
||||
continue;
|
||||
}
|
||||
|
@ -194,6 +193,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
|
|||
|
||||
if(zip_close(zip) == -1) {
|
||||
_lastError = zip_strerror(zip);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -202,128 +202,144 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
|
|||
return true;
|
||||
}
|
||||
|
||||
auto ProfileManager::backups() -> std::vector<Backup> const& {
|
||||
auto ProfileManager::backups() -> Containers::ArrayView<Backup> {
|
||||
return _backups;
|
||||
}
|
||||
|
||||
void ProfileManager::refreshBackups() {
|
||||
_backups.clear();
|
||||
_backups = Containers::Array<Backup>{};
|
||||
|
||||
using Utility::Directory::Flag;
|
||||
std::vector<std::string> files = Utility::Directory::list(_backupsDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
|
||||
using Utility::Path::ListFlag;
|
||||
auto files = Utility::Path::list(_backupsDirectory,
|
||||
ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
|
||||
|
||||
auto predicate = [](const std::string& file)->bool{
|
||||
return !Utility::String::endsWith(file, ".mbprofbackup");
|
||||
if(!files) {
|
||||
_lastError = _backupsDirectory + " can't be opened.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
auto predicate = [](Containers::StringView file)->bool{
|
||||
return !(file.hasSuffix(".mbprofbackup"_s) || file.hasSuffix(".backup.mbst"));
|
||||
};
|
||||
|
||||
files.erase(std::remove_if(files.begin(), files.end(), predicate), files.end());
|
||||
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||
|
||||
int error_code = 0;
|
||||
zip_t* zip = nullptr;
|
||||
for(const std::string& file : files) {
|
||||
for(Containers::StringView file : files_view) {
|
||||
Backup backup;
|
||||
backup.filename = file;
|
||||
|
||||
zip = zip_open(Utility::Directory::join(_backupsDirectory, file).c_str(), ZIP_RDONLY, &error_code);
|
||||
zip = zip_open(Utility::Path::join(_backupsDirectory, file).data(), ZIP_RDONLY, &error_code);
|
||||
if(zip == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Containers::ScopeGuard guard{zip, zip_close};
|
||||
|
||||
int comment_length;
|
||||
const char* comment = zip_get_archive_comment(zip, &comment_length, ZIP_FL_UNCHANGED);
|
||||
if(comment == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = Utility::String::split(comment, '|');
|
||||
|
||||
if(info.size() != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backup.company = info.at(0);
|
||||
|
||||
if(info.at(1) == "full") {
|
||||
backup.type = ProfileType::FullGame;
|
||||
}
|
||||
else if(info.at(1) == "demo") {
|
||||
backup.type = ProfileType::Demo;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ts = Utility::String::split(info.at(2), '-');
|
||||
if(ts.size() != 6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backup.timestamp.year = std::stoi(ts.at(0));
|
||||
backup.timestamp.month = std::stoi(ts.at(1));
|
||||
backup.timestamp.day = std::stoi(ts.at(2));
|
||||
backup.timestamp.hour = std::stoi(ts.at(3));
|
||||
backup.timestamp.minute = std::stoi(ts.at(4));
|
||||
backup.timestamp.second = std::stoi(ts.at(5));
|
||||
|
||||
Long num_entries = zip_get_num_entries(zip, ZIP_FL_UNCHANGED);
|
||||
|
||||
if(num_entries == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backup.includedFiles.reserve(num_entries);
|
||||
|
||||
for(Long i = 0; i < num_entries; i++) {
|
||||
backup.includedFiles.emplace_back(zip_get_name(zip, i, ZIP_FL_UNCHANGED));
|
||||
int comment_length;
|
||||
Containers::StringView comment = zip_get_archive_comment(zip, &comment_length, ZIP_FL_UNCHANGED);
|
||||
if(comment == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_backups.push_back(std::move(backup));
|
||||
auto info = comment.split('|');
|
||||
|
||||
if(info.size() != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backup.company = info[0];
|
||||
|
||||
if(info[1].hasPrefix("full")) {
|
||||
backup.type = ProfileType::FullGame;
|
||||
}
|
||||
else if(info[1].hasPrefix("demo")) {
|
||||
backup.type = ProfileType::Demo;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ts = info[2].split('-');
|
||||
if(ts.size() != 6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
backup.timestamp.year = std::strtol(ts[0].data(), nullptr, 10);
|
||||
backup.timestamp.month = std::strtol(ts[1].data(), nullptr, 10);
|
||||
backup.timestamp.day = std::strtol(ts[2].data(), nullptr, 10);
|
||||
backup.timestamp.hour = std::strtol(ts[3].data(), nullptr, 10);
|
||||
backup.timestamp.minute = std::strtol(ts[4].data(), nullptr, 10);
|
||||
backup.timestamp.second = std::strtol(ts[5].data(), nullptr, 10);
|
||||
|
||||
arrayReserve(backup.includedFiles, num_entries);
|
||||
|
||||
for(Long i = 0; i < num_entries; i++) {
|
||||
arrayAppend(backup.includedFiles, InPlaceInit, zip_get_name(zip, i, ZIP_FL_UNCHANGED));
|
||||
}
|
||||
|
||||
arrayAppend(_backups, std::move(backup));
|
||||
}
|
||||
}
|
||||
|
||||
auto ProfileManager::deleteBackup(std::size_t index) -> bool {
|
||||
if(!Utility::Directory::rm(Utility::Directory::join(_backupsDirectory, _backups.at(index).filename))) {
|
||||
_lastError = "Couldn't delete " + _backups.at(index).filename;
|
||||
if(!Utility::Path::remove(Utility::Path::join(_backupsDirectory, _backups[index].filename))) {
|
||||
_lastError = "Couldn't delete " + _backups[index].filename;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
_backups.erase(_backups.begin() + index);
|
||||
auto file = _backups[index].filename;
|
||||
auto it = std::remove_if(_backups.begin(), _backups.end(), [&file](Backup& backup){return backup.filename == file;});
|
||||
|
||||
if(it != _backups.end()) {
|
||||
arrayRemoveSuffix(_backups, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ProfileManager::restoreBackup(std::size_t index) -> bool {
|
||||
const Backup& backup = _backups.at(index);
|
||||
const Backup& backup = _backups[index];
|
||||
|
||||
static const char* error_format = "Extraction of file {} failed: {}";
|
||||
auto error_format = "Extraction of file {} failed: {}"_s;
|
||||
|
||||
int error_code = 0;
|
||||
zip_t* zip = nullptr;
|
||||
|
||||
zip = zip_open(Utility::Directory::join(_backupsDirectory, backup.filename).c_str(), ZIP_RDONLY, &error_code);
|
||||
zip = zip_open(Utility::Path::join(_backupsDirectory, backup.filename).data(), ZIP_RDONLY, &error_code);
|
||||
if(zip == nullptr) {
|
||||
zip_error_t error;
|
||||
zip_error_init_with_code(&error, error_code);
|
||||
_lastError = zip_error_strerror(&error);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
Containers::ScopeGuard zip_guard{zip, zip_close};
|
||||
|
||||
for(const std::string& file : backup.includedFiles) {
|
||||
FILE* out = std::fopen(Utility::Directory::join(_saveDirectory, file).c_str(), "wb");
|
||||
for(Containers::StringView file : backup.includedFiles) {
|
||||
FILE* out = std::fopen(Utility::Path::join(_saveDirectory, file).data(), "wb");
|
||||
if(out == nullptr) {
|
||||
_lastError = Utility::formatString(error_format, file, std::strerror(errno));
|
||||
_lastError = Utility::format(error_format.data(), file, std::strerror(errno));
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
Containers::ScopeGuard out_guard{out, std::fclose};
|
||||
|
||||
zip_file_t* zf = zip_fopen(zip, file.c_str(), ZIP_FL_ENC_GUESS);
|
||||
zip_file_t* zf = zip_fopen(zip, file.data(), ZIP_FL_ENC_GUESS);
|
||||
if(zf == nullptr) {
|
||||
_lastError = Utility::formatString(error_format, file, zip_strerror(zip));
|
||||
_lastError = Utility::format(error_format.data(), file, zip_strerror(zip));
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -334,13 +350,15 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
|
|||
Long bytes_read = 0;
|
||||
while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0) {
|
||||
if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast<std::size_t>(bytes_read)) {
|
||||
_lastError = Utility::formatString(error_format, file, "not enough bytes written.");
|
||||
_lastError = Utility::format(error_format.data(), file, "not enough bytes written.");
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(bytes_read == -1) {
|
||||
_lastError = Utility::formatString(error_format, file, "couldn't read bytes from archive.");
|
||||
_lastError = Utility::format(error_format.data(), file, "couldn't read bytes from archive.");
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -17,13 +17,17 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include "../Profile/Profile.h"
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
struct Backup {
|
||||
std::string filename;
|
||||
std::string company;
|
||||
Containers::String filename;
|
||||
Containers::String company;
|
||||
ProfileType type;
|
||||
struct {
|
||||
int year;
|
||||
|
@ -33,27 +37,24 @@ struct Backup {
|
|||
int minute;
|
||||
int second;
|
||||
} timestamp;
|
||||
std::vector<std::string> includedFiles;
|
||||
Containers::Array<Containers::String> includedFiles;
|
||||
};
|
||||
|
||||
class ProfileManager {
|
||||
public:
|
||||
ProfileManager(const std::string& base_path);
|
||||
explicit ProfileManager(Containers::StringView save_dir, Containers::StringView backup_dir);
|
||||
|
||||
auto ready() const -> bool;
|
||||
auto lastError() -> std::string const&;
|
||||
auto lastError() -> Containers::StringView;
|
||||
|
||||
auto saveDirectory() -> std::string const&;
|
||||
auto backupsDirectory() -> std::string const&;
|
||||
|
||||
auto profiles() -> std::vector<Profile> const&;
|
||||
auto profiles() -> Containers::ArrayView<Profile>;
|
||||
auto refreshProfiles() -> bool;
|
||||
|
||||
auto getProfile(std::size_t index) -> Profile*;
|
||||
auto deleteProfile(std::size_t index, bool delete_builds) -> bool;
|
||||
auto backupProfile(std::size_t index, bool backup_builds) -> bool;
|
||||
|
||||
auto backups() -> std::vector<Backup> const&;
|
||||
auto backups() -> Containers::ArrayView<Backup>;
|
||||
void refreshBackups();
|
||||
|
||||
auto deleteBackup(std::size_t index) -> bool;
|
||||
|
@ -61,11 +62,11 @@ class ProfileManager {
|
|||
|
||||
private:
|
||||
bool _ready = false;
|
||||
std::string _lastError = "";
|
||||
Containers::String _lastError;
|
||||
|
||||
std::string _saveDirectory;
|
||||
const std::string _backupsDirectory;
|
||||
Containers::StringView _saveDirectory;
|
||||
Containers::StringView _backupsDirectory;
|
||||
|
||||
std::vector<Profile> _profiles;
|
||||
std::vector<Backup> _backups;
|
||||
Containers::Array<Profile> _profiles;
|
||||
Containers::Array<Backup> _backups;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -16,33 +16,32 @@
|
|||
|
||||
#include "SaveTool.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
|
||||
#include <Corrade/Utility/FormatStl.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Utility/Unicode.h>
|
||||
|
||||
#include <Magnum/GL/DebugOutput.h>
|
||||
#include <Magnum/GL/DefaultFramebuffer.h>
|
||||
#include <Magnum/GL/Extensions.h>
|
||||
#include <Magnum/GL/Renderer.h>
|
||||
|
||||
#include <Magnum/ImGuiIntegration/Integration.h>
|
||||
#include <Magnum/ImGuiIntegration/Context.hpp>
|
||||
|
||||
#include <windef.h>
|
||||
#include <winuser.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
extern const ImVec2 center_pivot = {0.5f, 0.5f};
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#include <Corrade/Utility/Tweakable.h>
|
||||
|
||||
#define tw CORRADE_TWEAKABLE
|
||||
Utility::Tweakable tweak;
|
||||
#endif
|
||||
|
||||
|
@ -55,18 +54,7 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
tweak.enable("", "../../");
|
||||
#endif
|
||||
|
||||
if(SDL_VERSION_ATLEAST(2, 0, 5)) {
|
||||
if(SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1") == SDL_TRUE) {
|
||||
Utility::Debug{} << "Clickthrough is available.";
|
||||
}
|
||||
else {
|
||||
Utility::Debug{} << "Clickthrough is not available (hint couldn't be set).";
|
||||
}
|
||||
}
|
||||
else {
|
||||
Utility::Debug{} << "Clickthrough is not available (SDL2 is too old).";
|
||||
}
|
||||
|
||||
LOG_INFO("Configuring OpenGL renderer.");
|
||||
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||||
GL::Renderer::enable(GL::Renderer::Feature::ScissorTest);
|
||||
GL::Renderer::disable(GL::Renderer::Feature::FaceCulling);
|
||||
|
@ -76,81 +64,111 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add,
|
||||
GL::Renderer::BlendEquation::Add);
|
||||
|
||||
LOG_INFO("Configuring SDL2.");
|
||||
#if SDL_VERSION_ATLEAST(2,0,5)
|
||||
if(SDL_SetHintWithPriority(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1", SDL_HINT_OVERRIDE) == SDL_TRUE) {
|
||||
LOG_INFO("Clickthrough is enabled.");
|
||||
}
|
||||
else {
|
||||
LOG_WARNING("Clickthrough is disabled.");
|
||||
}
|
||||
#else
|
||||
LOG_WARNING_FORMAT("Clickthrough is disabled: SDL2 version is too old ({}.{}.{})",
|
||||
SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
|
||||
#endif
|
||||
|
||||
LOG_INFO("Registering custom events.");
|
||||
if((_initEventId = SDL_RegisterEvents(3)) == UnsignedInt(-1)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
"SDL_RegisterEvents() failed in SaveTool::SaveTool(). Exiting...", window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
_updateEventId = _initEventId + 1;
|
||||
_fileEventId = _initEventId + 2;
|
||||
|
||||
LOG_INFO("Initialising the timer subsystem.");
|
||||
if(SDL_InitSubSystem(SDL_INIT_TIMER) != 0) {
|
||||
LOG_ERROR(SDL_GetError());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
SDL_GetError(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
initialiseGui();
|
||||
|
||||
if((_initEventId = SDL_RegisterEvents(1)) == UnsignedInt(-1)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
"SDL_RegisterEvents failed in SaveTool::SaveTool(). Exiting...", nullptr);
|
||||
if(!initialiseToolDirectories()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
_lastError.data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!findGameDataDirectory()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
_lastError.data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
checkGameState();
|
||||
_gameCheckTimerId = SDL_AddTimer(2000,
|
||||
[](UnsignedInt interval, void* param)->UnsignedInt{
|
||||
static_cast<SaveTool*>(param)->checkGameState();
|
||||
return interval;
|
||||
}, this);
|
||||
if(_gameCheckTimerId == 0) {
|
||||
LOG_ERROR(SDL_GetError());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
initialiseConfiguration();
|
||||
|
||||
LOG_INFO("Initialising update checker.");
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
if(_checkUpdatesOnStartup) {
|
||||
_queue.addToast(Toast::Type::Default, "Checking for updates..."_s);
|
||||
_updateThread = std::thread{[this]{ checkForUpdates(); }};
|
||||
}
|
||||
|
||||
if(GL::Context::current().isExtensionSupported<GL::Extensions::KHR::debug>() &&
|
||||
GL::Context::current().detectedDriver() == GL::Context::DetectedDriver::NVidia)
|
||||
{
|
||||
GL::DebugOutput::setEnabled(GL::DebugOutput::Source::Api, GL::DebugOutput::Type::Other, {131185}, false);
|
||||
}
|
||||
|
||||
if(_skipDisclaimer) {
|
||||
_uiState = UiState::Initialising;
|
||||
_initThread = std::thread{[this]{ initialiseManager(); }};
|
||||
}
|
||||
|
||||
_timeline.start();
|
||||
}
|
||||
|
||||
SaveTool::~SaveTool() {
|
||||
LOG_INFO("Cleaning up.");
|
||||
|
||||
LOG_INFO("Shutting libcurl down.");
|
||||
curl_global_cleanup();
|
||||
|
||||
SDL_RemoveTimer(_gameCheckTimerId);
|
||||
}
|
||||
|
||||
void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
||||
const std::string&,
|
||||
const std::string& filename,
|
||||
efsw::Action action,
|
||||
std::string old_filename)
|
||||
{
|
||||
if(watch_id == _watchIDs[StagingDir] && Utility::String::endsWith(filename, ".sav")) {
|
||||
_massManager->refreshStagedMasses();
|
||||
return;
|
||||
}
|
||||
LOG_INFO("Saving the configuration.");
|
||||
|
||||
if(Utility::String::endsWith(filename, "Config.sav")) {
|
||||
return;
|
||||
}
|
||||
_conf.setValue("cheat_mode"_s, _cheatMode);
|
||||
_conf.setValue("advanced_mode"_s, _advancedMode);
|
||||
_conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup);
|
||||
_conf.setValue("skip_disclaimer"_s, _skipDisclaimer);
|
||||
_conf.setValue("swap_interval"_s, _swapInterval);
|
||||
_conf.setValue("fps_cap"_s, _fpsCap);
|
||||
|
||||
switch(action) {
|
||||
case efsw::Actions::Add:
|
||||
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
|
||||
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
|
||||
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
|
||||
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
|
||||
_massManager->refreshHangar(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case efsw::Actions::Delete:
|
||||
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
|
||||
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
|
||||
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
|
||||
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
|
||||
_massManager->refreshHangar(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case efsw::Actions::Modified:
|
||||
if(filename == _currentProfile->filename()) {
|
||||
_currentProfile->refreshValues();
|
||||
}
|
||||
else if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
|
||||
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
|
||||
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
|
||||
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
|
||||
_massManager->refreshHangar(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case efsw::Actions::Moved:
|
||||
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
|
||||
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
|
||||
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
|
||||
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
|
||||
_massManager->refreshHangar(index);
|
||||
int old_index = ((old_filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
|
||||
(old_filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
|
||||
_massManager->refreshHangar(old_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Unknown file watcher action type.", window());
|
||||
break;
|
||||
}
|
||||
_conf.save();
|
||||
|
||||
LOG_INFO("Exiting.");
|
||||
}
|
||||
|
||||
void SaveTool::drawEvent() {
|
||||
|
@ -163,13 +181,21 @@ void SaveTool::drawEvent() {
|
|||
drawImGui();
|
||||
|
||||
swapBuffers();
|
||||
|
||||
if(_swapInterval == 0 && _fpsCap < 301.0f) {
|
||||
while(_timeline.currentFrameDuration() < (1.0f / _fpsCap));
|
||||
}
|
||||
|
||||
redraw();
|
||||
|
||||
_timeline.nextFrame();
|
||||
}
|
||||
|
||||
void SaveTool::viewportEvent(ViewportEvent& event) {
|
||||
GL::defaultFramebuffer.setViewport({{}, event.framebufferSize()});
|
||||
|
||||
_imgui.relayout(event.windowSize());
|
||||
const Vector2 size = Vector2{windowSize()}/dpiScaling();
|
||||
_imgui.relayout(size, windowSize(), framebufferSize());
|
||||
}
|
||||
|
||||
void SaveTool::keyPressEvent(KeyEvent& event) {
|
||||
|
@ -207,123 +233,12 @@ void SaveTool::anyEvent(SDL_Event& event) {
|
|||
if(event.type == _initEventId) {
|
||||
initEvent(event);
|
||||
}
|
||||
else if(event.type == _updateEventId) {
|
||||
updateCheckEvent(event);
|
||||
}
|
||||
|
||||
void SaveTool::initEvent(SDL_Event& event) {
|
||||
_thread.join();
|
||||
|
||||
switch(event.user.code) {
|
||||
case InitSuccess:
|
||||
_uiState = UiState::ProfileManager;
|
||||
ImGui::CloseCurrentPopup();
|
||||
SDL_InitSubSystem(SDL_INIT_TIMER);
|
||||
_mbManager->checkGameState();
|
||||
_gameCheckTimerId = SDL_AddTimer(2000,
|
||||
[](UnsignedInt interval, void* param)->UnsignedInt{
|
||||
static_cast<MassBuilderManager*>(param)->checkGameState();
|
||||
return interval;
|
||||
},
|
||||
_mbManager.get());
|
||||
if(_gameCheckTimerId == 0) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
else if(event.type == _fileEventId) {
|
||||
fileUpdateEvent(event);
|
||||
}
|
||||
break;
|
||||
case MbManagerFailure:
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising MassBuilderManager", _mbManager->lastError().c_str(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
case ProfileManagerFailure:
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().c_str(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::initialiseGui() {
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf");
|
||||
ImFontConfig font_config;
|
||||
font_config.FontDataOwnedByAtlas = false;
|
||||
std::strcpy(font_config.Name, "Source Sans Pro");
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(reg_font.data()), reg_font.size(), 20.0f, &font_config);
|
||||
|
||||
auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS);
|
||||
static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
|
||||
ImFontConfig icon_config;
|
||||
icon_config.FontDataOwnedByAtlas = false;
|
||||
icon_config.MergeMode = true;
|
||||
icon_config.PixelSnapH = true;
|
||||
icon_config.OversampleH = icon_config.OversampleV = 1;
|
||||
icon_config.GlyphMinAdvanceX = 18.0f;
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(icon_font.data()), icon_font.size(), 16.0f, &icon_config, icon_range);
|
||||
|
||||
auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB);
|
||||
static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 };
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(brand_font.data()), brand_font.size(), 16.0f, &icon_config, brand_range);
|
||||
|
||||
auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf");
|
||||
ImVector<ImWchar> range;
|
||||
ImFontGlyphRangesBuilder builder;
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
||||
builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences.
|
||||
builder.BuildRanges(&range);
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(mono_font.data()), mono_font.size(), 18.0f, &font_config, range.Data);
|
||||
|
||||
_imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize());
|
||||
|
||||
io.IniFilename = nullptr;
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
style.WindowTitleAlign = {0.5f, 0.5f};
|
||||
style.FrameRounding = 3.2f;
|
||||
style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f);
|
||||
}
|
||||
|
||||
void SaveTool::initialiseManager() {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = _initEventId;
|
||||
|
||||
_mbManager.emplace();
|
||||
if(!_mbManager->ready()) {
|
||||
event.user.code = MbManagerFailure;
|
||||
SDL_PushEvent(&event);
|
||||
return;
|
||||
}
|
||||
|
||||
_profileManager.emplace(_mbManager->saveDirectory());
|
||||
if(!_profileManager->ready()) {
|
||||
event.user.code = ProfileManagerFailure;
|
||||
SDL_PushEvent(&event);
|
||||
return;
|
||||
}
|
||||
|
||||
event.user.code = InitSuccess;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void SaveTool::initialiseMassManager() {
|
||||
_currentProfile->refreshValues();
|
||||
|
||||
_massManager.emplace(_profileManager->saveDirectory(),
|
||||
_currentProfile->steamId(),
|
||||
_currentProfile->type() == ProfileType::Demo);
|
||||
|
||||
initialiseFileWatcher();
|
||||
}
|
||||
|
||||
void SaveTool::initialiseFileWatcher() {
|
||||
_fileWatcher.emplace();
|
||||
_watchIDs[SaveDir] = _fileWatcher->addWatch(_profileManager->saveDirectory(), this, false);
|
||||
_watchIDs[StagingDir] = _fileWatcher->addWatch(_massManager->stagingAreaDirectory(), this, false);
|
||||
_fileWatcher->watch();
|
||||
}
|
||||
|
||||
void SaveTool::drawImGui() {
|
||||
|
@ -359,6 +274,9 @@ void SaveTool::drawGui() {
|
|||
case UiState::MainManager:
|
||||
drawManager();
|
||||
break;
|
||||
case UiState::MassViewer:
|
||||
drawMassViewer();
|
||||
break;
|
||||
}
|
||||
|
||||
if(_aboutPopup) {
|
||||
|
@ -378,13 +296,15 @@ void SaveTool::drawGui() {
|
|||
ImGui::ShowMetricsWindow(&_metricsWindow);
|
||||
}
|
||||
#endif
|
||||
|
||||
_queue.draw(windowSize());
|
||||
}
|
||||
|
||||
void SaveTool::drawDisclaimer() {
|
||||
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
|
||||
|
||||
if(ImGui::Begin("Disclaimer##DisclaimerWindow", nullptr,
|
||||
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|
|
||||
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoBringToFrontOnFocus|
|
||||
ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
|
@ -394,11 +314,11 @@ void SaveTool::drawDisclaimer() {
|
|||
|
||||
ImGui::TextUnformatted("Before you start using the app, there are a few things you should know:");
|
||||
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.67f);
|
||||
ImGui::PushTextWrapPos(float(windowSize().x()) * 0.67f);
|
||||
|
||||
ImGui::Bullet();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted("For this application to work properly, it is recommended to disable Steam Cloud syncing for the game. To disable it, right-click the game in your Steam library, click \"Properties\", go to the \"General\" tab, and uncheck \"Keep game saves in the Steam Cloud for M.A.S.S. Builder\".");
|
||||
ImGui::TextUnformatted(R"(For this application to work properly, it is recommended to disable Steam Cloud syncing for the game. To disable it, right-click the game in your Steam library, click "Properties", go to the "General" tab, and uncheck "Keep game saves in the Steam Cloud for M.A.S.S. Builder".)");
|
||||
|
||||
ImGui::Bullet();
|
||||
ImGui::SameLine();
|
||||
|
@ -420,11 +340,16 @@ void SaveTool::drawDisclaimer() {
|
|||
ImGui::TableSetupColumn("##Empty2", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Dummy({0.0f, 5.0f});
|
||||
ImGui::Dummy({4.0f, 0.0f});
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Don't show next time", &_skipDisclaimer);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {24.0f, 12.0f});
|
||||
if(ImGui::Button("I understand the risks")) {
|
||||
_uiState = UiState::Initialising;
|
||||
_thread = std::thread{[this]{ initialiseManager(); }};
|
||||
_initThread = std::thread{[this]{ initialiseManager(); }};
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
|
@ -451,7 +376,7 @@ void SaveTool::drawGameState() {
|
|||
ImGui::TextUnformatted("Game state:");
|
||||
ImGui::SameLine();
|
||||
{
|
||||
switch(_mbManager->gameState()) {
|
||||
switch(_gameState) {
|
||||
case GameState::Unknown:
|
||||
ImGui::TextColored(ImColor{0xff00a5ff}, ICON_FA_CIRCLE);
|
||||
drawTooltip("unknown");
|
||||
|
@ -468,18 +393,18 @@ void SaveTool::drawGameState() {
|
|||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawHelpMarker(const char* text, Float wrap_pos) {
|
||||
void SaveTool::drawHelpMarker(Containers::StringView text, Float wrap_pos) {
|
||||
ImGui::TextUnformatted(ICON_FA_QUESTION_CIRCLE);
|
||||
drawTooltip(text, wrap_pos);
|
||||
}
|
||||
|
||||
void SaveTool::drawTooltip(const char* text, Float wrap_pos) {
|
||||
void SaveTool::drawTooltip(Containers::StringView text, Float wrap_pos) {
|
||||
if(ImGui::IsItemHovered()){
|
||||
ImGui::BeginTooltip();
|
||||
if(wrap_pos > 0.0f) {
|
||||
ImGui::PushTextWrapPos(wrap_pos);
|
||||
}
|
||||
ImGui::TextUnformatted(text);
|
||||
ImGui::TextUnformatted(text.data());
|
||||
if(wrap_pos > 0.0f) {
|
||||
ImGui::PopTextWrapPos();
|
||||
}
|
||||
|
@ -487,18 +412,28 @@ void SaveTool::drawTooltip(const char* text, Float wrap_pos) {
|
|||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawUnsafeText(const char* text, ...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
|
||||
ImGui::TextDisabledV(text, args);
|
||||
}
|
||||
else {
|
||||
ImGui::TextV(text, args);
|
||||
}
|
||||
va_end(args);
|
||||
void SaveTool::openUri(Containers::StringView uri) {
|
||||
ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri.data()), nullptr, nullptr, SW_SHOWDEFAULT);
|
||||
}
|
||||
|
||||
void SaveTool::openUri(const std::string& uri) {
|
||||
ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri).c_str(), nullptr, nullptr, SW_SHOW);
|
||||
void SaveTool::checkGameState() {
|
||||
WTS_PROCESS_INFOW* process_infos = nullptr;
|
||||
unsigned long process_count = 0;
|
||||
|
||||
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
|
||||
Containers::ScopeGuard guard{process_infos, WTSFreeMemory};
|
||||
|
||||
for(unsigned long i = 0; i < process_count; ++i) {
|
||||
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
|
||||
_gameState = GameState::Running;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
_gameState = GameState::NotRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_gameState = GameState::Unknown;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -16,26 +16,38 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <Corrade/Containers/Pointer.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
#include <Corrade/Utility/Configuration.h>
|
||||
#include <Corrade/Utility/Resource.h>
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#include <Corrade/Utility/Tweakable.h>
|
||||
#endif
|
||||
|
||||
#include <Magnum/Timeline.h>
|
||||
#include <Magnum/Platform/Sdl2Application.h>
|
||||
#include <Magnum/ImGuiIntegration/Context.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL_timer.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#include <efsw/efsw.hpp>
|
||||
|
||||
#include "../MassBuilderManager/MassBuilderManager.h"
|
||||
#include "../ProfileManager/ProfileManager.h"
|
||||
#include "../MassManager/MassManager.h"
|
||||
#include "../ToastQueue/ToastQueue.h"
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#define tw CORRADE_TWEAKABLE
|
||||
#endif
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Containers::Literals;
|
||||
using namespace Magnum;
|
||||
|
||||
class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener {
|
||||
|
@ -68,14 +80,32 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
|
||||
enum InitStatus: Int {
|
||||
InitSuccess,
|
||||
MbManagerFailure,
|
||||
ProfileManagerFailure
|
||||
};
|
||||
void initEvent(SDL_Event& event);
|
||||
|
||||
enum UpdateCheckStatus : Int {
|
||||
CurlInitFailed = 0,
|
||||
CurlError = 1,
|
||||
CurlTimeout = 2,
|
||||
};
|
||||
void updateCheckEvent(SDL_Event& event);
|
||||
|
||||
enum FileEventType: Int {
|
||||
FileAdded = efsw::Action::Add,
|
||||
FileDeleted = efsw::Action::Delete,
|
||||
FileModified = efsw::Action::Modified,
|
||||
FileMoved = efsw::Action::Moved,
|
||||
StagedUpdate = 1 << 3
|
||||
};
|
||||
void fileUpdateEvent(SDL_Event& event);
|
||||
|
||||
// Initialisation methods
|
||||
void initialiseConfiguration();
|
||||
void initialiseGui();
|
||||
void initialiseManager();
|
||||
auto initialiseToolDirectories() -> bool;
|
||||
auto findGameDataDirectory() -> bool;
|
||||
void initialiseMassManager();
|
||||
void initialiseFileWatcher();
|
||||
|
||||
|
@ -85,49 +115,90 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
void drawMainMenu();
|
||||
void drawDisclaimer();
|
||||
void drawInitialisation();
|
||||
|
||||
void drawProfileManager();
|
||||
auto drawBackupListPopup() -> ImGuiID;
|
||||
auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID;
|
||||
auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID;
|
||||
|
||||
void drawManager();
|
||||
auto drawIntEditPopup(int* value_to_edit, int max) -> bool;
|
||||
auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool;
|
||||
void drawGeneralInfo();
|
||||
void drawResearchInventory();
|
||||
template<typename Getter, typename Setter>
|
||||
void drawMaterialRow(Containers::StringView name, Int tier, Getter getter, Setter setter);
|
||||
void drawUnavailableMaterialRow(Containers::StringView name, Int tier);
|
||||
void drawMassManager();
|
||||
auto drawDeleteMassPopup(int mass_index) -> ImGuiID;
|
||||
auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID;
|
||||
auto drawDeleteStagedMassPopup(Containers::StringView filename) -> ImGuiID;
|
||||
|
||||
void drawMassViewer();
|
||||
void drawFrameInfo();
|
||||
void drawJointSliders();
|
||||
void drawFrameStyles();
|
||||
void drawEyeColourPicker();
|
||||
void drawCustomFrameStyles();
|
||||
void drawArmour();
|
||||
void drawCustomArmourStyles();
|
||||
void drawWeapons();
|
||||
void drawWeaponCategory(Containers::StringView name, Containers::ArrayView<Weapon> weapons_view, bool& dirty,
|
||||
Containers::StringView payload_type, Containers::StringView payload_tooltip);
|
||||
void drawWeaponEditor(Weapon& weapon);
|
||||
void drawGlobalStyles();
|
||||
void drawTuning();
|
||||
void drawDecalEditor(Decal& decal);
|
||||
void drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<CustomStyle> style_view);
|
||||
auto getStyleName(Int id, Containers::ArrayView<CustomStyle> view) -> Containers::StringView;
|
||||
|
||||
enum DCSResult {
|
||||
DCS_Fail,
|
||||
DCS_ResetStyle,
|
||||
DCS_Save
|
||||
};
|
||||
auto drawCustomStyle(CustomStyle& style) -> DCSResult;
|
||||
|
||||
void drawAbout();
|
||||
void drawGameState();
|
||||
|
||||
// Convenience wrappers over ImGui stuff
|
||||
void drawHelpMarker(const char* text, Float wrap_pos = 0.0f);
|
||||
void drawTooltip(const char* text, Float wrap_pos = 0.0f);
|
||||
void drawHelpMarker(Containers::StringView text, Float wrap_pos = 0.0f);
|
||||
void drawTooltip(Containers::StringView text, Float wrap_pos = 0.0f);
|
||||
|
||||
template<typename Functor, typename... Args>
|
||||
auto drawUnsafeWidget(Functor func, Args... args) -> bool {
|
||||
GameState game_state = _mbManager->gameState();
|
||||
if(!_unsafeMode && game_state != GameState::NotRunning) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
|
||||
}
|
||||
|
||||
GameState game_state = _gameState; // Copying the value to reduce the risk of a data race.
|
||||
ImGui::BeginDisabled(game_state != GameState::NotRunning);
|
||||
bool result = func(std::forward<Args>(args)...);
|
||||
|
||||
if(!_unsafeMode && game_state != GameState::NotRunning) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
return result;
|
||||
} // Obviously, should only be used with ImGui widgets that return a bool.
|
||||
// Also, func should be a lambda if there are any default arguments, like ImGui::Button(), etc...
|
||||
|
||||
void drawUnsafeText(const char* text, ...); // Alternative to the above, for ImGui::Text*() variants.
|
||||
template<typename... Args>
|
||||
void drawUnsafeText(const char* text, Args... args) { // Alternative to the above, for ImGui::Text*() variants.
|
||||
if(_gameState != GameState::NotRunning) {
|
||||
ImGui::TextDisabled(text, std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
ImGui::Text(text, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
void openUri(const std::string& uri);
|
||||
template<typename... Args>
|
||||
void drawAlignedText(Containers::StringView text, Args... args) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(text.data(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
Utility::Resource _rs{"assets"};
|
||||
void openUri(Containers::StringView uri);
|
||||
|
||||
void checkGameState();
|
||||
|
||||
void checkForUpdates();
|
||||
|
||||
Utility::Configuration _conf{"MassBuilderSaveTool.ini"_s};
|
||||
Utility::Resource _rs{"assets"_s};
|
||||
|
||||
// GUI-related members
|
||||
ImGuiIntegration::Context _imgui{NoCreate};
|
||||
|
@ -136,9 +207,9 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
Disclaimer,
|
||||
Initialising,
|
||||
ProfileManager,
|
||||
MainManager
|
||||
};
|
||||
UiState _uiState{UiState::Disclaimer};
|
||||
MainManager,
|
||||
MassViewer
|
||||
} _uiState{UiState::Disclaimer};
|
||||
|
||||
bool _aboutPopup{false};
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
|
@ -147,16 +218,42 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
bool _metricsWindow{false};
|
||||
#endif
|
||||
|
||||
std::thread _thread;
|
||||
UnsignedInt _initEventId;
|
||||
ToastQueue _queue;
|
||||
|
||||
Containers::Pointer<MassBuilderManager> _mbManager;
|
||||
SDL_TimerID _gameCheckTimerId;
|
||||
std::thread _initThread;
|
||||
std::thread _updateThread;
|
||||
|
||||
UnsignedInt _initEventId;
|
||||
UnsignedInt _updateEventId;
|
||||
UnsignedInt _fileEventId;
|
||||
|
||||
Containers::String _lastError;
|
||||
|
||||
Containers::String _gameDataDir;
|
||||
Containers::String _configDir;
|
||||
Containers::String _saveDir;
|
||||
Containers::String _screenshotsDir;
|
||||
|
||||
Containers::String _backupsDir;
|
||||
Containers::String _stagingDir;
|
||||
//Containers::String _armouryDir;
|
||||
//Containers::String _armoursDir;
|
||||
//Containers::String _weaponsDir;
|
||||
//Containers::String _stylesDir;
|
||||
|
||||
enum class GameState : UnsignedByte {
|
||||
Unknown, NotRunning, Running
|
||||
} _gameState{GameState::Unknown};
|
||||
|
||||
SDL_TimerID _gameCheckTimerId = 0;
|
||||
|
||||
Containers::Pointer<ProfileManager> _profileManager;
|
||||
Profile* _currentProfile{nullptr};
|
||||
|
||||
Containers::Pointer<MassManager> _massManager;
|
||||
Mass* _currentMass{nullptr};
|
||||
|
||||
Weapon* _currentWeapon = nullptr;
|
||||
|
||||
Containers::Pointer<efsw::FileWatcher> _fileWatcher;
|
||||
enum watchID {
|
||||
|
@ -165,5 +262,36 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
};
|
||||
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
|
||||
|
||||
bool _unsafeMode{false};
|
||||
int _swapInterval = 1;
|
||||
float _fpsCap = 60.0f;
|
||||
|
||||
bool _skipDisclaimer{false};
|
||||
bool _checkUpdatesOnStartup{true};
|
||||
|
||||
bool _updateAvailable{false};
|
||||
Containers::String _latestVersion;
|
||||
Containers::String _releaseLink;
|
||||
Containers::String _downloadLink;
|
||||
|
||||
bool _modifiedBySaveTool{false};
|
||||
bool _jointsDirty{false};
|
||||
bool _stylesDirty{false};
|
||||
bool _eyeFlareDirty{false};
|
||||
Containers::StaticArray<38, Int> _selectedArmourDecals{ValueInit};
|
||||
Containers::StaticArray<38, Int> _selectedArmourAccessories{ValueInit};
|
||||
Int _selectedBLPlacement{0};
|
||||
Int _selectedWeaponPart{0};
|
||||
Int _selectedWeaponDecal{0};
|
||||
Int _selectedWeaponAccessory{0};
|
||||
bool _meleeDirty{false};
|
||||
bool _shieldsDirty{false};
|
||||
bool _bShootersDirty{false};
|
||||
bool _eShootersDirty{false};
|
||||
bool _bLaunchersDirty{false};
|
||||
bool _eLaunchersDirty{false};
|
||||
|
||||
bool _cheatMode{false};
|
||||
bool _advancedMode{false};
|
||||
|
||||
Timeline _timeline;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
#include <Corrade/Utility/Unicode.h>
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#include <fileapi.h>
|
||||
#include <handleapi.h>
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
||||
const std::string&,
|
||||
const std::string& filename,
|
||||
efsw::Action action,
|
||||
std::string old_filename)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = _fileEventId;
|
||||
|
||||
event.user.data1 = Containers::String{Containers::AllocatedInit, filename.c_str()}.release();
|
||||
|
||||
if(watch_id == _watchIDs[StagingDir] && Utility::String::endsWith(filename, ".sav")) {
|
||||
event.user.code = StagedUpdate | action;
|
||||
SDL_PushEvent(&event);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Utility::String::endsWith(filename, "Config.sav")) {
|
||||
return;
|
||||
} // TODO: actually do something when config files will finally be handled
|
||||
|
||||
if(!Utility::String::endsWith(filename, Utility::format("Profile{}.sav", _currentProfile->account()).data())) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.user.code = action;
|
||||
if(action == efsw::Actions::Moved) {
|
||||
event.user.data2 = Containers::String{Containers::AllocatedInit, old_filename.c_str()}.release();
|
||||
}
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void SaveTool::fileUpdateEvent(SDL_Event& event) {
|
||||
Containers::String filename{static_cast<char*>(event.user.data1),
|
||||
std::strlen(static_cast<char*>(event.user.data1)), nullptr};
|
||||
|
||||
if((event.user.code & StagedUpdate) == StagedUpdate) {
|
||||
_massManager->refreshStagedMass(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
Containers::String old_filename;
|
||||
|
||||
Int index = 0;
|
||||
Int old_index = 0;
|
||||
bool is_current_profile = filename == _currentProfile->filename();
|
||||
bool is_unit = filename.hasPrefix(_currentProfile->isDemo() ? "DemoUnit"_s : "Unit"_s);
|
||||
if(is_unit) {
|
||||
index = ((filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) +
|
||||
(filename[_currentProfile->isDemo() ? 9 : 5] - 0x30);
|
||||
}
|
||||
|
||||
if(event.user.code == FileMoved) {
|
||||
old_filename = Containers::String{static_cast<char*>(event.user.data2), std::strlen(static_cast<char*>(event.user.data2)), nullptr};
|
||||
old_index = ((old_filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) +
|
||||
(old_filename[_currentProfile->isDemo() ? 9 : 5] - 0x30);
|
||||
}
|
||||
|
||||
switch(event.user.code) {
|
||||
case FileAdded:
|
||||
if(is_unit) {
|
||||
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
||||
_massManager->refreshHangar(index);
|
||||
}
|
||||
else {
|
||||
_currentMass->setDirty();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FileDeleted:
|
||||
if(is_current_profile) {
|
||||
_currentProfile = nullptr;
|
||||
_uiState = UiState::ProfileManager;
|
||||
if(!_profileManager->refreshProfiles()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else if(is_unit) {
|
||||
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
||||
_massManager->refreshHangar(index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FileModified:
|
||||
if(is_current_profile) {
|
||||
_currentProfile->refreshValues();
|
||||
}
|
||||
else if(is_unit) {
|
||||
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
||||
_massManager->refreshHangar(index);
|
||||
}
|
||||
else {
|
||||
if(_modifiedBySaveTool && _currentMass->filename() == filename) {
|
||||
auto handle = CreateFileW(Utility::Unicode::widen(Containers::StringView{filename}), GENERIC_READ, 0,
|
||||
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if(handle && handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
_modifiedBySaveTool = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_currentMass->setDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FileMoved:
|
||||
if(is_unit) {
|
||||
if(old_filename.hasSuffix(".sav"_s)) {
|
||||
_massManager->refreshHangar(index);
|
||||
_massManager->refreshHangar(old_index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_queue.addToast(Toast::Type::Warning, "Unknown file action type"_s);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Pair.h>
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
#include <Corrade/Utility/Unicode.h>
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::initEvent(SDL_Event& event) {
|
||||
_initThread.join();
|
||||
|
||||
switch(event.user.code) {
|
||||
case InitSuccess:
|
||||
_uiState = UiState::ProfileManager;
|
||||
ImGui::CloseCurrentPopup();
|
||||
break;
|
||||
case ProfileManagerFailure:
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error ",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::initialiseConfiguration() {
|
||||
LOG_INFO("Reading configuration file.");
|
||||
|
||||
if(_conf.hasValue("cheat_mode"_s)) {
|
||||
_cheatMode = _conf.value<bool>("cheat_mode"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("cheat_mode"_s, _cheatMode);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("advanced_mode"_s)) {
|
||||
_advancedMode = _conf.value<bool>("advanced_mode"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("advanced_mode"_s, _advancedMode);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("startup_update_check"_s)) {
|
||||
_checkUpdatesOnStartup = _conf.value<bool>("startup_update_check"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("skip_disclaimer"_s)) {
|
||||
_skipDisclaimer = _conf.value<bool>("skip_disclaimer"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("skip_disclaimer"_s, _skipDisclaimer);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("swap_interval"_s)) {
|
||||
_swapInterval = _conf.value<int>("swap_interval"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("swap_interval"_s, 1);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("fps_cap"_s)) {
|
||||
_fpsCap = _conf.value<float>("fps_cap");
|
||||
}
|
||||
else {
|
||||
_conf.setValue("fps_cap", 60.0f);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("frame_limit"_s)) {
|
||||
std::string frame_limit = _conf.value("frame_limit"_s);
|
||||
if(frame_limit == "half_vsync"_s) {
|
||||
_swapInterval = 2;
|
||||
}
|
||||
_conf.removeValue("frame_limit"_s);
|
||||
}
|
||||
|
||||
setSwapInterval(_swapInterval);
|
||||
if(_swapInterval == 0) {
|
||||
setMinimalLoopPeriod(0);
|
||||
}
|
||||
|
||||
_conf.save();
|
||||
}
|
||||
|
||||
void SaveTool::initialiseGui() {
|
||||
LOG_INFO("Initialising Dear ImGui.");
|
||||
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
const auto size = Vector2{windowSize()}/dpiScaling();
|
||||
|
||||
auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf"_s);
|
||||
ImFontConfig font_config;
|
||||
font_config.FontDataOwnedByAtlas = false;
|
||||
std::strcpy(font_config.Name, "Source Sans Pro");
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(reg_font.data()), int(reg_font.size()),
|
||||
20.0f * Float(framebufferSize().x()) / size.x(), &font_config);
|
||||
|
||||
auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS);
|
||||
static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
|
||||
ImFontConfig icon_config;
|
||||
icon_config.FontDataOwnedByAtlas = false;
|
||||
icon_config.MergeMode = true;
|
||||
icon_config.PixelSnapH = true;
|
||||
icon_config.OversampleH = icon_config.OversampleV = 1;
|
||||
icon_config.GlyphMinAdvanceX = 18.0f;
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(icon_font.data()), int(icon_font.size()),
|
||||
16.0f * Float(framebufferSize().x()) / size.x(), &icon_config, icon_range);
|
||||
|
||||
auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB);
|
||||
static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 };
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(brand_font.data()), int(brand_font.size()),
|
||||
16.0f * Float(framebufferSize().x()) / size.x(), &icon_config, brand_range);
|
||||
|
||||
auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf"_s);
|
||||
ImVector<ImWchar> range;
|
||||
ImFontGlyphRangesBuilder builder;
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
||||
builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences.
|
||||
builder.BuildRanges(&range);
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(mono_font.data()), int(mono_font.size()),
|
||||
18.0f * Float(framebufferSize().x()) / size.x(), &font_config, range.Data);
|
||||
|
||||
_imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize());
|
||||
|
||||
io.IniFilename = nullptr;
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
style.WindowTitleAlign = {0.5f, 0.5f};
|
||||
style.FrameRounding = 3.2f;
|
||||
style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f);
|
||||
}
|
||||
|
||||
void SaveTool::initialiseManager() {
|
||||
LOG_INFO("Initialising the profile manager.");
|
||||
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = _initEventId;
|
||||
|
||||
_profileManager.emplace(_saveDir, _backupsDir);
|
||||
if(!_profileManager->ready()) {
|
||||
event.user.code = ProfileManagerFailure;
|
||||
SDL_PushEvent(&event);
|
||||
return;
|
||||
}
|
||||
|
||||
event.user.code = InitSuccess;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
auto SaveTool::initialiseToolDirectories() -> bool {
|
||||
LOG_INFO("Initialising Save Tool directories.");
|
||||
|
||||
_backupsDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "backups");
|
||||
_stagingDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "staging");
|
||||
//_armouryDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "armoury");
|
||||
//_armoursDir = Utility::Directory::join(_armouryDir, "armours");
|
||||
//_weaponsDir = Utility::Directory::join(_armouryDir, "weapons");
|
||||
//_stylesDir = Utility::Directory::join(_armouryDir, "styles");
|
||||
|
||||
if(!Utility::Path::exists(_backupsDir)) {
|
||||
LOG_WARNING("Backups directory not found, creating...");
|
||||
if(!Utility::Path::make(_backupsDir)) {
|
||||
LOG_ERROR(_lastError = "Couldn't create the backups directory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Utility::Path::exists(_stagingDir)) {
|
||||
LOG_WARNING("Staging directory not found, creating...");
|
||||
if(!Utility::Path::make(_stagingDir)) {
|
||||
LOG_ERROR(_lastError = "Couldn't create the staging directory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//if(!Utility::Directory::exists(_armouryDir)) {
|
||||
// Utility::Debug{} << "Armoury directory not found, creating...";
|
||||
// if(!Utility::Path::make(_armouryDir)) {
|
||||
// Utility::Error{} << (_lastError = "Couldn't create the armoury directory.");
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if(!Utility::Directory::exists(_armoursDir)) {
|
||||
// Utility::Debug{} << "Armours directory not found, creating...";
|
||||
// if(!Utility::Path::make(_armoursDir)) {
|
||||
// Utility::Error{} << (_lastError = "Couldn't create the armours directory.");
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if(!Utility::Directory::exists(_weaponsDir)) {
|
||||
// Utility::Debug{} << "Weapons directory not found, creating...";
|
||||
// if(!Utility::Path::make(_weaponsDir)) {
|
||||
// Utility::Error{} << (_lastError = "Couldn't create the weapons directory.");
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if(!Utility::Directory::exists(_stylesDir)) {
|
||||
// Utility::Debug{} << "Styles directory not found, creating...";
|
||||
// if(!Utility::Path::make(_stylesDir)) {
|
||||
// Utility::Error{} << (_lastError = "Couldn't create the styles directory.");
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SaveTool::findGameDataDirectory() -> bool {
|
||||
LOG_INFO("Searching for the game's save directory.");
|
||||
|
||||
wchar_t* localappdata_path = nullptr;
|
||||
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
|
||||
if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK)
|
||||
{
|
||||
_lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}.", GetLastError());
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
_gameDataDir = Utility::Path::join(Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder"_s);
|
||||
|
||||
if(!Utility::Path::exists(_gameDataDir)) {
|
||||
LOG_ERROR(_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
_configDir = Utility::Path::join(_gameDataDir, "Saved/Config/WindowsNoEditor"_s);
|
||||
_saveDir = Utility::Path::join(_gameDataDir, "Saved/SaveGames"_s);
|
||||
_screenshotsDir = Utility::Path::join(_gameDataDir, "Saved/Screenshots/WindowsNoEditor"_s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveTool::initialiseMassManager() {
|
||||
LOG_INFO("Initialising the M.A.S.S. manager.");
|
||||
_massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir);
|
||||
}
|
||||
|
||||
void SaveTool::initialiseFileWatcher() {
|
||||
LOG_INFO("Initialising the file watcher.");
|
||||
_fileWatcher.emplace();
|
||||
_watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false);
|
||||
_watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false);
|
||||
_fileWatcher->watch();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -14,20 +14,18 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <Corrade/Containers/Reference.h>
|
||||
#include <Corrade/Utility/FormatStl.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "../Maps/LastMissionId.h"
|
||||
#include "../Maps/StoryProgress.h"
|
||||
|
||||
static const std::string empty_str;
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawManager() {
|
||||
ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always);
|
||||
|
@ -41,34 +39,19 @@ void SaveTool::drawManager() {
|
|||
return;
|
||||
}
|
||||
|
||||
if(ImGui::BeginTable("##TopRow", 2)) {
|
||||
ImGui::TableSetupColumn("##ProfileInfo", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##Unsafe", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Current profile: %s (%s)",
|
||||
_currentProfile->companyName().c_str(),
|
||||
_currentProfile->type() == ProfileType::Demo ? "demo" : "full game");
|
||||
drawAlignedText("Current profile: %s (%s)",
|
||||
_currentProfile->companyName().data(),
|
||||
_currentProfile->isDemo() ? "demo" : "full game");
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Close")) {
|
||||
if(ImGui::Button(ICON_FA_ARROW_LEFT " Back to profile manager")) {
|
||||
_currentProfile = nullptr;
|
||||
_massManager.reset();
|
||||
_fileWatcher.reset();
|
||||
_uiState = UiState::ProfileManager;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Checkbox("Unsafe mode", &_unsafeMode);
|
||||
drawTooltip("Enabling this allows interactions deemed to be unsafe to do while the game is running.",
|
||||
Float(windowSize().x()) * 0.35f);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if(ImGui::BeginChild("##ProfileInfo",
|
||||
{ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f},
|
||||
{ImGui::GetContentRegionAvail().x * 0.60f, 0.0f},
|
||||
true, ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
|
@ -120,8 +103,8 @@ auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool {
|
|||
drawHelpMarker("You can either drag the widget left or right to change the value,\n"
|
||||
"or click on it while holding Ctrl to edit the value directly.");
|
||||
ImGui::SameLine();
|
||||
drawUnsafeWidget([](auto... args){ return ImGui::DragInt("", args...); },
|
||||
value_to_edit, 1.0f, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp);
|
||||
drawUnsafeWidget([](auto... args){ return ImGui::SliderInt("", args...); },
|
||||
value_to_edit, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp);
|
||||
ImGui::SameLine();
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button("Apply"); })) {
|
||||
apply = true;
|
||||
|
@ -161,13 +144,12 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView<char> name_view) -> bool {
|
|||
ImGuiInputTextFlags_CallbackCharFilter,
|
||||
callback, nullptr);
|
||||
ImGui::SameLine();
|
||||
GameState game_state = _mbManager->gameState();
|
||||
if((!_unsafeMode && game_state != GameState::NotRunning) ||
|
||||
GameState game_state = _gameState;
|
||||
if(game_state != GameState::NotRunning ||
|
||||
!(len >= 6 && len <= 32) ||
|
||||
!(name_view[0] != ' ' && name_view[len - 1] != ' '))
|
||||
{
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if(ImGui::Button("Apply")) {
|
||||
|
@ -175,12 +157,11 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView<char> name_view) -> bool {
|
|||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if((!_unsafeMode && game_state != GameState::NotRunning) ||
|
||||
if(game_state != GameState::NotRunning ||
|
||||
!(len >= 6 && len <= 32) ||
|
||||
!(name_view[0] != ' ' && name_view[len - 1] != ' '))
|
||||
{
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
|
@ -201,11 +182,11 @@ void SaveTool::drawGeneralInfo() {
|
|||
{
|
||||
ImGui::TextUnformatted("Story progress:");
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.x / 4.0f);
|
||||
if(std::strcmp(it->after, "") == 0) {
|
||||
ImGui::TextWrapped("%s - %s", it->chapter, it->point);
|
||||
if(!it->after) {
|
||||
ImGui::TextWrapped("%s - %s", it->chapter.data(), it->point.data());
|
||||
}
|
||||
else {
|
||||
ImGui::TextWrapped("%s - %s - %s", it->chapter, it->after, it->point);
|
||||
ImGui::TextWrapped("%s - %s - %s", it->chapter.data(), it->after.data(), it->point.data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -213,7 +194,7 @@ void SaveTool::drawGeneralInfo() {
|
|||
}
|
||||
|
||||
if(mission_id_map.find(_currentProfile->lastMissionId()) != mission_id_map.cend()) {
|
||||
ImGui::Text("Last mission: %s", mission_id_map.at(_currentProfile->lastMissionId()));
|
||||
ImGui::Text("Last mission: %s", mission_id_map.at(_currentProfile->lastMissionId()).data());
|
||||
}
|
||||
else if(_currentProfile->lastMissionId() == -1) {
|
||||
ImGui::TextUnformatted("Last mission: none");
|
||||
|
@ -222,7 +203,7 @@ void SaveTool::drawGeneralInfo() {
|
|||
ImGui::Text("Last mission: 0x%x", _currentProfile->lastMissionId());
|
||||
}
|
||||
drawTooltip("This is the last mission selected in the mission selection screen, not the last mission played.",
|
||||
windowSize().x() * 0.35f);
|
||||
float(windowSize().x()) * 0.35f);
|
||||
|
||||
const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||
ImGui::Dummy({ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - footer_height_to_reserve});
|
||||
|
@ -234,16 +215,19 @@ void SaveTool::drawGeneralInfo() {
|
|||
for(auto& c : name_buf) {
|
||||
c = '\0';
|
||||
}
|
||||
std::strncpy(name_buf.data(), _currentProfile->companyName().c_str(), 32);
|
||||
std::strncpy(name_buf.data(), _currentProfile->companyName().data(), 32);
|
||||
ImGui::OpenPopup("name_edit");
|
||||
}
|
||||
if(drawRenamePopup(name_buf)) {
|
||||
if(!_currentProfile->renameCompany(name_buf.data())) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_currentProfile->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
|
||||
}
|
||||
}
|
||||
|
||||
if(!_cheatMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
static Int credits;
|
||||
|
@ -253,8 +237,7 @@ void SaveTool::drawGeneralInfo() {
|
|||
}
|
||||
if(drawIntEditPopup(&credits, 20000000)) {
|
||||
if(!_currentProfile->setCredits(credits)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_currentProfile->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,25 +248,23 @@ void SaveTool::drawGeneralInfo() {
|
|||
}
|
||||
drawTooltip("Story progress directly affects unlocked levels.");
|
||||
if(ImGui::BeginPopup("StoryProgressMenu")) {
|
||||
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
|
||||
if(_gameState != GameState::NotRunning) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
for(const auto& sp : story_progress) {
|
||||
if(ImGui::BeginMenu(sp.chapter)) {
|
||||
if(std::strcmp(sp.after, "") == 0) {
|
||||
if(ImGui::MenuItem(sp.point)) {
|
||||
if(ImGui::BeginMenu(sp.chapter.data())) {
|
||||
if(!sp.after) {
|
||||
if(ImGui::MenuItem(sp.point.data())) {
|
||||
if(!_currentProfile->setStoryProgress(sp.id)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_currentProfile->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(ImGui::BeginMenu(sp.after)) {
|
||||
if(ImGui::MenuItem(sp.point)) {
|
||||
if(ImGui::BeginMenu(sp.after.data())) {
|
||||
if(ImGui::MenuItem(sp.point.data())) {
|
||||
if(!_currentProfile->setStoryProgress(sp.id)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_currentProfile->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
|
@ -301,99 +282,157 @@ void SaveTool::drawResearchInventory() {
|
|||
return;
|
||||
}
|
||||
|
||||
#define unavRow(name) \
|
||||
ImGui::TableNextRow(); \
|
||||
ImGui::TableSetColumnIndex(0); \
|
||||
ImGui::TextUnformatted(name); \
|
||||
ImGui::TableSetColumnIndex(1); \
|
||||
ImGui::TextDisabled("Unavailable as of M.A.S.S. Builder version " SUPPORTED_GAME_VERSION);
|
||||
|
||||
#define matRow(name, var, getter, setter) \
|
||||
ImGui::TableNextRow(); \
|
||||
ImGui::TableSetColumnIndex(0); \
|
||||
ImGui::TextUnformatted((name)); \
|
||||
ImGui::TableSetColumnIndex(1); \
|
||||
if(_currentProfile->getter() != -1) { \
|
||||
drawUnsafeText("%i", _currentProfile->getter()); \
|
||||
ImGui::TableSetColumnIndex(2); \
|
||||
ImGui::PushID(#setter); \
|
||||
static Int var = _currentProfile->getter(); \
|
||||
if(drawUnsafeWidget([]{ return ImGui::SmallButton(ICON_FA_EDIT); })) { \
|
||||
(var) = _currentProfile->getter(); \
|
||||
ImGui::OpenPopup("int_edit"); \
|
||||
} \
|
||||
if(drawIntEditPopup(&(var), 9999)) { \
|
||||
if(!_currentProfile->set##setter((var))) { \
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", \
|
||||
_currentProfile->lastError().c_str(), window()); \
|
||||
} \
|
||||
} \
|
||||
ImGui::PopID(); \
|
||||
} \
|
||||
else { \
|
||||
ImGui::TextDisabled("Not found in the save file"); \
|
||||
}
|
||||
|
||||
if(ImGui::BeginTable("##ResearchInventoryTable", 3,
|
||||
ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_RowBg))
|
||||
if(ImGui::BeginTable("##ResearchInventoryTable", 4,
|
||||
ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersInnerH))
|
||||
{
|
||||
ImGui::TableSetupColumn("##Tier", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("##Name", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("##Value", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##Edit", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("Engine materials");
|
||||
|
||||
matRow("Verse steel", verse_steel, verseSteel, VerseSteel)
|
||||
matRow("Undinium", undinium, undinium, Undinium)
|
||||
matRow("Necrium alloy", necrium_alloy, necriumAlloy, NecriumAlloy)
|
||||
matRow("Lunarite", lunarite, lunarite, Lunarite)
|
||||
matRow("Asterite", asterite, asterite, Asterite)
|
||||
unavRow("Hallite fragma")
|
||||
unavRow("Unnoctinium")
|
||||
drawMaterialRow("Verse steel", 1,
|
||||
[this]{ return _currentProfile->verseSteel(); },
|
||||
[this](Int amount){ return _currentProfile->setVerseSteel(amount); });
|
||||
drawMaterialRow("Undinium", 2,
|
||||
[this]{ return _currentProfile->undinium(); },
|
||||
[this](Int amount){ return _currentProfile->setUndinium(amount); });
|
||||
drawMaterialRow("Necrium alloy", 3,
|
||||
[this]{ return _currentProfile->necriumAlloy(); },
|
||||
[this](Int amount){ return _currentProfile->setNecriumAlloy(amount); });
|
||||
drawMaterialRow("Lunarite", 4,
|
||||
[this]{ return _currentProfile->lunarite(); },
|
||||
[this](Int amount){ return _currentProfile->setLunarite(amount); });
|
||||
drawMaterialRow("Asterite", 5,
|
||||
[this]{ return _currentProfile->asterite(); },
|
||||
[this](Int amount){ return _currentProfile->setAsterite(amount); });
|
||||
drawMaterialRow("Hallite fragma", 6,
|
||||
[this]{ return _currentProfile->halliteFragma(); },
|
||||
[this](Int amount){ return _currentProfile->setHalliteFragma(amount); });
|
||||
drawUnavailableMaterialRow("Unnoctinium", 7);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("OS materials");
|
||||
|
||||
matRow("Ednil", ednil, ednil, Ednil)
|
||||
matRow("Nuflalt", nuflalt, nuflalt, Nuflalt)
|
||||
matRow("Aurelene", aurelene, aurelene, Aurelene)
|
||||
matRow("Soldus", soldus, soldus, Soldus)
|
||||
matRow("Synthesized N", synthesized_n, synthesizedN, SynthesizedN)
|
||||
unavRow("Nanoc")
|
||||
unavRow("Abyssillite")
|
||||
drawMaterialRow("Ednil", 1,
|
||||
[this]{ return _currentProfile->ednil(); },
|
||||
[this](Int amount){ return _currentProfile->setEdnil(amount); });
|
||||
drawMaterialRow("Nuflalt", 2,
|
||||
[this]{ return _currentProfile->nuflalt(); },
|
||||
[this](Int amount){ return _currentProfile->setNuflalt(amount); });
|
||||
drawMaterialRow("Aurelene", 3,
|
||||
[this]{ return _currentProfile->aurelene(); },
|
||||
[this](Int amount){ return _currentProfile->setAurelene(amount); });
|
||||
drawMaterialRow("Soldus", 4,
|
||||
[this]{ return _currentProfile->soldus(); },
|
||||
[this](Int amount){ return _currentProfile->setSoldus(amount); });
|
||||
drawMaterialRow("Synthesized N", 5,
|
||||
[this]{ return _currentProfile->synthesisedN(); },
|
||||
[this](Int amount){ return _currentProfile->setSynthesisedN(amount); });
|
||||
drawMaterialRow("Nanoc", 6,
|
||||
[this]{ return _currentProfile->nanoc(); },
|
||||
[this](Int amount){ return _currentProfile->setNanoc(amount); });
|
||||
drawUnavailableMaterialRow("Abyssillite", 7);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("Architect materials");
|
||||
|
||||
matRow("Alcarbonite", alcarbonite, alcarbonite, Alcarbonite)
|
||||
matRow("Keriphene", keriphene, keriphene, Keriphene)
|
||||
matRow("Nitinol-CM", nitinol_cm, nitinolCM, NitinolCM)
|
||||
matRow("Quarkium", quarkium, quarkium, Quarkium)
|
||||
matRow("Alterene", alterene, alterene, Alterene)
|
||||
unavRow("Cosmium")
|
||||
unavRow("Purified quarkium")
|
||||
drawMaterialRow("Alcarbonite", 1,
|
||||
[this]{ return _currentProfile->alcarbonite(); },
|
||||
[this](Int amount){ return _currentProfile->setAlcarbonite(amount); });
|
||||
drawMaterialRow("Keripehene", 2,
|
||||
[this]{ return _currentProfile->keriphene(); },
|
||||
[this](Int amount){ return _currentProfile->setKeriphene(amount); });
|
||||
drawMaterialRow("Nitinol-CM", 3,
|
||||
[this]{ return _currentProfile->nitinolCM(); },
|
||||
[this](Int amount){ return _currentProfile->setNitinolCM(amount); });
|
||||
drawMaterialRow("Quarkium", 4,
|
||||
[this]{ return _currentProfile->quarkium(); },
|
||||
[this](Int amount){ return _currentProfile->setQuarkium(amount); });
|
||||
drawMaterialRow("Alterene", 5,
|
||||
[this]{ return _currentProfile->alterene(); },
|
||||
[this](Int amount){ return _currentProfile->setAlterene(amount); });
|
||||
drawMaterialRow("Cosmium", 6,
|
||||
[this]{ return _currentProfile->cosmium(); },
|
||||
[this](Int amount){ return _currentProfile->setCosmium(amount); });
|
||||
drawUnavailableMaterialRow("Purified quarkium", 7);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("Quark data");
|
||||
|
||||
matRow("Mixed composition", mixed_composition, mixedComposition, MixedComposition)
|
||||
matRow("Void residue", void_residue, voidResidue, VoidResidue)
|
||||
matRow("Muscular construction", muscular_construction, muscularConstruction, MuscularConstruction)
|
||||
matRow("Mineral exoskeletology", mineral_exoskeletology, mineralExoskeletology, MineralExoskeletology)
|
||||
matRow("Carbonized skin", carbonized_skin, carbonizedSkin, CarbonizedSkin)
|
||||
unavRow("Isolated void particle")
|
||||
unavRow("Weaponised physiology")
|
||||
drawMaterialRow("Mixed composition", 1,
|
||||
[this]{ return _currentProfile->mixedComposition(); },
|
||||
[this](Int amount){ return _currentProfile->setMixedComposition(amount); });
|
||||
drawMaterialRow("Void residue", 2,
|
||||
[this]{ return _currentProfile->voidResidue(); },
|
||||
[this](Int amount){ return _currentProfile->setVoidResidue(amount); });
|
||||
drawMaterialRow("Muscular construction", 3,
|
||||
[this]{ return _currentProfile->muscularConstruction(); },
|
||||
[this](Int amount){ return _currentProfile->setMuscularConstruction(amount); });
|
||||
drawMaterialRow("Mineral exoskeletology", 4,
|
||||
[this]{ return _currentProfile->mineralExoskeletology(); },
|
||||
[this](Int amount){ return _currentProfile->setMineralExoskeletology(amount); });
|
||||
drawMaterialRow("Carbonized skin", 5,
|
||||
[this]{ return _currentProfile->carbonisedSkin(); },
|
||||
[this](Int amount){ return _currentProfile->setCarbonisedSkin(amount); });
|
||||
drawMaterialRow("Isolated void particle", 6,
|
||||
[this]{ return _currentProfile->isolatedVoidParticle(); },
|
||||
[this](Int amount){ return _currentProfile->setIsolatedVoidParticle(amount); });
|
||||
drawUnavailableMaterialRow("Weaponised physiology", 7);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
#undef unavRow
|
||||
#undef matRow
|
||||
template<typename Getter, typename Setter>
|
||||
void SaveTool::drawMaterialRow(Containers::StringView name, Int tier, Getter getter, Setter setter) {
|
||||
static_assert(std::is_same<decltype(getter()), Int>::value, "getter doesn't return an Int, and/or doesn't take zero arguments.");
|
||||
static_assert(std::is_same<decltype(setter(0)), bool>::value, "setter doesn't return a bool, and/or doesn't take a single Int as an argument.");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("T%i", tier);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TextUnformatted(name.data());
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
if(getter() != -1) {
|
||||
ImGui::Text("%i", getter());
|
||||
if(_cheatMode) {
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::PushID(name.data());
|
||||
static Int var = 0;
|
||||
if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_EDIT)) {
|
||||
(var) = getter();
|
||||
ImGui::OpenPopup("int_edit");
|
||||
}
|
||||
drawTooltip("Edit");
|
||||
if(drawIntEditPopup(&(var), 9999)) {
|
||||
if(!setter(var)) {
|
||||
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImGui::TextDisabled("Not found in the save file");
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawUnavailableMaterialRow(Containers::StringView name, Int tier) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("T%i", tier);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TextUnformatted(name.data());
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::TextDisabled("Unavailable as of game version " SUPPORTED_GAME_VERSION);
|
||||
}
|
||||
|
||||
void SaveTool::drawMassManager() {
|
||||
|
@ -411,7 +450,7 @@ void SaveTool::drawMassManager() {
|
|||
ImGui::TableSetupColumn("##Hangar", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("##MASSName", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##Active", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("##DeleteButton", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("##Buttons", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
|
||||
|
@ -429,35 +468,34 @@ void SaveTool::drawMassManager() {
|
|||
static int drag_drop_index = 0;
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Selectable(Utility::formatString("{:.2d}", i + 1).c_str(),
|
||||
ImGui::Selectable(Utility::format("{:.2d}", i + 1).data(),
|
||||
false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap);
|
||||
if(_massManager->massState(i) == MassState::Valid &&
|
||||
if(_massManager->hangar(i).state() == Mass::State::Valid &&
|
||||
ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
|
||||
{
|
||||
drag_drop_index = i;
|
||||
ImGui::SetDragDropPayload("Mass", &drag_drop_index, sizeof(int));
|
||||
|
||||
ImGui::Text("%s - Hangar %.2d", _massManager->massName(i).c_str(), i + 1);
|
||||
ImGui::Text("%s - Hangar %.2d", _massManager->hangar(i).name().data(), i + 1);
|
||||
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
if((!_unsafeMode && _mbManager->gameState() == GameState::NotRunning) && ImGui::BeginDragDropTarget()) {
|
||||
if(_gameState == GameState::NotRunning && ImGui::BeginDragDropTarget()) {
|
||||
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) {
|
||||
if(payload->DataSize != sizeof(std::string)) {
|
||||
if(payload->DataSize != sizeof(Containers::String)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
|
||||
"payload->DataSize != sizeof(std::string) in SaveTool::drawMassManager()",
|
||||
"payload->DataSize != sizeof(Containers::String) in SaveTool::drawMassManager()",
|
||||
window());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string file = *(static_cast<std::string*>(payload->Data));
|
||||
Containers::StringView file = *static_cast<Containers::String*>(payload->Data);
|
||||
|
||||
if(!_massManager->importMass(file, i)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error importing M.A.S.S.",
|
||||
_massManager->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _massManager->lastError());
|
||||
}
|
||||
}
|
||||
else if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("Mass")) {
|
||||
else if((payload = ImGui::AcceptDragDropPayload("Mass"))) {
|
||||
if(payload->DataSize != sizeof(int)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
|
||||
"payload->DataSize != sizeof(int) in SaveTool::drawMassManager()",
|
||||
|
@ -468,9 +506,7 @@ void SaveTool::drawMassManager() {
|
|||
int index = *(static_cast<int*>(payload->Data));
|
||||
|
||||
if(!_massManager->moveMass(index, i)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_massManager->lastError().c_str(),
|
||||
window());
|
||||
_queue.addToast(Toast::Type::Error, _massManager->lastError());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,30 +514,45 @@ void SaveTool::drawMassManager() {
|
|||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
switch(_massManager->massState(i)) {
|
||||
case MassState::Empty:
|
||||
ImGui::TextUnformatted("<empty>");
|
||||
switch(_massManager->hangar(i).state()) {
|
||||
case Mass::State::Empty:
|
||||
ImGui::TextDisabled("<empty>");
|
||||
break;
|
||||
case MassState::Invalid:
|
||||
ImGui::TextUnformatted("<invalid>");
|
||||
case Mass::State::Invalid:
|
||||
ImGui::TextDisabled("<invalid>");
|
||||
break;
|
||||
case MassState::Valid:
|
||||
ImGui::TextUnformatted(_massManager->massName(i).c_str());
|
||||
case Mass::State::Valid:
|
||||
ImGui::TextUnformatted(_massManager->hangar(i).name().data());
|
||||
break;
|
||||
}
|
||||
|
||||
if(i == _currentProfile->activeFrameSlot()) {
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::TextUnformatted(ICON_FA_CHECK);
|
||||
drawTooltip("This is the currently active frame slot.");
|
||||
}
|
||||
|
||||
if(_massManager->massState(i) != MassState::Empty) {
|
||||
if(_massManager->hangar(i).state() != Mass::State::Empty) {
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::PushID(i);
|
||||
if(_massManager->hangar(i).state() == Mass::State::Valid) {
|
||||
if(ImGui::SmallButton(ICON_FA_SEARCH)) {
|
||||
_currentMass = &_massManager->hangar(i);
|
||||
_uiState = UiState::MassViewer;
|
||||
}
|
||||
drawTooltip("Open in M.A.S.S. editor");
|
||||
}
|
||||
else{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.0f);
|
||||
ImGui::SmallButton(ICON_FA_SEARCH);
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGui::SameLine(0.0f, 2.0f);
|
||||
if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) {
|
||||
mass_to_delete = i;
|
||||
ImGui::OpenPopup(mass_deletion_popup_ID);
|
||||
}
|
||||
drawTooltip("Delete");
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
@ -512,7 +563,7 @@ void SaveTool::drawMassManager() {
|
|||
drawDeleteMassPopup(mass_to_delete);
|
||||
|
||||
static ImGuiID staged_mass_deletion_popup_ID = drawDeleteStagedMassPopup("");
|
||||
static Containers::Reference<const std::string> staged_mass_to_delete{empty_str};
|
||||
static Containers::StringView staged_mass_to_delete;
|
||||
|
||||
if(ImGui::BeginTable("##StagingArea", 2,
|
||||
ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_RowBg))
|
||||
|
@ -528,31 +579,32 @@ void SaveTool::drawMassManager() {
|
|||
ImGui::TextUnformatted("Staging area");
|
||||
ImGui::SameLine();
|
||||
if(ImGui::SmallButton(ICON_FA_FOLDER_OPEN " Open staging folder")) {
|
||||
openUri(_massManager->stagingAreaDirectory());
|
||||
openUri(Utility::Path::toNativeSeparators(_stagingDir));
|
||||
}
|
||||
|
||||
for(const auto& pair : _massManager->stagedMasses()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
std::string staged_formatted = Utility::formatString("{} ({})", pair.second, pair.first);
|
||||
ImGui::Selectable(staged_formatted.c_str());
|
||||
if((ImGui::CalcTextSize(staged_formatted.c_str()).x + ImGui::GetStyle().FramePadding.x) > ImGui::GetContentRegionAvailWidth()) {
|
||||
drawTooltip(staged_formatted.c_str());
|
||||
Containers::String staged_formatted = Utility::format("{} ({})", pair.second, pair.first);
|
||||
ImGui::Selectable(staged_formatted.data());
|
||||
if((ImGui::CalcTextSize(staged_formatted.data()).x + ImGui::GetStyle().FramePadding.x) > ImGui::GetContentRegionAvail().x) {
|
||||
drawTooltip(staged_formatted.data());
|
||||
}
|
||||
if(ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) {
|
||||
ImGui::SetDragDropPayload("StagedMass", &(pair.first), sizeof(std::string));
|
||||
ImGui::SetDragDropPayload("StagedMass", &(pair.first), sizeof(Containers::String));
|
||||
|
||||
ImGui::Text("%s - Staged", pair.second.c_str());
|
||||
ImGui::Text("%s - Staged", pair.second.data());
|
||||
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::PushID(pair.first.c_str());
|
||||
ImGui::PushID(pair.first.data());
|
||||
if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) {
|
||||
staged_mass_to_delete = Containers::Reference<const std::string>{pair.first};
|
||||
staged_mass_to_delete = pair.first;
|
||||
ImGui::OpenPopup(staged_mass_deletion_popup_ID);
|
||||
}
|
||||
drawTooltip("Delete");
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
|
@ -570,16 +622,14 @@ void SaveTool::drawMassManager() {
|
|||
int index = *(static_cast<int*>(payload->Data));
|
||||
|
||||
if(!_massManager->exportMass(index)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_massManager->lastError().c_str(),
|
||||
window());
|
||||
_queue.addToast(Toast::Type::Error, _massManager->lastError());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
drawDeleteStagedMassPopup(staged_mass_to_delete.get());
|
||||
drawDeleteStagedMassPopup(staged_mass_to_delete);
|
||||
}
|
||||
|
||||
auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
|
||||
|
@ -589,26 +639,26 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
|
|||
return ImGui::GetID("Confirmation##DeleteMassConfirmation");
|
||||
}
|
||||
|
||||
if(_massManager->massState(mass_index) == MassState::Empty) {
|
||||
if(_massManager->hangar(mass_index).state() == Mass::State::Empty) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(_mbManager->gameState() != GameState::NotRunning) {
|
||||
if(_gameState != GameState::NotRunning) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
if(_massManager->massState(mass_index) == MassState::Invalid) {
|
||||
ImGui::PushTextWrapPos(float(windowSize().x()) * 0.40f);
|
||||
if(_massManager->hangar(mass_index).state() == Mass::State::Invalid) {
|
||||
ImGui::Text("Are you sure you want to delete the invalid M.A.S.S. data in hangar %.2i ? This operation is irreversible.",
|
||||
mass_index + 1);
|
||||
}
|
||||
else {
|
||||
ImGui::Text("Are you sure you want to delete the M.A.S.S. named %s in hangar %.2i ? This operation is irreversible.",
|
||||
_massManager->massName(mass_index).c_str(), mass_index + 1);
|
||||
_massManager->hangar(mass_index).name().data(), mass_index + 1);
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
|
@ -621,8 +671,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
|
|||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::Button("Yes")) {
|
||||
if(!_massManager->deleteMass(mass_index)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.",
|
||||
_massManager->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _massManager->lastError());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
@ -639,16 +688,16 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto SaveTool::drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID {
|
||||
auto SaveTool::drawDeleteStagedMassPopup(Containers::StringView filename) -> ImGuiID {
|
||||
if(!ImGui::BeginPopupModal("Confirmation##DeleteStagedMassConfirmation", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove))
|
||||
{
|
||||
return ImGui::GetID("Confirmation##DeleteStagedMassConfirmation");
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
ImGui::PushTextWrapPos(float(windowSize().x()) * 0.40f);
|
||||
ImGui::Text("Are you sure you want to delete the staged M.A.S.S. named %s ? This operation is irreversible.",
|
||||
_massManager->stagedMasses().at(filename).c_str());
|
||||
_massManager->stagedMasses().at(filename).data());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
if(ImGui::BeginTable("##DeleteStagedMassLayout", 2)) {
|
||||
|
@ -660,8 +709,7 @@ auto SaveTool::drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID
|
|||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::Button("Yes")) {
|
||||
if(!_massManager->deleteStagedMass(filename)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.",
|
||||
_massManager->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _massManager->lastError());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,745 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
|
||||
#include <Magnum/ImGuiIntegration/Integration.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../Maps/Accessories.h"
|
||||
#define STYLENAMES_DEFINITION
|
||||
#include "../Maps/StyleNames.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawMassViewer() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
_currentMass = nullptr;
|
||||
_currentWeapon = nullptr;
|
||||
_uiState = UiState::MainManager;
|
||||
_queue.addToast(Toast::Type::Error, "The selected M.A.S.S. isn't valid anymore.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y},
|
||||
ImGuiCond_Always);
|
||||
if(!ImGui::Begin("##MassViewer", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove|
|
||||
ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoBringToFrontOnFocus))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if(ImGui::BeginChild("##MassInfo",
|
||||
{0.0f, 0.0f},
|
||||
true, ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
if(ImGui::BeginTable("##MassViewerMenuTable", 4)) {
|
||||
ImGui::TableSetupColumn("##MassName");
|
||||
ImGui::TableSetupColumn("##Spacer", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##Updates");
|
||||
ImGui::TableSetupColumn("##Close", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("M.A.S.S.: %s", _currentMass->name().data());
|
||||
drawTooltip(_currentMass->filename());
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
if(_currentMass->dirty()) {
|
||||
ImGui::TextUnformatted("External changes detected");
|
||||
ImGui::SameLine();
|
||||
if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) {
|
||||
_currentMass->refreshValues();
|
||||
_currentMass->setDirty(false);
|
||||
_jointsDirty = false;
|
||||
_stylesDirty = false;
|
||||
_eyeFlareDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
if(ImGui::SmallButton(ICON_FA_TIMES)) {
|
||||
_currentWeapon = nullptr;
|
||||
_currentMass = nullptr;
|
||||
_uiState = UiState::MainManager;
|
||||
_jointsDirty = false;
|
||||
_stylesDirty = false;
|
||||
_eyeFlareDirty = false;
|
||||
_selectedArmourDecals = Containers::StaticArray<38, Int>{ValueInit};
|
||||
_selectedArmourAccessories = Containers::StaticArray<38, Int>{ValueInit};
|
||||
_selectedBLPlacement = 0;
|
||||
_selectedWeaponPart = 0;
|
||||
_selectedWeaponDecal = 0;
|
||||
_selectedWeaponAccessory = 0;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE);
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline.");
|
||||
ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE);
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::TextWrapped("Real-time updates are disabled on this screen.");
|
||||
|
||||
if(_currentMass) {
|
||||
if(ImGui::BeginTabBar("##MassTabBar")) {
|
||||
if(ImGui::BeginTabItem("Frame")) {
|
||||
drawFrameInfo();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if(ImGui::BeginTabItem("Custom frame styles")) {
|
||||
drawCustomFrameStyles();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if(ImGui::BeginTabItem("Armour")) {
|
||||
drawArmour();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if(ImGui::BeginTabItem("Custom armour styles")) {
|
||||
drawCustomArmourStyles();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if(ImGui::BeginTabItem("Weapons")) {
|
||||
drawWeapons();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if(_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) {
|
||||
drawGlobalStyles();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
if(ImGui::BeginTabItem("Tuning (WIP)")) {
|
||||
drawTuning();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void SaveTool::drawGlobalStyles() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ImGui::BeginChild("##GlobalStyles")) {
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->globalStyles().size(); i++) {
|
||||
ImGui::PushID(int(i));
|
||||
DCSResult result;
|
||||
result = drawCustomStyle(_currentMass->globalStyles()[i]);
|
||||
switch(result) {
|
||||
case DCS_ResetStyle:
|
||||
_currentMass->getGlobalStyles();
|
||||
break;
|
||||
case DCS_Save:
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeGlobalStyle(i)) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SaveTool::drawTuning() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ImGui::BeginTable("##TuningTable", 3)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TableSetupColumn("##EngineColumn");
|
||||
ImGui::TableSetupColumn("##OSColumn");
|
||||
ImGui::TableSetupColumn("##ArchitectureColumn");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
|
||||
if(ImGui::BeginTable("##EngineTable", 1, ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("##Engine");
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Engine");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%i", _currentMass->engine());
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Gears");
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->gears().size(); i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%i", _currentMass->gears()[i]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
|
||||
if(ImGui::BeginTable("##OSTable", 1, ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("##OS");
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("OS");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%i", _currentMass->os());
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Modules");
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->modules().size(); i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%i", _currentMass->modules()[i]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
|
||||
if(ImGui::BeginTable("##ArchTable", 1, ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("##Arch");
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Architecture");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%i", _currentMass->architecture());
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("Techs");
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->techs().size(); i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%i", _currentMass->techs()[i]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return DCS_Fail;
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {8.0f, 0.0f});
|
||||
|
||||
Containers::ScopeGuard guard{[]{ ImGui::PopStyleVar(); }};
|
||||
|
||||
DCSResult return_value = DCS_Fail;
|
||||
|
||||
if(!ImGui::BeginChild("##CustomStyle", {0.0f, 244.0f}, true, ImGuiWindowFlags_MenuBar)) {
|
||||
ImGui::EndChild();
|
||||
return DCS_Fail;
|
||||
}
|
||||
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
ImGui::TextUnformatted(style.name.data());
|
||||
|
||||
static Containers::StaticArray<33, char> name_buf{ValueInit};
|
||||
if(ImGui::SmallButton(ICON_FA_EDIT " Rename")) {
|
||||
for(auto& c : name_buf) {
|
||||
c = '\0';
|
||||
}
|
||||
std::strncpy(name_buf.data(), style.name.data(), 32);
|
||||
ImGui::OpenPopup("name_edit");
|
||||
}
|
||||
if(drawRenamePopup(name_buf)) {
|
||||
style.name = name_buf.data();
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
if(ImGui::BeginTable("##StyleTable", 2, ImGuiTableFlags_BordersInnerV)) {
|
||||
ImGui::TableSetupColumn("##Colour", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##Pattern", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Colour:");
|
||||
drawAlignedText("Metallic:");
|
||||
drawAlignedText("Gloss:");
|
||||
drawAlignedText("Glow:");
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::ColorEdit3("##Picker", &style.colour.r());
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("Right-click for more option, click the coloured square for the full picker.");
|
||||
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SliderFloat("##SliderMetallic", &style.metallic, 0.0f, 1.0f);
|
||||
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SliderFloat("##SliderGloss", &style.gloss,0.0f, 1.0f);
|
||||
|
||||
ImGui::Checkbox("##Glow", &style.glow);
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Pattern:");
|
||||
drawAlignedText("Opacity:");
|
||||
drawAlignedText("X offset:");
|
||||
drawAlignedText("Y offset:");
|
||||
drawAlignedText("Rotation:");
|
||||
drawAlignedText("Scale:");
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("%i", style.patternId);
|
||||
ImGui::PushItemWidth(-FLT_MIN);
|
||||
ImGui::SliderFloat("##SliderOpacity", &style.opacity, 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("##SliderOffsetX", &style.offset.x(), 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("##SliderOffsetY", &style.offset.y(), 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("##SliderRotation", &style.rotation, 0.0f, 1.0f);
|
||||
ImGui::SliderFloat("##SliderScale", &style.scale, 0.0f, 1.0f);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
||||
return_value = DCS_Save;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
|
||||
return_value = DCS_ResetStyle;
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
void SaveTool::drawDecalEditor(Decal& decal) {
|
||||
ImGui::Text("ID: %i", decal.id);
|
||||
|
||||
if(ImGui::BeginTable("##DecalTable", _advancedMode ? 2 : 1, ImGuiTableFlags_BordersInnerV)) {
|
||||
ImGui::TableSetupColumn("##Normal", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
if(_advancedMode) {
|
||||
ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Colour:");
|
||||
drawAlignedText("Offset:");
|
||||
drawAlignedText("Rotation:");
|
||||
drawAlignedText("Scale:");
|
||||
drawAlignedText("Flip X:");
|
||||
drawAlignedText("Surface wrap:");
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::ColorEdit3("##Picker", &decal.colour.r());
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("Right-click for more option, click the coloured square for the full picker.");
|
||||
|
||||
ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##OffsetX", &decal.offset.x(), 0.0f, 1.0f, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##OffsetY", &decal.offset.y(), 0.0f, 1.0f, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("0.0 = -100 in-game\n1.0 = 100 in-game");
|
||||
|
||||
ImGui::SliderFloat("##Rotation", &decal.rotation, 0.0f, 1.0f);
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("0.0 = 0 in-game\n1.0 = 360 in-game");
|
||||
|
||||
ImGui::SliderFloat("##Scale", &decal.scale, 0.0f, 1.0f);
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("0.0 = 1 in-game\n1.0 = 100 in-game");
|
||||
|
||||
ImGui::Checkbox("##Flip", &decal.flip);
|
||||
|
||||
ImGui::Checkbox("##Wrap", &decal.wrap);
|
||||
ImGui::EndGroup();
|
||||
|
||||
if(_advancedMode) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE);
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::TextUnformatted("Advanced settings. Touch these at your own risk.");
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Position:");
|
||||
drawAlignedText("U axis:");
|
||||
drawAlignedText("V axis:");
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##PosX", &decal.position.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##PosY", &decal.position.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##PosZ", &decal.position.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##UX", &decal.uAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##UY", &decal.uAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##UZ", &decal.uAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##VX", &decal.vAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##VY", &decal.vAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##VZ", &decal.vAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<CustomStyle> style_view) {
|
||||
if(accessory.id < 1) {
|
||||
ImGui::TextUnformatted("Accessory: <none>");
|
||||
}
|
||||
else if(accessories.find(accessory.id) != accessories.cend()) {
|
||||
ImGui::Text("Accessory #%.4i - %s", accessory.id, accessories.at(accessory.id).name.data());
|
||||
}
|
||||
else {
|
||||
ImGui::Text("Accessory #%i", accessory.id);
|
||||
drawTooltip("WARNING: accessory mapping is a WIP.");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
static Int tab = 0;
|
||||
static Containers::Optional<AccessorySize> size = Containers::NullOpt;
|
||||
if(ImGui::SmallButton("Change")) {
|
||||
ImGui::OpenPopup("##AccessoryPopup");
|
||||
if(accessory.id >= 3000) {
|
||||
tab = 3;
|
||||
}
|
||||
else if(accessory.id >= 2000) {
|
||||
tab = 2;
|
||||
}
|
||||
else if(accessory.id >= 1000) {
|
||||
tab = 1;
|
||||
}
|
||||
else {
|
||||
tab = 0;
|
||||
}
|
||||
}
|
||||
if(ImGui::BeginPopup("##AccessoryPopup")) {
|
||||
static const char* size_labels[] = {
|
||||
"S",
|
||||
"M",
|
||||
"L",
|
||||
"XL"
|
||||
};
|
||||
static const Float selectable_width = 90.0f;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f});
|
||||
if(ImGui::Selectable("Primitives", tab == 0, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||
tab = 0;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("Armours", tab == 1, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||
tab = 1;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("Components", tab == 2, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||
tab = 2;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("Connectors", tab == 3, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||
tab = 3;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f});
|
||||
if(ImGui::Selectable("All", !size, ImGuiSelectableFlags_DontClosePopups, {70.0f, 0.0f})) {
|
||||
size = Containers::NullOpt;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("S", size && *size == AccessorySize::S, ImGuiSelectableFlags_DontClosePopups, {70.0f, 0.0f})) {
|
||||
if(!size) {
|
||||
size.emplace();
|
||||
}
|
||||
*size = AccessorySize::S;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("M", size && *size == AccessorySize::M, ImGuiSelectableFlags_DontClosePopups, {70.0f, 0.0f})) {
|
||||
if(!size) {
|
||||
size.emplace();
|
||||
}
|
||||
*size = AccessorySize::M;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("L", size && *size == AccessorySize::L, ImGuiSelectableFlags_DontClosePopups, {70.0f, 0.0f})) {
|
||||
if(!size) {
|
||||
size.emplace();
|
||||
}
|
||||
*size = AccessorySize::L;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Selectable("XL", size && *size == AccessorySize::XL, ImGuiSelectableFlags_DontClosePopups, {70.0f, 0.0f})) {
|
||||
if(!size) {
|
||||
size.emplace();
|
||||
}
|
||||
*size = AccessorySize::XL;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if(ImGui::BeginListBox("##AccessoryListbox", {-1.0f, 0.0f})) {
|
||||
for(const auto& acc : accessories) {
|
||||
if(acc.first >= tab * 1000 && acc.first < ((tab + 1) * 1000) && (!size || *size == acc.second.size)) {
|
||||
if(ImGui::Selectable(Utility::format("#{:.4d} - {} ({})", acc.first, acc.second.name, size_labels[acc.second.size]).data(),
|
||||
acc.first == accessory.id))
|
||||
{
|
||||
accessory.id = acc.first;
|
||||
accessory.attachIndex = 0;
|
||||
}
|
||||
if(acc.first == accessory.id) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if(accessory.id > 0) {
|
||||
ImGui::SameLine();
|
||||
if(ImGui::SmallButton("Unequip")) {
|
||||
accessory.id = 0;
|
||||
accessory.attachIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Styles:");
|
||||
if(_advancedMode) {
|
||||
drawAlignedText("Base position:");
|
||||
}
|
||||
drawAlignedText("Position offset:");
|
||||
if(_advancedMode) {
|
||||
drawAlignedText("Base rotation:");
|
||||
}
|
||||
drawAlignedText("Rotation offset:");
|
||||
drawAlignedText("Scale:");
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth());
|
||||
if(ImGui::BeginCombo("##Style1", getStyleName(accessory.styles[0], style_view).data())) {
|
||||
for(const auto& style : style_names) {
|
||||
if(ImGui::Selectable(getStyleName(style.first, style_view).data(), accessory.styles[0] == style.first)) {
|
||||
accessory.styles[0] = style.first;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
if(ImGui::BeginCombo("##Style2", getStyleName(accessory.styles[1], style_view).data())) {
|
||||
for(const auto& style : style_names) {
|
||||
if(ImGui::Selectable(getStyleName(style.first, style_view).data(), accessory.styles[1] == style.first)) {
|
||||
accessory.styles[1] = style.first;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if(_advancedMode) {
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##PosX", &accessory.relativePosition.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##PosY", &accessory.relativePosition.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##PosZ", &accessory.relativePosition.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##PosOffsetX", &accessory.relativePositionOffset.x(), -500.0f, +500.0f, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##PosOffsetY", &accessory.relativePositionOffset.y(), -500.0f, +500.0f, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##PosOffsetZ", &accessory.relativePositionOffset.z(), -500.0f, +500.0f, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("+/-500.0 = +/-250 in-game");
|
||||
|
||||
if(_advancedMode) {
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##RotX", &accessory.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##RotY", &accessory.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##RotZ", &accessory.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##RotOffsetX", &accessory.relativeRotationOffset.x(), -180.0f, +180.0f, "Roll: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##RotOffsetY", &accessory.relativeRotationOffset.y(), -180.0f, +180.0f, "Yaw: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##RotOffsetZ", &accessory.relativeRotationOffset.z(), -180.0f, +180.0f, "Pitch: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##ScaleX", &accessory.localScale.x(), -3.0f, +3.0f, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##ScaleY", &accessory.localScale.y(), -3.0f, +3.0f, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##ScaleZ", &accessory.localScale.z(), -3.0f, +3.0f, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("+/-3.0 = +/-150 in-game");
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
auto SaveTool::getStyleName(Int id, Containers::ArrayView<CustomStyle> view) -> Containers::StringView {
|
||||
if(id >= 0 && id <= 15) {
|
||||
return view[id].name;
|
||||
}
|
||||
else if(id >= 50 && id <= 65) {
|
||||
return _currentMass->globalStyles()[id - 50].name;
|
||||
}
|
||||
else {
|
||||
return style_names.at(id);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,325 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "../Maps/ArmourSets.h"
|
||||
#include "../Maps/StyleNames.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawArmour() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) {
|
||||
_currentMass->getArmourParts();
|
||||
_currentMass->getBulletLauncherAttachments();
|
||||
}
|
||||
|
||||
if(!ImGui::BeginChild("##ArmourParts", {0.0f, 0.0f}, true)) {
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
static Containers::StringView slot_labels[] = {
|
||||
#define c(enumerator, strenum, name) name,
|
||||
#include "../Maps/ArmourSlots.hpp"
|
||||
#undef c
|
||||
};
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) {
|
||||
ImGui::PushID(int(i));
|
||||
|
||||
auto& part = _currentMass->armourParts()[i];
|
||||
|
||||
static char header[129] = {'\0'};
|
||||
|
||||
std::memset(header, '\0', 129);
|
||||
|
||||
if(armour_sets.find(part.id) != armour_sets.cend()) {
|
||||
std::snprintf(header, 128, "%s: %s###%u", slot_labels[UnsignedInt(part.slot)].data(), armour_sets.at(part.id).name.data(), UnsignedInt(part.slot));
|
||||
}
|
||||
else {
|
||||
std::snprintf(header, 128, "%s: %i###%u", slot_labels[UnsignedInt(part.slot)].data(), part.id, UnsignedInt(part.slot));
|
||||
}
|
||||
|
||||
if(ImGui::CollapsingHeader(header)) {
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.491f);
|
||||
if(ImGui::BeginListBox("##ChangePart")) {
|
||||
if(std::strncmp("Neck", slot_labels[UnsignedInt(part.slot)].data(), 4) != 0) {
|
||||
for(auto& set : armour_sets) {
|
||||
if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) {
|
||||
part.id = set.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(auto& set : armour_sets) {
|
||||
if(!set.second.neck_compatible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) {
|
||||
part.id = set.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::TextUnformatted("Styles:");
|
||||
|
||||
for(Int j = 0; j < 4; j++) {
|
||||
drawAlignedText("Slot %d:", j + 1);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushID(j);
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 2.0f);
|
||||
if(ImGui::BeginCombo("##Style", getStyleName(part.styles[j], _currentMass->armourCustomStyles()).data())) {
|
||||
for(const auto& style : style_names) {
|
||||
if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(), part.styles[j] == style.first)) {
|
||||
part.styles[j] = style.first;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushID("Decal");
|
||||
|
||||
drawAlignedText("Showing/editing decal");
|
||||
for(UnsignedInt j = 0; j < part.decals.size(); j++) {
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], int(j));
|
||||
}
|
||||
|
||||
drawDecalEditor(part.decals[_selectedArmourDecals[i]]);
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if(part.accessories.size() != 0) {
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushID("Accessory");
|
||||
|
||||
drawAlignedText("Showing/editing accessory");
|
||||
for(UnsignedInt j = 0; j < part.accessories.size(); j++) {
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], int(j));
|
||||
}
|
||||
|
||||
drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles());
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeArmourPart(part.slot)) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if(_currentMass->bulletLauncherAttachmentStyle() != BulletLauncherAttachmentStyle::NotFound &&
|
||||
ImGui::CollapsingHeader("Bullet launcher placement"))
|
||||
{
|
||||
drawAlignedText("Attachment style:"_s);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("Active one",
|
||||
_currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::ActiveOne);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("Active one per slot",
|
||||
_currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::ActiveOnePerSlot);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("All equipped",
|
||||
_currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::AllEquipped);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
drawAlignedText("Launcher slot:");
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("1", &_selectedBLPlacement, 0);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("2", &_selectedBLPlacement, 1);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("3", &_selectedBLPlacement, 2);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("4", &_selectedBLPlacement, 3);
|
||||
|
||||
auto& placement = _currentMass->bulletLauncherAttachments()[_selectedBLPlacement];
|
||||
|
||||
static const Containers::StringView socket_labels[] = {
|
||||
#define c(enumerator, enumstr, name) name,
|
||||
#include "../Maps/BulletLauncherSockets.hpp"
|
||||
#undef c
|
||||
};
|
||||
|
||||
drawAlignedText("Socket:");
|
||||
ImGui::SameLine();
|
||||
if(ImGui::BeginCombo("##Socket", socket_labels[UnsignedInt(placement.socket)].data())) {
|
||||
for(UnsignedInt i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) {
|
||||
if(ImGui::Selectable(socket_labels[i].data(), i == UnsignedInt(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) {
|
||||
placement.socket = static_cast<BulletLauncherSocket>(i);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if(placement.socket != BulletLauncherSocket::Auto) {
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Relative position:");
|
||||
drawAlignedText("Offset position:");
|
||||
drawAlignedText("Relative rotation:");
|
||||
drawAlignedText("Offset rotation:");
|
||||
drawAlignedText("Scale:");
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##RelPosX", &placement.relativeLocation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##RelPosY", &placement.relativeLocation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##RelPosZ", &placement.relativeLocation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##OffPosX", &placement.offsetLocation.x(), -500.0f, +500.0f, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##OffPosY", &placement.offsetLocation.y(), -500.0f, +500.0f, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##OffPosZ", &placement.offsetLocation.z(), -500.0f, +500.0f, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("+/-500.0 = +/-250 in-game");
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::DragFloat("##RotX", &placement.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##RotY", &placement.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::DragFloat("##RotZ", &placement.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##RotOffsetZ", &placement.offsetRotation.z(), -180.0f, +180.0f, "Roll: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##RotOffsetX", &placement.offsetRotation.x(), -30.0f, +30.0f, "Pitch: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##RotOffsetY", &placement.offsetRotation.y(), -30.0f, +30.0f, "Yaw: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
|
||||
ImGui::SliderFloat("##ScaleX", &placement.relativeScale.x(), 0.5f, 1.5f, "X: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##ScaleY", &placement.relativeScale.y(), 0.5f, 1.5f, "Y: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::SliderFloat("##ScaleZ", &placement.relativeScale.z(), 0.5f, 1.5f, "Z: %.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("0.5 = 50 in-game\n1.5 = 150 in-game");
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
_modifiedBySaveTool = true;
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); }) &&
|
||||
!_currentMass->writeBulletLauncherAttachments())
|
||||
{
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SaveTool::drawCustomArmourStyles() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ImGui::BeginChild("##ArmourStyles")) {
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->armourCustomStyles().size(); i++) {
|
||||
ImGui::PushID(int(i));
|
||||
DCSResult result;
|
||||
result = drawCustomStyle(_currentMass->armourCustomStyles()[i]);
|
||||
switch(result) {
|
||||
case DCS_ResetStyle:
|
||||
_currentMass->getArmourCustomStyles();
|
||||
break;
|
||||
case DCS_Save:
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeArmourCustomStyle(i)) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "../Maps/StyleNames.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawFrameInfo() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ImGui::BeginChild("##FrameInfo")) {
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if(ImGui::BeginChild("##JointSliders", {(ImGui::GetContentRegionAvail().x / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 300.0f}, true, ImGuiWindowFlags_MenuBar)) {
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
ImGui::TextUnformatted("Joint sliders");
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
drawJointSliders();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if(ImGui::BeginChild("##FrameStyles", {(ImGui::GetContentRegionAvail().x / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 0.0f}, true, ImGuiWindowFlags_MenuBar)) {
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
ImGui::TextUnformatted("Frame styles");
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
drawFrameStyles();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(ImGui::BeginChild("##EyeFlare", {0.0f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) {
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
ImGui::TextUnformatted("Eye flare colour");
|
||||
drawHelpMarker("Right-click the picker for more options.", 250.0f);
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
drawEyeColourPicker();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SaveTool::drawJointSliders() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("In-game values are multiplied by 100.\nFor example, 0.500 here is equal to 50 in-game.");
|
||||
|
||||
if(ImGui::BeginTable("##JointSliderTable", 2, ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("##SliderLabel", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("##Sliders", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
drawAlignedText("Neck");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
drawAlignedText("Body");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
drawAlignedText("Shoulders");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
drawAlignedText("Hips");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
drawAlignedText("Arms");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f});
|
||||
if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2)) {
|
||||
ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
drawAlignedText("Legs");
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f});
|
||||
if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2)) {
|
||||
ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) {
|
||||
_jointsDirty = true;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if(!_jointsDirty) {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button(ICON_FA_SAVE " Save");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_UNDO " Reset");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else {
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeJointSliders()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
_jointsDirty = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
|
||||
_currentMass->getJointSliders();
|
||||
_jointsDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawFrameStyles() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(Int i = 0; i < 4; i++) {
|
||||
drawAlignedText("Slot %d:", i + 1);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
if(ImGui::BeginCombo("##Style", getStyleName(_currentMass->frameStyles()[i], _currentMass->frameCustomStyles()).data())) {
|
||||
for(const auto& style : style_names) {
|
||||
if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()).data(), _currentMass->frameStyles()[i] == style.first)) {
|
||||
_currentMass->frameStyles()[i] = style.first;
|
||||
_stylesDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if(!_stylesDirty) {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button(ICON_FA_SAVE " Save");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_UNDO " Reset");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else {
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeFrameStyles()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
_stylesDirty = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
|
||||
_currentMass->getFrameStyles();
|
||||
_stylesDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawEyeColourPicker() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) {
|
||||
_eyeFlareDirty = true;
|
||||
}
|
||||
|
||||
if(!_eyeFlareDirty) {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button(ICON_FA_SAVE " Save");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_UNDO " Reset");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else {
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeEyeFlareColour()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
_eyeFlareDirty = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
|
||||
_currentMass->getEyeFlareColour();
|
||||
_eyeFlareDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTool::drawCustomFrameStyles() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ImGui::BeginChild("##FrameStyles")) {
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->frameCustomStyles().size(); i++) {
|
||||
ImGui::PushID(int(i));
|
||||
DCSResult result;
|
||||
result = drawCustomStyle(_currentMass->frameCustomStyles()[i]);
|
||||
switch(result) {
|
||||
case DCS_ResetStyle:
|
||||
_currentMass->getFrameCustomStyles();
|
||||
break;
|
||||
case DCS_Save:
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeFrameCustomStyle(i)) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
|
@ -0,0 +1,565 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "../Maps/StyleNames.h"
|
||||
#include "../Maps/WeaponParts.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawWeapons() {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
|
||||
_currentWeapon = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if(!ImGui::BeginTable("##WeaponsList", 1,
|
||||
ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH,
|
||||
{ImGui::GetContentRegionAvail().x * 0.2f, -footer_height_to_reserve}))
|
||||
{
|
||||
ImGui::EndGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TableSetupColumn("Weapon");
|
||||
|
||||
drawWeaponCategory("Melee weapons", _currentMass->meleeWeapons(), _meleeDirty, "MeleeWeapon", "Melee weapon");
|
||||
drawWeaponCategory("Shield", _currentMass->shields(), _shieldsDirty, "Shield", "Shield");
|
||||
drawWeaponCategory("Bullet shooters", _currentMass->bulletShooters(), _bShootersDirty, "BShooter", "Bullet shooter");
|
||||
drawWeaponCategory("Energy shooters", _currentMass->energyShooters(), _eShootersDirty, "EShooter", "Energy shooter");
|
||||
drawWeaponCategory("Bullet launchers", _currentMass->bulletLaunchers(), _bLaunchersDirty, "BLauncher", "Bullet launcher");
|
||||
drawWeaponCategory("Energy launchers", _currentMass->energyLaunchers(), _eLaunchersDirty, "ELauncher", "Energy launcher");
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
bool dirty = _meleeDirty || _shieldsDirty || _bShootersDirty || _eShootersDirty || _bLaunchersDirty || _eLaunchersDirty;
|
||||
|
||||
if(!dirty) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
|
||||
if(_meleeDirty) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeMeleeWeapons()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
else {
|
||||
_meleeDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_shieldsDirty) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeShields()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
else {
|
||||
_shieldsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_bShootersDirty) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(!_currentMass->writeBulletShooters()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
else {
|
||||
_bShootersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_eShootersDirty) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(_currentMass->writeEnergyShooters()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
else {
|
||||
_eShootersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_bLaunchersDirty) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(_currentMass->writeBulletLaunchers()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
else {
|
||||
_bLaunchersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(_eLaunchersDirty) {
|
||||
_modifiedBySaveTool = true;
|
||||
if(_currentMass->writeEnergyLaunchers()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
else {
|
||||
_eLaunchersDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset")) {
|
||||
if(_meleeDirty) {
|
||||
_currentMass->getMeleeWeapons();
|
||||
_meleeDirty = false;
|
||||
}
|
||||
if(_shieldsDirty) {
|
||||
_currentMass->getShields();
|
||||
_shieldsDirty = false;
|
||||
}
|
||||
if(_bShootersDirty) {
|
||||
_currentMass->getBulletShooters();
|
||||
_bShootersDirty = false;
|
||||
}
|
||||
if(_eShootersDirty) {
|
||||
_currentMass->getEnergyShooters();
|
||||
_eShootersDirty = false;
|
||||
}
|
||||
if(_bLaunchersDirty) {
|
||||
_currentMass->getBulletLaunchers();
|
||||
_bLaunchersDirty = false;
|
||||
}
|
||||
if(_eLaunchersDirty) {
|
||||
_currentMass->getEnergyLaunchers();
|
||||
_eLaunchersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dirty) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(!_currentWeapon) {
|
||||
ImGui::TextUnformatted("No weapon selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) {
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
drawWeaponEditor(*_currentWeapon);
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) {
|
||||
_modifiedBySaveTool = true;
|
||||
switch(_currentWeapon->type) {
|
||||
case WeaponType::Melee:
|
||||
if(!_currentMass->writeMeleeWeapons()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
case WeaponType::Shield:
|
||||
if(!_currentMass->writeShields()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
case WeaponType::BulletShooter:
|
||||
if(!_currentMass->writeBulletShooters()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
case WeaponType::EnergyShooter:
|
||||
if(!_currentMass->writeEnergyShooters()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
case WeaponType::BulletLauncher:
|
||||
if(!_currentMass->writeBulletLaunchers()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
case WeaponType::EnergyLauncher:
|
||||
if(!_currentMass->writeEnergyLaunchers()) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, "Unknown weapon type");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) {
|
||||
switch(_currentWeapon->type) {
|
||||
case WeaponType::Melee:
|
||||
_currentMass->getMeleeWeapons();
|
||||
break;
|
||||
case WeaponType::Shield:
|
||||
_currentMass->getShields();
|
||||
break;
|
||||
case WeaponType::BulletShooter:
|
||||
_currentMass->getBulletShooters();
|
||||
break;
|
||||
case WeaponType::EnergyShooter:
|
||||
_currentMass->getEnergyShooters();
|
||||
break;
|
||||
case WeaponType::BulletLauncher:
|
||||
_currentMass->getBulletLaunchers();
|
||||
break;
|
||||
case WeaponType::EnergyLauncher:
|
||||
_currentMass->getEnergyLaunchers();
|
||||
break;
|
||||
default:
|
||||
_queue.addToast(Toast::Type::Error, "Unknown weapon type");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void SaveTool::drawWeaponCategory(Containers::StringView name, Containers::ArrayView<Weapon> weapons_view, bool& dirty,
|
||||
Containers::StringView payload_type, Containers::StringView payload_tooltip)
|
||||
{
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(name.data());
|
||||
|
||||
ImGui::PushID(payload_type.data());
|
||||
|
||||
for(UnsignedInt i = 0; i < weapons_view.size(); i++) {
|
||||
auto& weapon = weapons_view[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushID(int(i));
|
||||
|
||||
if(ImGui::Selectable(weapon.name.data(), _currentWeapon == &weapon)) {
|
||||
_currentWeapon = &weapon;
|
||||
}
|
||||
if(ImGui::BeginDragDropSource()) {
|
||||
ImGui::SetDragDropPayload(payload_type.data(), &i, sizeof(UnsignedInt));
|
||||
if(ImGui::GetIO().KeyCtrl) {
|
||||
ImGui::Text("%s %i - %s (copy)", payload_tooltip.data(), i + 1, weapon.name.data());
|
||||
}
|
||||
else {
|
||||
ImGui::Text("%s %i - %s", payload_tooltip.data(), i + 1, weapon.name.data());
|
||||
}
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
if(ImGui::BeginDragDropTarget()) {
|
||||
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type.data())) {
|
||||
int index = *static_cast<int*>(payload->Data);
|
||||
|
||||
if(!ImGui::GetIO().KeyCtrl) {
|
||||
if(_currentWeapon == &weapons_view[index]) {
|
||||
_currentWeapon = &weapons_view[i];
|
||||
}
|
||||
else if (_currentWeapon == &weapons_view[i]) {
|
||||
_currentWeapon = &weapons_view[index];
|
||||
}
|
||||
|
||||
std::swap(weapons_view[index], weapons_view[i]);
|
||||
}
|
||||
else {
|
||||
weapons_view[i] = weapons_view[index];
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if(weapon.attached) {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void SaveTool::drawWeaponEditor(Weapon& weapon) {
|
||||
if(!_currentMass || _currentMass->state() != Mass::State::Valid || !_currentWeapon) {
|
||||
return;
|
||||
}
|
||||
|
||||
static Containers::StringView labels[] {
|
||||
#define c(enumerator, strenum, name) name,
|
||||
#include "../Maps/WeaponTypes.hpp"
|
||||
#undef c
|
||||
};
|
||||
|
||||
drawAlignedText("%s: %s", labels[UnsignedInt(weapon.type)].data(), weapon.name.data());
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
static Containers::StaticArray<33, char> name_buf{ValueInit};
|
||||
if(ImGui::Button(ICON_FA_EDIT " Rename")) {
|
||||
for(auto& c : name_buf) {
|
||||
c = '\0';
|
||||
}
|
||||
std::strncpy(name_buf.data(), weapon.name.data(), 32);
|
||||
ImGui::OpenPopup("name_edit");
|
||||
}
|
||||
if(drawRenamePopup(name_buf)) {
|
||||
weapon.name = name_buf.data();
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Equipped:");
|
||||
|
||||
if(weapon.type != WeaponType::Shield) {
|
||||
drawAlignedText("Damage type:");
|
||||
}
|
||||
|
||||
if(weapon.type == WeaponType::Melee) {
|
||||
drawAlignedText("Dual-wield:");
|
||||
|
||||
drawAlignedText("Custom effect mode:");
|
||||
|
||||
drawAlignedText("Custom effect colour:");
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::Checkbox("##EquippedCheckbox", &weapon.attached);
|
||||
|
||||
if(weapon.type != WeaponType::Shield) {
|
||||
if(weapon.type == WeaponType::Melee &&
|
||||
ImGui::RadioButton("Physical##NoElement", weapon.damageType == DamageType::Physical))
|
||||
{
|
||||
weapon.damageType = DamageType::Physical;
|
||||
}
|
||||
else if((weapon.type == WeaponType::BulletShooter || weapon.type == WeaponType::BulletLauncher) &&
|
||||
ImGui::RadioButton("Piercing##NoElement", weapon.damageType == DamageType::Piercing))
|
||||
{
|
||||
weapon.damageType = DamageType::Piercing;
|
||||
}
|
||||
else if((weapon.type == WeaponType::EnergyShooter || weapon.type == WeaponType::EnergyLauncher) &&
|
||||
ImGui::RadioButton("Plasma##NoElement", weapon.damageType == DamageType::Plasma))
|
||||
{
|
||||
weapon.damageType = DamageType::Plasma;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::RadioButton("Heat##Heat", weapon.damageType == DamageType::Heat)) {
|
||||
weapon.damageType = DamageType::Heat;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == DamageType::Freeze)) {
|
||||
weapon.damageType = DamageType::Freeze;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::RadioButton("Shock##Shock", weapon.damageType == DamageType::Shock)) {
|
||||
weapon.damageType = DamageType::Shock;
|
||||
}
|
||||
}
|
||||
|
||||
if(weapon.type == WeaponType::Melee) {
|
||||
ImGui::Checkbox("##DualWield", &weapon.dualWield);
|
||||
|
||||
if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == EffectColourMode::Default)) {
|
||||
weapon.effectColourMode = EffectColourMode::Default;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == EffectColourMode::Custom)) {
|
||||
weapon.effectColourMode = EffectColourMode::Custom;
|
||||
}
|
||||
|
||||
bool custom_effect = (weapon.effectColourMode == EffectColourMode::Custom);
|
||||
if(!custom_effect) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
ImGui::ColorEdit3("##CustomEffectColourPicker", &weapon.effectColour.x(), ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_Float);
|
||||
ImGui::SameLine();
|
||||
drawHelpMarker("Click the coloured square for the full picker.");
|
||||
|
||||
if(!custom_effect) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if(ImGui::CollapsingHeader("Weapon parts")) {
|
||||
drawAlignedText("Viewing/editing part:");
|
||||
for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) {
|
||||
if(UnsignedLong(_selectedWeaponPart) >= weapon.parts.size()) {
|
||||
_selectedWeaponPart = 0;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(std::to_string(i).c_str(), &_selectedWeaponPart, i);
|
||||
}
|
||||
|
||||
auto& part = weapon.parts[_selectedWeaponPart];
|
||||
|
||||
const auto* map = [this, &weapon]()-> const std::map<Int, Containers::StringView>* {
|
||||
switch(weapon.type) {
|
||||
case WeaponType::Melee:
|
||||
return _selectedWeaponPart == 0 ? &melee_grips : &melee_assaulters;
|
||||
case WeaponType::Shield:
|
||||
return _selectedWeaponPart == 0 ? &shield_handles : &shield_shells;
|
||||
case WeaponType::BulletShooter:
|
||||
return _selectedWeaponPart == 0 ? &bshooter_triggers : &bshooter_barrels;
|
||||
case WeaponType::EnergyShooter:
|
||||
return _selectedWeaponPart == 0 ? &eshooter_triggers : &eshooter_busters;
|
||||
case WeaponType::BulletLauncher:
|
||||
return _selectedWeaponPart == 0 ? &blauncher_pods : &blauncher_projectiles;
|
||||
case WeaponType::EnergyLauncher:
|
||||
return _selectedWeaponPart == 0 ? &elauncher_generators : &elauncher_pods;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if(!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(map->find(part.id) != map->cend()) {
|
||||
ImGui::TextUnformatted(map->at(part.id).data());
|
||||
}
|
||||
else if(part.id == -1) {
|
||||
ImGui::TextUnformatted("<none>");
|
||||
}
|
||||
else{
|
||||
ImGui::Text("ID: %i", part.id);
|
||||
}
|
||||
|
||||
if(!map->empty()) {
|
||||
ImGui::SameLine();
|
||||
if(ImGui::SmallButton("Change")) {
|
||||
ImGui::OpenPopup("##WeaponPartPopup");
|
||||
}
|
||||
if(ImGui::BeginPopup("##WeaponPartPopup")) {
|
||||
if(ImGui::BeginListBox("##WeaponParts")) {
|
||||
for(const auto& mapped_part : *map) {
|
||||
if(ImGui::Selectable(mapped_part.second.data(), mapped_part.first == part.id)) {
|
||||
part.id = mapped_part.first;
|
||||
}
|
||||
if(mapped_part.first == part.id) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if(weapon.type == WeaponType::Shield ||
|
||||
(weapon.type == WeaponType::BulletLauncher && _selectedWeaponPart != 0))
|
||||
{
|
||||
ImGui::SameLine();
|
||||
if(ImGui::SmallButton("Unequip")) {
|
||||
part.id = -1;
|
||||
}
|
||||
if(weapon.type == WeaponType::Shield && _selectedWeaponPart == 0) {
|
||||
drawTooltip("This will make the whole shield and its accessories invisible.");
|
||||
}
|
||||
else {
|
||||
drawTooltip("This will make accessories invisible as well.");
|
||||
}
|
||||
}
|
||||
|
||||
if(ImGui::BeginChild("##PartDetails", {0.0f, 0.0f}, true)) {
|
||||
ImGui::TextUnformatted("Styles:");
|
||||
|
||||
for(Int i = 0; i < 4; i++) {
|
||||
drawAlignedText("Slot %d:", i + 1);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
if(ImGui::BeginCombo("##Style", getStyleName(part.styles[i], weapon.customStyles).data())) {
|
||||
for(const auto& style: style_names) {
|
||||
if(ImGui::Selectable(getStyleName(style.first, weapon.customStyles).data(),
|
||||
part.styles[i] == style.first)) {
|
||||
part.styles[i] = style.first;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushID("Decal");
|
||||
|
||||
drawAlignedText("Showing/editing decal");
|
||||
for(UnsignedLong i = 0; i < part.decals.size(); i++) {
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, int(i));
|
||||
}
|
||||
|
||||
drawDecalEditor(part.decals[_selectedWeaponDecal]);
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if(part.accessories.size() != 0) {
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushID("Accessory");
|
||||
|
||||
drawAlignedText("Showing/editing accessory");
|
||||
for(UnsignedLong i = 0; i < part.accessories.size(); i++) {
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, int(i));
|
||||
}
|
||||
|
||||
drawAccessoryEditor(part.accessories[_selectedWeaponAccessory], weapon.customStyles);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -14,12 +14,14 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
#include <Magnum/ImGuiIntegration/Integration.h>
|
||||
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
extern const ImVec2 center_pivot;
|
||||
|
||||
void SaveTool::drawProfileManager() {
|
||||
|
@ -27,7 +29,7 @@ void SaveTool::drawProfileManager() {
|
|||
|
||||
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
|
||||
if(ImGui::Begin("Profile management##ProfileManager", nullptr,
|
||||
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize|
|
||||
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBringToFrontOnFocus|
|
||||
ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
|
@ -50,7 +52,8 @@ void SaveTool::drawProfileManager() {
|
|||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::SmallButton("Refresh")) {
|
||||
if(!_profileManager->refreshProfiles()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error in ProfileManager", _profileManager->lastError().c_str(), window());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
@ -77,31 +80,36 @@ void SaveTool::drawProfileManager() {
|
|||
ImGui::TextUnformatted("Actions");
|
||||
|
||||
for(std::size_t i = 0; i < _profileManager->profiles().size(); ++i) {
|
||||
Profile& profile = _profileManager->profiles()[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
if(ImGui::Selectable(_profileManager->profiles().at(i).companyName().c_str(), false,
|
||||
ImGui::PushID(int(i));
|
||||
if(ImGui::Selectable(profile.companyName().data(), false,
|
||||
ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap))
|
||||
{
|
||||
_currentProfile = _profileManager->getProfile(i);
|
||||
initialiseMassManager();
|
||||
initialiseFileWatcher();
|
||||
_uiState = UiState::MainManager;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::TextUnformatted(_profileManager->profiles().at(i).type() == ProfileType::Demo ? "Demo" : "Full");
|
||||
ImGui::TextUnformatted(profile.isDemo() ? "Demo" : "Full");
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::PushID(i);
|
||||
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
|
||||
profile_index = i;
|
||||
ImGui::OpenPopup(backup_popup_id);
|
||||
}
|
||||
drawTooltip("Backup");
|
||||
ImGui::SameLine(0.0f, 2.0f);
|
||||
if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) {
|
||||
profile_index = i;
|
||||
ImGui::OpenPopup(delete_popup_id);
|
||||
}
|
||||
drawTooltip("Delete");
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
@ -130,15 +138,15 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
if(ImGui::BeginPopupModal("Restore backup", nullptr,
|
||||
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
ImGui::PushTextWrapPos(float(windowSize().x()) * 0.40f);
|
||||
ImGui::Text("Are you sure you want to restore the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ? Any existing data will be overwritten.",
|
||||
_profileManager->backups().at(backup_index).company.c_str(),
|
||||
_profileManager->backups().at(backup_index).timestamp.year,
|
||||
_profileManager->backups().at(backup_index).timestamp.month,
|
||||
_profileManager->backups().at(backup_index).timestamp.day,
|
||||
_profileManager->backups().at(backup_index).timestamp.hour,
|
||||
_profileManager->backups().at(backup_index).timestamp.minute,
|
||||
_profileManager->backups().at(backup_index).timestamp.second);
|
||||
_profileManager->backups()[backup_index].company.data(),
|
||||
_profileManager->backups()[backup_index].timestamp.year,
|
||||
_profileManager->backups()[backup_index].timestamp.month,
|
||||
_profileManager->backups()[backup_index].timestamp.day,
|
||||
_profileManager->backups()[backup_index].timestamp.hour,
|
||||
_profileManager->backups()[backup_index].timestamp.minute,
|
||||
_profileManager->backups()[backup_index].timestamp.second);
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
if(ImGui::BeginTable("##RestoreBackupLayout", 2)) {
|
||||
|
@ -150,10 +158,13 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::Button("Yes")) {
|
||||
if(!_profileManager->restoreBackup(backup_index)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when restoring backup",
|
||||
_profileManager->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
|
||||
}
|
||||
if(!_profileManager->refreshProfiles()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
_profileManager->refreshProfiles();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -170,15 +181,15 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
if(ImGui::BeginPopupModal("Delete backup", nullptr,
|
||||
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
ImGui::PushTextWrapPos(float(windowSize().x()) * 0.40f);
|
||||
ImGui::Text("Are you sure you want to delete the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ? This operation is irreversible.",
|
||||
_profileManager->backups().at(backup_index).company.c_str(),
|
||||
_profileManager->backups().at(backup_index).timestamp.year,
|
||||
_profileManager->backups().at(backup_index).timestamp.month,
|
||||
_profileManager->backups().at(backup_index).timestamp.day,
|
||||
_profileManager->backups().at(backup_index).timestamp.hour,
|
||||
_profileManager->backups().at(backup_index).timestamp.minute,
|
||||
_profileManager->backups().at(backup_index).timestamp.second);
|
||||
_profileManager->backups()[backup_index].company.data(),
|
||||
_profileManager->backups()[backup_index].timestamp.year,
|
||||
_profileManager->backups()[backup_index].timestamp.month,
|
||||
_profileManager->backups()[backup_index].timestamp.day,
|
||||
_profileManager->backups()[backup_index].timestamp.hour,
|
||||
_profileManager->backups()[backup_index].timestamp.minute,
|
||||
_profileManager->backups()[backup_index].timestamp.second);
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
if(ImGui::BeginTable("##DeleteBackupLayout", 2)) {
|
||||
|
@ -190,8 +201,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::Button("Yes")) {
|
||||
if(!_profileManager->deleteBackup(backup_index)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting backup",
|
||||
_profileManager->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
@ -225,7 +235,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if(_profileManager->backups().empty()) {
|
||||
if(_profileManager->backups().isEmpty()) {
|
||||
ImGui::TextDisabled("No backups were found.");
|
||||
}
|
||||
else if(ImGui::BeginTable("##Backups", 4,
|
||||
|
@ -248,41 +258,44 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
ImGui::TextUnformatted("Actions");
|
||||
|
||||
for(std::size_t i = 0; i < _profileManager->backups().size(); ++i) {
|
||||
auto& backup = _profileManager->backups()[i];
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::TextUnformatted(_profileManager->backups().at(i).company.c_str());
|
||||
ImGui::TextUnformatted(backup.company.data());
|
||||
if(ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
for(const auto& file : _profileManager->backups().at(i).includedFiles) {
|
||||
ImGui::TextUnformatted(file.c_str());
|
||||
for(const auto& file : backup.includedFiles) {
|
||||
ImGui::TextUnformatted(file.data());
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("%.4i-%.2i-%.2i %.2i:%.2i:%.2i",
|
||||
_profileManager->backups().at(i).timestamp.year,
|
||||
_profileManager->backups().at(i).timestamp.month,
|
||||
_profileManager->backups().at(i).timestamp.day,
|
||||
_profileManager->backups().at(i).timestamp.hour,
|
||||
_profileManager->backups().at(i).timestamp.minute,
|
||||
_profileManager->backups().at(i).timestamp.second);
|
||||
backup.timestamp.year,
|
||||
backup.timestamp.month,
|
||||
backup.timestamp.day,
|
||||
backup.timestamp.hour,
|
||||
backup.timestamp.minute,
|
||||
backup.timestamp.second);
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::TextUnformatted(_profileManager->backups().at(i).type == ProfileType::Demo ? "Demo" : "Full");
|
||||
ImGui::TextUnformatted(backup.type == ProfileType::Demo ? "Demo" : "Full");
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushID(int(i));
|
||||
if(ImGui::SmallButton(ICON_FA_UNDO)) {
|
||||
backup_index = i;
|
||||
ImGui::OpenPopup(restore_backup_popup_id);
|
||||
}
|
||||
drawTooltip("Restore");
|
||||
ImGui::SameLine(0.0f, 2.0f);
|
||||
if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) {
|
||||
backup_index = i;
|
||||
ImGui::OpenPopup(delete_backup_popup_id);
|
||||
}
|
||||
drawTooltip("Delete");
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
@ -328,12 +341,18 @@ auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID {
|
|||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::Button("Yes")) {
|
||||
_profileManager->backupProfile(profile_index, true);
|
||||
if(!_profileManager->backupProfile(profile_index, true)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("No", ImGui::GetItemRectSize())) {
|
||||
_profileManager->backupProfile(profile_index, false);
|
||||
if(!_profileManager->backupProfile(profile_index, false)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -361,10 +380,10 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
|
|||
delete_builds = false;
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
ImGui::Text("Are you sure you want to delete the %s %s profile ? This operation is irreversible.",
|
||||
_profileManager->profiles().at(profile_index).companyName().c_str(),
|
||||
_profileManager->profiles().at(profile_index).type() == ProfileType::Demo ? "demo" : "full game");
|
||||
ImGui::PushTextWrapPos(float(windowSize().x()) * 0.40f);
|
||||
ImGui::Text("Are you sure you want to delete the %s profile named %s ? This operation is irreversible.",
|
||||
_profileManager->profiles()[profile_index].isDemo() ? "demo" : "full game",
|
||||
_profileManager->profiles()[profile_index].companyName().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
if(ImGui::BeginTable("##DeleteProfileLayout", 2)) {
|
||||
|
@ -378,8 +397,7 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
|
|||
ImGui::TableSetColumnIndex(1);
|
||||
if(ImGui::Button("Yes")) {
|
||||
if(!_profileManager->deleteProfile(profile_index, delete_builds)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting profile",
|
||||
_profileManager->lastError().c_str(), window());
|
||||
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Utility/Format.h>
|
||||
|
||||
#include <SDL_events.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::updateCheckEvent(SDL_Event& event) {
|
||||
_updateThread.join();
|
||||
|
||||
if(event.user.code == CurlInitFailed) {
|
||||
_queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s);
|
||||
LOG_ERROR("Couldn't initialise libcurl. Update check aborted.");
|
||||
return;
|
||||
}
|
||||
else if(event.user.code == CurlError) {
|
||||
Containers::String error{static_cast<char*>(event.user.data2), CURL_ERROR_SIZE, nullptr};
|
||||
_queue.addToast(Toast::Type::Error, error, std::chrono::milliseconds{5000});
|
||||
_queue.addToast(Toast::Type::Error, static_cast<char*>(event.user.data1),
|
||||
std::chrono::milliseconds{5000});
|
||||
LOG_ERROR_FORMAT("{}: {}", static_cast<char*>(event.user.data1), static_cast<char*>(event.user.data2));
|
||||
return;
|
||||
}
|
||||
else if(event.user.code == CurlTimeout) {
|
||||
_queue.addToast(Toast::Type::Error, "The request timed out."_s);
|
||||
LOG_ERROR("The request timed out.");
|
||||
return;
|
||||
}
|
||||
else if(event.user.code != 200) {
|
||||
_queue.addToast(Toast::Type::Error,
|
||||
Utility::format("The request failed with error code {}.", event.user.code));
|
||||
LOG_ERROR_FORMAT("The request failed with error code {}.", event.user.code);
|
||||
return;
|
||||
}
|
||||
|
||||
struct Version {
|
||||
explicit Version(Containers::StringView str) {
|
||||
std::size_t start_point = 0;
|
||||
|
||||
if(str[0] == 'v') {
|
||||
start_point++;
|
||||
}
|
||||
|
||||
auto components = Containers::StringView{str.data() + start_point}.split('.');
|
||||
|
||||
major = std::strtol(components[0].data(), nullptr, 10);
|
||||
minor = std::strtol(components[1].data(), nullptr, 10);
|
||||
patch = std::strtol(components[2].data(), nullptr, 10);
|
||||
|
||||
fullVersion = major * 10000 + minor * 100 + patch;
|
||||
|
||||
if(str.hasSuffix("-pre")) {
|
||||
prerelease = true;
|
||||
}
|
||||
}
|
||||
Int fullVersion;
|
||||
Int major = 0;
|
||||
Int minor = 0;
|
||||
Int patch = 0;
|
||||
bool prerelease = false;
|
||||
|
||||
bool operator==(const Version& other) const {
|
||||
return fullVersion == other.fullVersion && prerelease == other.prerelease;
|
||||
}
|
||||
bool operator>(const Version& other) const {
|
||||
if((fullVersion > other.fullVersion) ||
|
||||
(fullVersion == other.fullVersion && !prerelease && other.prerelease))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
explicit operator Containers::String() const {
|
||||
return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : "");
|
||||
}
|
||||
};
|
||||
|
||||
static const Version current_ver{SAVETOOL_VERSION};
|
||||
|
||||
auto str = static_cast<char*>(event.user.data1);
|
||||
Containers::String response{str, strlen(str), nullptr};
|
||||
auto components = response.splitOnAnyWithoutEmptyParts("\r\n");
|
||||
|
||||
Version latest_ver{components.front()};
|
||||
|
||||
if(latest_ver > current_ver) {
|
||||
_queue.addToast(Toast::Type::Warning,
|
||||
"Your version is out of date.\nCheck the settings for more information."_s,
|
||||
std::chrono::milliseconds{5000});
|
||||
_updateAvailable = true;
|
||||
_latestVersion = Containers::String{latest_ver};
|
||||
_releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}",
|
||||
components.front());
|
||||
_downloadLink = components.back();
|
||||
}
|
||||
else if(latest_ver == current_ver || (current_ver > latest_ver && current_ver.prerelease)) {
|
||||
_queue.addToast(Toast::Type::Success, "The application is already up to date."_s);
|
||||
}
|
||||
else if(current_ver > latest_ver && !current_ver.prerelease) {
|
||||
_queue.addToast(Toast::Type::Warning,
|
||||
"Your version is more recent than the latest one in the repo. How???"_s);
|
||||
}
|
||||
}
|
||||
|
||||
inline auto writeData(char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf)-> std::size_t {
|
||||
if(!ptr || !buf) return 0;
|
||||
(*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb});
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
void SaveTool::checkForUpdates() {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = _updateEventId;
|
||||
|
||||
auto curl = curl_easy_init();
|
||||
if(!curl) {
|
||||
event.user.code = CurlInitFailed;
|
||||
}
|
||||
|
||||
if(curl) {
|
||||
Containers::String response_body{Containers::AllocatedInit, ""};
|
||||
Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE * 2};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data());
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L);
|
||||
|
||||
auto code = curl_easy_perform(curl);
|
||||
|
||||
if(code == CURLE_OK) {
|
||||
long status = 0;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
||||
event.user.code = Int(status);
|
||||
event.user.data1 = response_body.release();
|
||||
}
|
||||
else if(code == CURLE_OPERATION_TIMEDOUT) {
|
||||
event.user.code = CurlTimeout;
|
||||
}
|
||||
else {
|
||||
event.user.code = CurlError;
|
||||
event.user.data1 = const_cast<char*>(curl_easy_strerror(code));
|
||||
event.user.data2 = Containers::String{error_buffer}.release();
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <zipconf.h>
|
||||
|
||||
#include <curl/curlver.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||
|
||||
|
@ -31,7 +33,7 @@ extern const ImVec2 center_pivot;
|
|||
|
||||
void SaveTool::drawAbout() {
|
||||
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
|
||||
ImGui::SetNextWindowSize({windowSize().x() * 0.8f, windowSize().y() * 0.75f}, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize({float(windowSize().x()) * 0.8f, float(windowSize().y()) * 0.75f}, ImGuiCond_Always);
|
||||
|
||||
ImGui::OpenPopup("About##AboutPopup");
|
||||
if(!ImGui::BeginPopupModal("About##AboutPopup", &_aboutPopup,
|
||||
|
@ -57,9 +59,18 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextWrapped("This application, made for the M.A.S.S. Builder community by Guillaume Jacquemin (aka William JCM), "
|
||||
"is a rewrite of the wxWidgets-powered M.A.S.S. Builder Save Tool (formerly known as wxMASSManager).");
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* repo = "https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool";
|
||||
ImGui::Text(ICON_FA_GIT_ALT " %s", repo);
|
||||
auto website = "https://williamjcm.ovh/coding/mbst";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(website);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Open in browser")) {
|
||||
openUri(website);
|
||||
}
|
||||
auto repo = "https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool";
|
||||
drawAlignedText(ICON_FA_GIT_ALT " %s", repo);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(repo);
|
||||
|
@ -74,10 +85,10 @@ void SaveTool::drawAbout() {
|
|||
if(ImGui::CollapsingHeader("Licence")) {
|
||||
ImGui::TextWrapped("This application is made available under the terms of the GNU General Public License, version 3, the full text of which is available below:");
|
||||
|
||||
if(ImGui::BeginChild("##GPL", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static const auto licence = _rs.get("COPYING");
|
||||
if(ImGui::BeginChild("##GPL", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
static auto licence = _rs.getRaw("COPYING");
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(licence.c_str());
|
||||
ImGui::TextEx(licence.data(), licence.data() + licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -90,9 +101,8 @@ void SaveTool::drawAbout() {
|
|||
|
||||
if(ImGui::TreeNodeEx("Corrade", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::Text("Version used: %s", CORRADE_VERSION_STRING);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* corrade_website = "https://magnum.graphics/corrade";
|
||||
ImGui::Text(ICON_FA_GLOBE " %s", corrade_website);
|
||||
auto corrade_website = "https://magnum.graphics/corrade";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", corrade_website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(corrade_website);
|
||||
|
@ -104,10 +114,10 @@ void SaveTool::drawAbout() {
|
|||
|
||||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static const auto corrade_licence = _rs.get("COPYING.Corrade");
|
||||
if(ImGui::BeginChild("##CorradeLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static auto corrade_licence = _rs.getRaw("COPYING.Corrade");
|
||||
if(ImGui::BeginChild("##CorradeLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(corrade_licence.c_str());
|
||||
ImGui::TextEx(corrade_licence.data(), corrade_licence.data() + corrade_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -119,9 +129,8 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Versions used:");
|
||||
ImGui::BulletText("Magnum: %s", MAGNUM_VERSION_STRING);
|
||||
ImGui::BulletText("Integration: %s", MAGNUMINTEGRATION_VERSION_STRING);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* magnum_website = "https://magnum.graphics";
|
||||
ImGui::Text(ICON_FA_GLOBE " %s", magnum_website);
|
||||
auto magnum_website = "https://magnum.graphics";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", magnum_website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(magnum_website);
|
||||
|
@ -133,10 +142,10 @@ void SaveTool::drawAbout() {
|
|||
|
||||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static const auto magnum_licence = _rs.get("COPYING.Magnum");
|
||||
if(ImGui::BeginChild("##MagnumLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static auto magnum_licence = _rs.getRaw("COPYING.Magnum");
|
||||
if(ImGui::BeginChild("##MagnumLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(magnum_licence.c_str());
|
||||
ImGui::TextEx(magnum_licence.data(), magnum_licence.data() + magnum_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -146,9 +155,8 @@ void SaveTool::drawAbout() {
|
|||
|
||||
if(ImGui::TreeNodeEx("Dear ImGui", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::Text("Version used: %s", IMGUI_VERSION);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* imgui_repo = "https://github.com/ocornut/imgui";
|
||||
ImGui::Text(ICON_FA_GITHUB " %s", imgui_repo);
|
||||
auto imgui_repo = "https://github.com/ocornut/imgui";
|
||||
drawAlignedText(ICON_FA_GITHUB " %s", imgui_repo);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(imgui_repo);
|
||||
|
@ -160,10 +168,10 @@ void SaveTool::drawAbout() {
|
|||
|
||||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static const auto imgui_licence = _rs.get("LICENSE.ImGui");
|
||||
if(ImGui::BeginChild("##ImGuiLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static auto imgui_licence = _rs.getRaw("LICENSE.ImGui");
|
||||
if(ImGui::BeginChild("##ImGuiLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(imgui_licence.c_str());
|
||||
ImGui::TextEx(imgui_licence.data(), imgui_licence.data() + imgui_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -173,9 +181,8 @@ void SaveTool::drawAbout() {
|
|||
|
||||
if(ImGui::TreeNodeEx("Simple DirectMedia Layer (SDL) 2", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::Text("Version used: %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* sdl_website = "https://www.libsdl.org/";
|
||||
ImGui::Text(ICON_FA_GLOBE " %s", sdl_website);
|
||||
auto sdl_website = "https://www.libsdl.org/";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", sdl_website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(sdl_website);
|
||||
|
@ -187,10 +194,10 @@ void SaveTool::drawAbout() {
|
|||
|
||||
ImGui::TextUnformatted("Licence: zlib");
|
||||
|
||||
static const auto sdl_licence = _rs.get("LICENSE.SDL");
|
||||
if(ImGui::BeginChild("##SDLLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static auto sdl_licence = _rs.getRaw("LICENSE.SDL");
|
||||
if(ImGui::BeginChild("##SDLLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(sdl_licence.c_str());
|
||||
ImGui::TextEx(sdl_licence.data(), sdl_licence.data() + sdl_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -200,9 +207,8 @@ void SaveTool::drawAbout() {
|
|||
|
||||
if(ImGui::TreeNodeEx("libzip", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::Text("Version used: %s", LIBZIP_VERSION);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* libzip_website = "https://libzip.org/";
|
||||
ImGui::Text(ICON_FA_GLOBE " %s", libzip_website);
|
||||
auto libzip_website = "https://libzip.org/";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", libzip_website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(libzip_website);
|
||||
|
@ -214,10 +220,10 @@ void SaveTool::drawAbout() {
|
|||
|
||||
ImGui::TextUnformatted("Licence: 3-clause BSD");
|
||||
|
||||
static const auto libzip_licence = _rs.get("LICENSE.libzip");
|
||||
if(ImGui::BeginChild("##libzipLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static auto libzip_licence = _rs.getRaw("LICENSE.libzip");
|
||||
if(ImGui::BeginChild("##libzipLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(libzip_licence.c_str());
|
||||
ImGui::TextEx(libzip_licence.data(), libzip_licence.data() + libzip_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -226,9 +232,8 @@ void SaveTool::drawAbout() {
|
|||
}
|
||||
|
||||
if(ImGui::TreeNodeEx("Entropia File System Watcher (efsw)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* efsw_repo = "https://github.com/SpartanJ/efsw";
|
||||
ImGui::Text(ICON_FA_GITHUB " %s", efsw_repo);
|
||||
auto efsw_repo = "https://github.com/SpartanJ/efsw";
|
||||
drawAlignedText(ICON_FA_GITHUB " %s", efsw_repo);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(efsw_repo);
|
||||
|
@ -240,10 +245,36 @@ void SaveTool::drawAbout() {
|
|||
|
||||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static const auto efsw_licence = _rs.get("LICENSE.efsw");
|
||||
if(ImGui::BeginChild("##efswLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
static auto efsw_licence = _rs.getRaw("LICENSE.efsw");
|
||||
if(ImGui::BeginChild("##efswLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextUnformatted(efsw_licence.c_str());
|
||||
ImGui::TextEx(efsw_licence.data(), efsw_licence.data() + efsw_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if(ImGui::TreeNodeEx("libcurl", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::Text("Version used: %s", LIBCURL_VERSION);
|
||||
auto curl_website = "https://curl.se/libcurl";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", curl_website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(curl_website);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Open in browser")) {
|
||||
openUri(curl_website);
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted("Licence: MIT/X derivative");
|
||||
|
||||
static auto curl_licence = _rs.getRaw("LICENSE.curl");
|
||||
if(ImGui::BeginChild("##libcurlLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(curl_licence.data(), curl_licence.data() + curl_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
@ -253,9 +284,8 @@ void SaveTool::drawAbout() {
|
|||
|
||||
if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::TextUnformatted("Version used: 5.15.3");
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* fa_website = "https://fontawesome.com/";
|
||||
ImGui::Text(ICON_FA_GLOBE " %s", fa_website);
|
||||
auto fa_website = "https://fontawesome.com/";
|
||||
drawAlignedText(ICON_FA_GLOBE " %s", fa_website);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(fa_website);
|
||||
|
@ -271,9 +301,8 @@ void SaveTool::drawAbout() {
|
|||
}
|
||||
|
||||
if(ImGui::TreeNodeEx("IconFontCppHeaders", ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
const char* icon_repo = "https://github.com/juliettef/IconFontCppHeaders";
|
||||
ImGui::Text(ICON_FA_GITHUB " %s", icon_repo);
|
||||
auto icon_repo = "https://github.com/juliettef/IconFontCppHeaders";
|
||||
drawAlignedText(ICON_FA_GITHUB " %s", icon_repo);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Copy to clipboard")) {
|
||||
ImGui::SetClipboardText(icon_repo);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021 Guillaume Jacquemin
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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
|
||||
|
@ -14,40 +14,39 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
#include <Corrade/Utility/Directory.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawMainMenu() {
|
||||
if(ImGui::BeginMainMenuBar()) {
|
||||
if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) {
|
||||
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open data directory", _mbManager != nullptr)) {
|
||||
if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, _mbManager != nullptr)) {
|
||||
openUri(Utility::Directory::toNativeSeparators(_mbManager->saveDirectory() + "/Saved/Config/WindowsNoEditor"));
|
||||
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open game data directory", Utility::Path::exists(_gameDataDir))) {
|
||||
if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, Utility::Path::exists(_configDir))) {
|
||||
openUri(Utility::Path::toNativeSeparators(_configDir));
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, _profileManager != nullptr)) {
|
||||
openUri(Utility::Directory::toNativeSeparators(_profileManager->saveDirectory()));
|
||||
if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, Utility::Path::exists(_saveDir))) {
|
||||
openUri(Utility::Path::toNativeSeparators(_saveDir));
|
||||
}
|
||||
|
||||
static bool _screenshotsAvailable = Utility::Directory::exists(_mbManager->saveDirectory() + "/Saved/Screenshots/WindowsNoEditor");
|
||||
if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, _screenshotsAvailable)) {
|
||||
openUri(Utility::Directory::toNativeSeparators(_mbManager->saveDirectory() + "/Screenshots/WindowsNoEditor"));
|
||||
if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, Utility::Path::exists(_screenshotsDir))) {
|
||||
openUri(Utility::Path::toNativeSeparators(_screenshotsDir));
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open manager directory")) {
|
||||
if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, _massManager != nullptr)) {
|
||||
openUri(Utility::Directory::toNativeSeparators(_massManager->stagingAreaDirectory()));
|
||||
if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false, Utility::Path::exists(_backupsDir))) {
|
||||
openUri(Utility::Path::toNativeSeparators(_backupsDir));
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false, _profileManager != nullptr)) {
|
||||
openUri(Utility::Directory::toNativeSeparators(_profileManager->backupsDirectory()));
|
||||
if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, Utility::Path::exists(_stagingDir))) {
|
||||
openUri(Utility::Path::toNativeSeparators(_stagingDir));
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
|
@ -55,6 +54,87 @@ void SaveTool::drawMainMenu() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
if(ImGui::BeginMenu(ICON_FA_COG " Settings")) {
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Vertical sync:");
|
||||
if(_swapInterval == 0) {
|
||||
drawAlignedText("FPS cap:");
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
static const char* framelimit_labels[] = {
|
||||
"Off",
|
||||
"Every VBLANK",
|
||||
"Every second VBLANK",
|
||||
"Every third VBLANK",
|
||||
};
|
||||
|
||||
ImGui::PushItemWidth(300.0f);
|
||||
|
||||
if(ImGui::BeginCombo("##FrameLimit", framelimit_labels[_swapInterval])) {
|
||||
for(int i = 0; i <= 3; i++) {
|
||||
if(ImGui::Selectable(framelimit_labels[i], _swapInterval == i)) {
|
||||
_swapInterval = i;
|
||||
setSwapInterval(i);
|
||||
if(i == 0) {
|
||||
setMinimalLoopPeriod(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if(_swapInterval == 0) {
|
||||
ImGui::SliderFloat("##FpsCapSlider", &_fpsCap, 15.0f, 301.0f,
|
||||
_fpsCap != 301.0f ? "%.0f" : "Uncapped", ImGuiSliderFlags_AlwaysClamp);
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::Checkbox("Cheat mode", &_cheatMode);
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
drawHelpMarker("This gives access to save edition features that can be considered cheats.",
|
||||
Float(windowSize().x()) * 0.4f);
|
||||
|
||||
ImGui::Checkbox("Advanced mode", &_advancedMode);
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
drawHelpMarker("This gives access to editing values that have unknown purposes or are undocumented.",
|
||||
Float(windowSize().x()) * 0.4f);
|
||||
|
||||
ImGui::Checkbox("Check for updates on startup", &_checkUpdatesOnStartup);
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button(ICON_FA_SYNC_ALT " Check now")) {
|
||||
_queue.addToast(Toast::Type::Default, "Checking for updates...");
|
||||
_updateThread = std::thread{[this]{ checkForUpdates(); }};
|
||||
}
|
||||
|
||||
if(_updateAvailable) {
|
||||
drawAlignedText("Version %s is available.", _latestVersion.data());
|
||||
if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) {
|
||||
openUri(_releaseLink);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button(ICON_FA_DOWNLOAD " Download now")) {
|
||||
openUri(_downloadLink);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Skip disclaimer", &_skipDisclaimer);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if(ImGui::MenuItem(ICON_FA_SIGN_OUT_ALT " Quit##QuitMenuItem")) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -76,11 +156,11 @@ void SaveTool::drawMainMenu() {
|
|||
|
||||
if(ImGui::BeginMenu(ICON_FA_DISCORD " Discord communities")) {
|
||||
if(ImGui::MenuItem("Official server")) {
|
||||
openUri("https://discord.gg/quS7E46");
|
||||
openUri("https://discord.gg/sekai-project");
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Community server")) {
|
||||
openUri("https://discord.gg/YSSRTRB");
|
||||
openUri("https://discord.gg/massbuildercommunity");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
|
@ -100,12 +180,26 @@ void SaveTool::drawMainMenu() {
|
|||
#endif
|
||||
|
||||
if(ImGui::BeginMenu("Help")) {
|
||||
if(ImGui::BeginMenu(ICON_FA_KEYBOARD " Keyboard shortcuts")) {
|
||||
ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text.");
|
||||
ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
|
||||
ImGui::BulletText("While inputing text:\n");
|
||||
ImGui::Indent();
|
||||
ImGui::BulletText("CTRL+Left/Right to word jump.");
|
||||
ImGui::BulletText("CTRL+A or double-click to select all.");
|
||||
ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
|
||||
ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo.");
|
||||
ImGui::BulletText("ESCAPE to revert.");
|
||||
ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract.");
|
||||
ImGui::Unindent();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if(_mbManager != nullptr) {
|
||||
if(_gameCheckTimerId != 0) {
|
||||
if(ImGui::BeginTable("##MainMenuLayout", 2)) {
|
||||
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##GameState", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Utility/Format.h>
|
||||
|
||||
#include <Magnum/Math/Functions.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "ToastQueue.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
constexpr UnsignedInt success_colour = 0xff67d23bu;
|
||||
constexpr UnsignedInt info_colour = 0xffcc832fu;
|
||||
constexpr UnsignedInt warning_colour = 0xff2fcfc7u;
|
||||
constexpr UnsignedInt error_colour = 0xff3134cdu;
|
||||
|
||||
constexpr UnsignedInt fade_time = 150;
|
||||
constexpr Float base_opacity = 1.0f;
|
||||
constexpr Vector2 padding{20.0f, 20.0f};
|
||||
constexpr Float toast_spacing = 10.0f;
|
||||
|
||||
Toast::Toast(Type type, Containers::StringView message, std::chrono::milliseconds timeout):
|
||||
_type{type}, _message{message}, _timeout{timeout}, _creationTime{std::chrono::steady_clock::now()}
|
||||
{
|
||||
_phaseTrack = Animation::Track<UnsignedInt, Phase>{{
|
||||
{0, Phase::FadeIn},
|
||||
{fade_time, Phase::Wait},
|
||||
{fade_time + timeout.count(), Phase::FadeOut},
|
||||
{(fade_time * 2) + timeout.count(), Phase::TimedOut}
|
||||
}, Math::select, Animation::Extrapolation::Constant};
|
||||
}
|
||||
|
||||
auto Toast::type() -> Type {
|
||||
return _type;
|
||||
}
|
||||
|
||||
auto Toast::message() -> Containers::StringView {
|
||||
return _message;
|
||||
}
|
||||
|
||||
auto Toast::timeout() -> std::chrono::milliseconds {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
auto Toast::creationTime() -> std::chrono::steady_clock::time_point {
|
||||
return _creationTime;
|
||||
}
|
||||
|
||||
auto Toast::elapsedTime() -> std::chrono::milliseconds {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - _creationTime);
|
||||
}
|
||||
|
||||
auto Toast::phase() -> Phase {
|
||||
return _phaseTrack.at(elapsedTime().count());
|
||||
}
|
||||
|
||||
auto Toast::opacity() -> Float {
|
||||
Phase phase = this->phase();
|
||||
Long elapsed_time = elapsedTime().count();
|
||||
|
||||
if(phase == Phase::FadeIn) {
|
||||
return Float(elapsed_time) / Float(fade_time);
|
||||
}
|
||||
else if(phase == Phase::FadeOut) {
|
||||
return 1.0f - ((Float(elapsed_time) - Float(fade_time) - Float(_timeout.count())) / Float(fade_time));
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void ToastQueue::addToast(Toast&& toast) {
|
||||
_toasts.push_back(std::move(toast));
|
||||
}
|
||||
|
||||
void ToastQueue::addToast(Toast::Type type, Containers::StringView message, std::chrono::milliseconds timeout) {
|
||||
_toasts.emplace_back(type, message, timeout);
|
||||
}
|
||||
|
||||
void ToastQueue::draw(Vector2i viewport_size) {
|
||||
Float height = 0.0f;
|
||||
|
||||
for(UnsignedInt i = 0; i < _toasts.size(); i++) {
|
||||
Toast* current = &_toasts[i];
|
||||
|
||||
if(current->phase() == Toast::Phase::TimedOut) {
|
||||
removeToast(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
Containers::String win_id = Utility::format("##Toast{}", i);
|
||||
|
||||
Float opacity = base_opacity * current->opacity();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, opacity);
|
||||
|
||||
ImGui::SetNextWindowPos({viewport_size.x() - padding.x(), viewport_size.y() - padding.y() - height}, ImGuiCond_Always, {1.0f, 1.0f});
|
||||
if(ImGui::Begin(win_id.data(), nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoDecoration|
|
||||
ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoNav|ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
ImColor colour = 0xffffffff;
|
||||
|
||||
switch(current->type()) {
|
||||
case Toast::Type::Default:
|
||||
break;
|
||||
case Toast::Type::Success:
|
||||
colour = success_colour;
|
||||
ImGui::TextColored(colour, ICON_FA_CHECK_CIRCLE);
|
||||
break;
|
||||
case Toast::Type::Info:
|
||||
colour = info_colour;
|
||||
ImGui::TextColored(colour, ICON_FA_INFO_CIRCLE);
|
||||
break;
|
||||
case Toast::Type::Warning:
|
||||
colour = warning_colour;
|
||||
ImGui::TextColored(colour, ICON_FA_EXCLAMATION_TRIANGLE);
|
||||
break;
|
||||
case Toast::Type::Error:
|
||||
colour = error_colour;
|
||||
ImGui::TextColored(colour, ICON_FA_TIMES_CIRCLE);
|
||||
break;
|
||||
}
|
||||
|
||||
if(current->type() != Toast::Type::Default) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(500.0f);
|
||||
ImGui::TextColored(colour, current->message().data());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
height += ImGui::GetWindowHeight() + toast_spacing;
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
|
||||
void ToastQueue::removeToast(Long index) {
|
||||
_toasts.erase(_toasts.begin() + index);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Animation/Track.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class Toast {
|
||||
public:
|
||||
enum class Type : UnsignedByte {
|
||||
Default, Success, Info, Warning, Error
|
||||
};
|
||||
|
||||
enum class Phase : UnsignedByte {
|
||||
FadeIn, Wait, FadeOut, TimedOut
|
||||
};
|
||||
|
||||
explicit Toast(Type type, Containers::StringView message,
|
||||
std::chrono::milliseconds timeout = std::chrono::milliseconds{3000});
|
||||
|
||||
Toast(const Toast& other) = delete;
|
||||
Toast& operator=(const Toast& other) = delete;
|
||||
|
||||
Toast(Toast&& other) = default;
|
||||
Toast& operator=(Toast&& other) = default;
|
||||
|
||||
auto type() -> Type;
|
||||
|
||||
auto message() -> Containers::StringView;
|
||||
|
||||
auto timeout() -> std::chrono::milliseconds;
|
||||
|
||||
auto creationTime() -> std::chrono::steady_clock::time_point;
|
||||
|
||||
auto elapsedTime() -> std::chrono::milliseconds;
|
||||
|
||||
auto phase() -> Phase;
|
||||
|
||||
auto opacity() -> Float;
|
||||
|
||||
private:
|
||||
Type _type{Type::Default};
|
||||
Containers::String _message;
|
||||
std::chrono::milliseconds _timeout;
|
||||
std::chrono::steady_clock::time_point _creationTime;
|
||||
Animation::Track<UnsignedInt, Phase> _phaseTrack;
|
||||
};
|
||||
|
||||
class ToastQueue {
|
||||
public:
|
||||
void addToast(Toast&& toast);
|
||||
|
||||
void addToast(Toast::Type type, Containers::StringView message,
|
||||
std::chrono::milliseconds timeout = std::chrono::milliseconds{3000});
|
||||
|
||||
void draw(Vector2i viewport_size);
|
||||
|
||||
private:
|
||||
void removeToast(Long index);
|
||||
|
||||
std::vector<Toast> _toasts;
|
||||
};
|
|
@ -0,0 +1,127 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "BinaryReader.h"
|
||||
|
||||
BinaryReader::BinaryReader(Containers::StringView filename) {
|
||||
_file = std::fopen(filename.data(), "rb");
|
||||
|
||||
if(!_file) {
|
||||
LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
BinaryReader::~BinaryReader() {
|
||||
closeFile();
|
||||
}
|
||||
|
||||
auto BinaryReader::open() -> bool {
|
||||
return _file;
|
||||
}
|
||||
|
||||
auto BinaryReader::eof() -> bool {
|
||||
return std::feof(_file) != 0;
|
||||
}
|
||||
|
||||
auto BinaryReader::position() -> Long {
|
||||
return _ftelli64(_file);
|
||||
}
|
||||
|
||||
auto BinaryReader::seek(Long position) -> bool {
|
||||
return _fseeki64(_file, position, SEEK_SET) == 0;
|
||||
}
|
||||
|
||||
void BinaryReader::closeFile() {
|
||||
std::fclose(_file);
|
||||
_file = nullptr;
|
||||
}
|
||||
|
||||
auto BinaryReader::readChar(char& value) -> bool {
|
||||
return std::fread(&value, sizeof(char), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readByte(Byte& value) -> bool {
|
||||
return std::fread(&value, sizeof(Byte), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readUnsignedByte(UnsignedByte& value) -> bool {
|
||||
return std::fread(&value, sizeof(UnsignedByte), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readShort(Short& value) -> bool {
|
||||
return std::fread(&value, sizeof(Short), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readUnsignedShort(UnsignedShort& value) -> bool {
|
||||
return std::fread(&value, sizeof(UnsignedShort), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readInt(Int& value) -> bool {
|
||||
return std::fread(&value, sizeof(Int), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readUnsignedInt(UnsignedInt& value) -> bool {
|
||||
return std::fread(&value, sizeof(UnsignedInt), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readLong(Long& value) -> bool {
|
||||
return std::fread(&value, sizeof(Long), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readUnsignedLong(UnsignedLong& value) -> bool {
|
||||
return std::fread(&value, sizeof(UnsignedLong), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readFloat(Float& value) -> bool {
|
||||
return std::fread(&value, sizeof(Float), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readDouble(Double& value) -> bool {
|
||||
return std::fread(&value, sizeof(Double), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryReader::readArray(Containers::Array<char>& array, std::size_t count) -> bool {
|
||||
if(array.size() < count) {
|
||||
array = Containers::Array<char>{ValueInit, count};
|
||||
}
|
||||
|
||||
return std::fread(array.data(), sizeof(char), count, _file) == count;
|
||||
}
|
||||
|
||||
auto BinaryReader::readUEString(Containers::String& str) -> bool {
|
||||
UnsignedInt length = 0;
|
||||
if(!readUnsignedInt(length) || length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
str = Containers::String{ValueInit, length - 1};
|
||||
|
||||
return std::fread(str.data(), sizeof(char), length, _file) == length;
|
||||
}
|
||||
|
||||
auto BinaryReader::peekChar() -> Int {
|
||||
Int c;
|
||||
c = std::fgetc(_file);
|
||||
std::ungetc(c, _file);
|
||||
return c;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <Corrade/Containers/Containers.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class BinaryReader {
|
||||
public:
|
||||
explicit BinaryReader(Containers::StringView filename);
|
||||
~BinaryReader();
|
||||
|
||||
auto open() -> bool;
|
||||
auto eof() -> bool;
|
||||
auto position() -> Long;
|
||||
|
||||
auto seek(Long position) -> bool;
|
||||
|
||||
void closeFile();
|
||||
|
||||
auto readChar(char& value) -> bool;
|
||||
auto readByte(Byte& value) -> bool;
|
||||
auto readUnsignedByte(UnsignedByte& value) -> bool;
|
||||
auto readShort(Short& value) -> bool;
|
||||
auto readUnsignedShort(UnsignedShort& value) -> bool;
|
||||
auto readInt(Int& value) -> bool;
|
||||
auto readUnsignedInt(UnsignedInt& value) -> bool;
|
||||
auto readLong(Long& value) -> bool;
|
||||
auto readUnsignedLong(UnsignedLong& value) -> bool;
|
||||
auto readFloat(Float& value) -> bool;
|
||||
auto readDouble(Double& value) -> bool;
|
||||
auto readArray(Containers::Array<char>& array, std::size_t count) -> bool;
|
||||
|
||||
template<typename T>
|
||||
auto readValue(T& value) -> bool {
|
||||
return fread(&value, sizeof(T), 1, _file) == sizeof(T);
|
||||
}
|
||||
|
||||
template<std::size_t S>
|
||||
auto readStaticArray(Containers::StaticArray<S, char>& array) -> bool {
|
||||
return std::fread(array.data(), sizeof(char), S, _file) == S;
|
||||
}
|
||||
|
||||
auto readUEString(Containers::String& str) -> bool;
|
||||
|
||||
auto peekChar() -> Int;
|
||||
|
||||
private:
|
||||
std::FILE* _file = nullptr;
|
||||
};
|
|
@ -0,0 +1,139 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "BinaryWriter.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
BinaryWriter::BinaryWriter(Containers::StringView filename) {
|
||||
_file = std::fopen(filename.data(), "wb");
|
||||
if(!_file) {
|
||||
LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
BinaryWriter::~BinaryWriter() {
|
||||
closeFile();
|
||||
}
|
||||
|
||||
auto BinaryWriter::open() -> bool {
|
||||
return _file;
|
||||
}
|
||||
|
||||
void BinaryWriter::closeFile() {
|
||||
std::fflush(_file);
|
||||
std::fclose(_file);
|
||||
_file = nullptr;
|
||||
}
|
||||
|
||||
auto BinaryWriter::position() -> Long {
|
||||
return _ftelli64(_file);
|
||||
}
|
||||
|
||||
auto BinaryWriter::array() const -> Containers::ArrayView<const char> {
|
||||
return _data;
|
||||
}
|
||||
|
||||
auto BinaryWriter::arrayPosition() const -> UnsignedLong {
|
||||
return _index;
|
||||
}
|
||||
|
||||
auto BinaryWriter::flushToFile() -> bool {
|
||||
bool ret = writeArray(_data);
|
||||
std::fflush(_file);
|
||||
_data = Containers::Array<char>{};
|
||||
_index = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeChar(char value) -> bool {
|
||||
return std::fwrite(&value, sizeof(char), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeByte(Byte value) -> bool {
|
||||
return std::fwrite(&value, sizeof(Byte), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeUnsignedByte(UnsignedByte value) -> bool {
|
||||
return std::fwrite(&value, sizeof(UnsignedByte), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeShort(Short value) -> bool {
|
||||
return std::fwrite(&value, sizeof(Short), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeUnsignedShort(UnsignedShort value) -> bool {
|
||||
return std::fwrite(&value, sizeof(UnsignedShort), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeInt(Int value) -> bool {
|
||||
return std::fwrite(&value, sizeof(Int), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeUnsignedInt(UnsignedInt value) -> bool {
|
||||
return std::fwrite(&value, sizeof(UnsignedInt), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeLong(Long value) -> bool {
|
||||
return std::fwrite(&value, sizeof(Long), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeUnsignedLong(UnsignedLong value) -> bool {
|
||||
return std::fwrite(&value, sizeof(UnsignedLong), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeFloat(Float value) -> bool {
|
||||
return std::fwrite(&value, sizeof(Float), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeDouble(Double value) -> bool {
|
||||
return std::fwrite(&value, sizeof(Double), 1, _file) == 1;
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeArray(Containers::ArrayView<const char> array) -> bool {
|
||||
if(array.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::fwrite(array.data(), sizeof(char), array.size(), _file) == array.size();
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeUEString(Containers::StringView str) -> bool {
|
||||
if(str.size() > UINT32_MAX) {
|
||||
LOG_ERROR_FORMAT("String is too big. Expected size() < UINT32_MAX, got {} instead.", str.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
writeUnsignedInt(static_cast<UnsignedInt>(str.size()) + 1);
|
||||
|
||||
if(str.size() > 0) {
|
||||
std::size_t count = std::fwrite(str.data(), sizeof(char), str.size(), _file);
|
||||
if(count != str.size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return writeChar('\0');
|
||||
}
|
||||
|
||||
auto BinaryWriter::writeUEStringToArray(Containers::StringView value) -> UnsignedLong {
|
||||
return writeValueToArray<UnsignedInt>(UnsignedInt(value.size()) + 1u) +
|
||||
writeDataToArray(Containers::ArrayView<const char>{value}) +
|
||||
writeValueToArray<char>('\0');
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/GrowableArray.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class BinaryWriter {
|
||||
public:
|
||||
explicit BinaryWriter(Containers::StringView filename);
|
||||
~BinaryWriter();
|
||||
|
||||
BinaryWriter(const BinaryWriter& other) = delete;
|
||||
BinaryWriter& operator=(const BinaryWriter& other) = delete;
|
||||
|
||||
BinaryWriter(BinaryWriter&& other) = default;
|
||||
BinaryWriter& operator=(BinaryWriter&& other) = default;
|
||||
|
||||
auto open() -> bool;
|
||||
|
||||
void closeFile();
|
||||
|
||||
auto position() -> Long;
|
||||
|
||||
auto array() const -> Containers::ArrayView<const char>;
|
||||
auto arrayPosition() const -> UnsignedLong;
|
||||
auto flushToFile() -> bool;
|
||||
|
||||
auto writeByte(Byte value) -> bool;
|
||||
auto writeChar(char value) -> bool;
|
||||
auto writeUnsignedByte(UnsignedByte value) -> bool;
|
||||
auto writeShort(Short value) -> bool;
|
||||
auto writeUnsignedShort(UnsignedShort value) -> bool;
|
||||
auto writeInt(Int value) -> bool;
|
||||
auto writeUnsignedInt(UnsignedInt value) -> bool;
|
||||
auto writeLong(Long value) -> bool;
|
||||
auto writeUnsignedLong(UnsignedLong value) -> bool;
|
||||
auto writeFloat(Float value) -> bool;
|
||||
auto writeDouble(Double value) -> bool;
|
||||
auto writeArray(Containers::ArrayView<const char> array) -> bool;
|
||||
template<std::size_t size>
|
||||
auto writeString(const char(&str)[size]) -> bool {
|
||||
return writeArray({str, size - 1});
|
||||
}
|
||||
|
||||
template<std::size_t S>
|
||||
auto writeStaticArray(Containers::StaticArrayView<S, const char> array) -> bool {
|
||||
return std::fwrite(array.data(), sizeof(char), S, _file) == S;
|
||||
}
|
||||
|
||||
auto writeUEString(Containers::StringView str) -> bool;
|
||||
|
||||
template<typename T, typename U = std::conditional_t<std::is_trivially_copyable<T>::value, T, T&>>
|
||||
auto writeValueToArray(U value) -> UnsignedLong {
|
||||
Containers::ArrayView<T> view{&value, 1};
|
||||
return writeDataToArray(view);
|
||||
}
|
||||
|
||||
auto writeUEStringToArray(Containers::StringView value) -> UnsignedLong;
|
||||
|
||||
template<typename T>
|
||||
void writeValueToArrayAt(T& value, UnsignedLong position) {
|
||||
Containers::ArrayView<T> view{&value, 1};
|
||||
writeDataToArrayAt(view, position);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto writeDataToArray(Containers::ArrayView<T> view) -> UnsignedLong {
|
||||
arrayAppend(_data, Containers::arrayCast<const char>(view));
|
||||
_index += sizeof(T) * view.size();
|
||||
return sizeof(T) * view.size();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeDataToArrayAt(Containers::ArrayView<T> view, UnsignedLong position) {
|
||||
auto casted_view = Containers::arrayCast<const char>(view);
|
||||
for(UnsignedLong i = 0; i < casted_view.size(); i++) {
|
||||
_data[position + i] = casted_view[i];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FILE* _file = nullptr;
|
||||
|
||||
Containers::Array<char> _data;
|
||||
UnsignedLong _index = 0;
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Types/UnrealPropertyBase.h"
|
||||
#include "Types/ArrayProperty.h"
|
||||
#include "Types/SetProperty.h"
|
||||
#include "Types/StructProperty.h"
|
||||
#include "Types/GenericStructProperty.h"
|
||||
|
||||
#include "Debug.h"
|
||||
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop) {
|
||||
return debug << (*prop->name) << Utility::Debug::nospace << ":" <<
|
||||
prop->propertyType << "of" << prop->items.size() << prop->itemType;
|
||||
}
|
||||
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop) {
|
||||
return debug << (*prop->name) << Utility::Debug::nospace << ":" <<
|
||||
prop->propertyType << "of" << prop->items.size() << prop->itemType;
|
||||
}
|
||||
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop) {
|
||||
debug << (*prop->name) << Utility::Debug::nospace << ":" <<
|
||||
prop->structType << "(" << Utility::Debug::nospace << prop->propertyType << Utility::Debug::nospace <<
|
||||
") Contents:";
|
||||
for(const auto& item : prop->properties) {
|
||||
debug << "\n " << Utility::Debug::nospace << item.get();
|
||||
}
|
||||
return debug;
|
||||
}
|
||||
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop) {
|
||||
auto cast = dynamic_cast<const GenericStructProperty*>(prop);
|
||||
if(cast) {
|
||||
return debug << cast;
|
||||
}
|
||||
|
||||
return debug << (*prop->name) << Utility::Debug::nospace << ":" <<
|
||||
prop->structType << "(" << Utility::Debug::nospace << prop->propertyType << Utility::Debug::nospace << ")";
|
||||
}
|
||||
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop) {
|
||||
if(prop->propertyType == "ArrayProperty") {
|
||||
auto array_prop = dynamic_cast<const ArrayProperty*>(prop);
|
||||
if(array_prop) {
|
||||
return debug << array_prop;
|
||||
}
|
||||
}
|
||||
else if(prop->propertyType == "SetProperty") {
|
||||
auto set_prop = dynamic_cast<const SetProperty*>(prop);
|
||||
if(set_prop) {
|
||||
return debug << set_prop;
|
||||
}
|
||||
}
|
||||
else if(prop->propertyType == "StructProperty") {
|
||||
auto struct_prop = dynamic_cast<const StructProperty*>(prop);
|
||||
if(struct_prop) {
|
||||
return debug << struct_prop;
|
||||
}
|
||||
}
|
||||
|
||||
return debug << (*prop->name) << Utility::Debug::nospace << ":" << prop->propertyType;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Utility/Debug.h>
|
||||
|
||||
struct ArrayProperty;
|
||||
struct SetProperty;
|
||||
struct GenericStructProperty;
|
||||
struct StructProperty;
|
||||
struct UnrealPropertyBase;
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop);
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop);
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop);
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop);
|
||||
Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop);
|
|
@ -0,0 +1,264 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Serialisers/ArrayPropertySerialiser.h"
|
||||
#include "Serialisers/BoolPropertySerialiser.h"
|
||||
#include "Serialisers/BytePropertySerialiser.h"
|
||||
#include "Serialisers/ColourPropertySerialiser.h"
|
||||
#include "Serialisers/DateTimePropertySerialiser.h"
|
||||
#include "Serialisers/EnumPropertySerialiser.h"
|
||||
#include "Serialisers/FloatPropertySerialiser.h"
|
||||
#include "Serialisers/GuidPropertySerialiser.h"
|
||||
#include "Serialisers/IntPropertySerialiser.h"
|
||||
#include "Serialisers/MapPropertySerialiser.h"
|
||||
#include "Serialisers/ResourcePropertySerialiser.h"
|
||||
#include "Serialisers/RotatorPropertySerialiser.h"
|
||||
#include "Serialisers/StringPropertySerialiser.h"
|
||||
#include "Serialisers/SetPropertySerialiser.h"
|
||||
#include "Serialisers/StructSerialiser.h"
|
||||
#include "Serialisers/TextPropertySerialiser.h"
|
||||
#include "Serialisers/VectorPropertySerialiser.h"
|
||||
#include "Serialisers/Vector2DPropertySerialiser.h"
|
||||
|
||||
#include "Types/NoneProperty.h"
|
||||
|
||||
#include "BinaryReader.h"
|
||||
#include "BinaryWriter.h"
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "PropertySerialiser.h"
|
||||
|
||||
PropertySerialiser::PropertySerialiser() {
|
||||
arrayAppend(_serialisers, Containers::pointer<ArrayPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<BoolPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<BytePropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<ColourPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<DateTimePropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<EnumPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<FloatPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<GuidPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<IntPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<MapPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<ResourcePropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<RotatorPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<StringPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<SetPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<TextPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<VectorPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<Vector2DPropertySerialiser>());
|
||||
arrayAppend(_serialisers, Containers::pointer<StructSerialiser>());
|
||||
|
||||
arrayAppend(_collectionSerialisers, Containers::pointer<StructSerialiser>());
|
||||
}
|
||||
|
||||
auto PropertySerialiser::instance() -> PropertySerialiser& {
|
||||
static PropertySerialiser serialiser;
|
||||
return serialiser;
|
||||
}
|
||||
|
||||
auto PropertySerialiser::read(BinaryReader& reader) -> UnrealPropertyBase::ptr {
|
||||
if(reader.peekChar() < 0 || reader.eof()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Containers::String name;
|
||||
if(!reader.readUEString(name)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(name == "None") {
|
||||
return Containers::pointer<NoneProperty>();
|
||||
}
|
||||
|
||||
Containers::String type;
|
||||
if(!reader.readUEString(type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedLong value_length;
|
||||
if(!reader.readUnsignedLong(value_length)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return deserialise(std::move(name), std::move(type), value_length, reader);
|
||||
}
|
||||
|
||||
auto PropertySerialiser::readItem(BinaryReader& reader, Containers::String type, UnsignedLong value_length,
|
||||
Containers::String name) -> UnrealPropertyBase::ptr {
|
||||
if(reader.peekChar() < 0 || reader.eof()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return deserialise(std::move(name), std::move(type), value_length, reader);
|
||||
}
|
||||
|
||||
auto PropertySerialiser::readSet(BinaryReader& reader, Containers::StringView item_type,
|
||||
UnsignedInt count) -> Containers::Array<UnrealPropertyBase::ptr>
|
||||
{
|
||||
if(reader.peekChar() < 0 || reader.eof()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto serialiser = getCollectionSerialiser(item_type);
|
||||
|
||||
Containers::Array<UnrealPropertyBase::ptr> array;
|
||||
|
||||
if(serialiser) {
|
||||
Containers::String name;
|
||||
if(!reader.readUEString(name)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Containers::String type;
|
||||
if(!reader.readUEString(type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedLong value_length;
|
||||
if(!reader.readUnsignedLong(value_length)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
array = serialiser->deserialise(name, type, value_length, count, reader, *this);
|
||||
|
||||
for(auto& item : array) {
|
||||
if(item->name == Containers::NullOpt) {
|
||||
item->name.emplace(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(UnsignedInt i = 0; i < count; i++) {
|
||||
auto item = readItem(reader, item_type, UnsignedLong(-1), "");
|
||||
arrayAppend(array, std::move(item));
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
auto PropertySerialiser::deserialise(Containers::String name, Containers::String type, UnsignedLong value_length,
|
||||
BinaryReader& reader) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
UnrealPropertyBase::ptr prop;
|
||||
auto serialiser = getSerialiser(type);
|
||||
|
||||
if(serialiser == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prop = serialiser->deserialise(name, type, value_length, reader, *this);
|
||||
|
||||
if(!prop) {
|
||||
LOG_ERROR("No property.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prop->name = std::move(name);
|
||||
prop->propertyType = std::move(type);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto PropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, Containers::StringView item_type,
|
||||
UnsignedLong& bytes_written, BinaryWriter& writer) -> bool
|
||||
{
|
||||
auto serialiser = getSerialiser(item_type);
|
||||
if(!serialiser) {
|
||||
return false;
|
||||
}
|
||||
return serialiser->serialise(prop, bytes_written, writer, *this);
|
||||
}
|
||||
|
||||
auto PropertySerialiser::write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool {
|
||||
if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast<NoneProperty*>(prop.get())) {
|
||||
bytes_written += writer.writeUEStringToArray(*prop->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bytes_written += writer.writeUEStringToArray(*prop->name);
|
||||
bytes_written += writer.writeUEStringToArray(prop->propertyType);
|
||||
|
||||
UnsignedLong value_length = 0;
|
||||
UnsignedLong vl_position = writer.arrayPosition();
|
||||
|
||||
bytes_written += writer.writeValueToArray<UnsignedLong>(value_length);
|
||||
|
||||
bool ret = serialise(prop, prop->propertyType, value_length, writer);
|
||||
|
||||
writer.writeValueToArrayAt(value_length, vl_position);
|
||||
|
||||
bytes_written += value_length;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto PropertySerialiser::writeItem(UnrealPropertyBase::ptr& prop, Containers::StringView item_type,
|
||||
UnsignedLong& bytes_written, BinaryWriter& writer) -> bool
|
||||
{
|
||||
if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast<NoneProperty*>(prop.get())) {
|
||||
bytes_written += writer.writeUEStringToArray(*prop->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
return serialise(prop, item_type, bytes_written, writer);
|
||||
}
|
||||
|
||||
auto PropertySerialiser::writeSet(Containers::ArrayView<UnrealPropertyBase::ptr> props,
|
||||
Containers::StringView item_type, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer) -> bool
|
||||
{
|
||||
auto serialiser = getCollectionSerialiser(item_type);
|
||||
if(serialiser) {
|
||||
return serialiser->serialise(props, item_type, bytes_written, writer, *this);
|
||||
}
|
||||
else {
|
||||
for(auto& prop : props) {
|
||||
if(!writeItem(prop, item_type, bytes_written, writer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
auto PropertySerialiser::getSerialiser(Containers::StringView item_type) -> AbstractUnrealPropertySerialiser* {
|
||||
for(auto& item : _serialisers) {
|
||||
for(auto serialiser_type : item->types()) {
|
||||
if(item_type == serialiser_type) {
|
||||
return item.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto PropertySerialiser::getCollectionSerialiser(Containers::StringView item_type) -> AbstractUnrealCollectionPropertySerialiser* {
|
||||
for(auto& item : _collectionSerialisers) {
|
||||
for(Containers::StringView serialiser_type : item->types()) {
|
||||
if(item_type == serialiser_type) {
|
||||
return item.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/GrowableArray.h>
|
||||
#include <Corrade/Containers/String.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "Serialisers/AbstractUnrealPropertySerialiser.h"
|
||||
#include "Serialisers/AbstractUnrealCollectionPropertySerialiser.h"
|
||||
|
||||
#include "Types/UnrealPropertyBase.h"
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
class BinaryReader;
|
||||
class BinaryWriter;
|
||||
|
||||
class PropertySerialiser {
|
||||
public:
|
||||
static auto instance() -> PropertySerialiser&;
|
||||
|
||||
auto read(BinaryReader& reader) -> UnrealPropertyBase::ptr;
|
||||
auto readItem(BinaryReader& reader, Containers::String type, UnsignedLong value_length,
|
||||
Containers::String name) -> UnrealPropertyBase::ptr;
|
||||
auto readSet(BinaryReader& reader, Containers::StringView item_type, UnsignedInt count) -> Containers::Array<UnrealPropertyBase::ptr>;
|
||||
auto deserialise(Containers::String name, Containers::String type, UnsignedLong value_length,
|
||||
BinaryReader& reader) -> UnrealPropertyBase::ptr;
|
||||
|
||||
auto serialise(UnrealPropertyBase::ptr& prop, Containers::StringView item_type, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer) -> bool;
|
||||
auto write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool;
|
||||
auto writeItem(UnrealPropertyBase::ptr& prop, Containers::StringView item_type, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer) -> bool;
|
||||
auto writeSet(Containers::ArrayView<UnrealPropertyBase::ptr> props, Containers::StringView item_type,
|
||||
UnsignedLong& bytes_written, BinaryWriter& writer) -> bool;
|
||||
|
||||
private:
|
||||
PropertySerialiser();
|
||||
|
||||
auto getSerialiser(Containers::StringView item_type) -> AbstractUnrealPropertySerialiser*;
|
||||
auto getCollectionSerialiser(Containers::StringView item_type) -> AbstractUnrealCollectionPropertySerialiser*;
|
||||
|
||||
Containers::Array<AbstractUnrealPropertySerialiser::ptr> _serialisers;
|
||||
Containers::Array<AbstractUnrealCollectionPropertySerialiser::ptr> _collectionSerialisers;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/Pointer.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "../Types/UnrealPropertyBase.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class BinaryReader;
|
||||
class BinaryWriter;
|
||||
class PropertySerialiser;
|
||||
|
||||
class AbstractUnrealCollectionPropertySerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<AbstractUnrealCollectionPropertySerialiser>;
|
||||
|
||||
virtual ~AbstractUnrealCollectionPropertySerialiser() = default;
|
||||
|
||||
virtual auto types() -> Containers::ArrayView<const Containers::String> = 0;
|
||||
|
||||
virtual auto deserialise(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, UnsignedInt count, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> Containers::Array<UnrealPropertyBase::ptr> = 0;
|
||||
|
||||
virtual auto serialise(Containers::ArrayView<UnrealPropertyBase::ptr> props, Containers::StringView item_type,
|
||||
UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0;
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/Pointer.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "../Types/UnrealPropertyBase.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class BinaryReader;
|
||||
class BinaryWriter;
|
||||
class PropertySerialiser;
|
||||
|
||||
class AbstractUnrealPropertySerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<AbstractUnrealPropertySerialiser>;
|
||||
|
||||
virtual ~AbstractUnrealPropertySerialiser() = default;
|
||||
|
||||
virtual auto types() -> Containers::ArrayView<const Containers::String> = 0;
|
||||
|
||||
virtual auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr = 0;
|
||||
|
||||
virtual auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool = 0;
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/Pointer.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "../Types/UnrealPropertyBase.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class BinaryReader;
|
||||
class BinaryWriter;
|
||||
|
||||
class AbstractUnrealStructSerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<AbstractUnrealStructSerialiser>;
|
||||
|
||||
virtual ~AbstractUnrealStructSerialiser() = default;
|
||||
|
||||
virtual auto supportsType(Containers::StringView type) -> bool = 0;
|
||||
|
||||
virtual auto deserialise(BinaryReader& reader) -> UnrealPropertyBase::ptr = 0;
|
||||
|
||||
virtual auto serialise(UnrealPropertyBase::ptr& structProp, BinaryWriter& writer,
|
||||
UnsignedLong& bytes_written) -> bool = 0;
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/String.h>
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "ArrayPropertySerialiser.h"
|
||||
|
||||
auto ArrayPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
Containers::String item_type;
|
||||
if(!reader.readUEString(item_type)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read the item type of array property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in array property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedInt item_count;
|
||||
if(!reader.readUnsignedInt(item_count)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read array property {}'s item count.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prop = Containers::pointer<ArrayProperty>();
|
||||
prop->itemType = std::move(item_type);
|
||||
prop->items = serialiser.readSet(reader, prop->itemType, item_count);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto ArrayPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto array_prop = dynamic_cast<ArrayProperty*>(prop.get());
|
||||
if(!array_prop) {
|
||||
LOG_ERROR("The property is not a valid array property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
writer.writeUEStringToArray(array_prop->itemType);
|
||||
writer.writeValueToArray<char>('\0');
|
||||
bytes_written += writer.writeValueToArray<UnsignedInt>(UnsignedInt(array_prop->items.size()));
|
||||
|
||||
UnsignedLong start_pos = writer.arrayPosition();
|
||||
UnsignedLong dummy_bytes_written = 0;
|
||||
bool ret = serialiser.writeSet(array_prop->items, array_prop->itemType, dummy_bytes_written, writer);
|
||||
bytes_written += writer.arrayPosition() - start_pos;
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/Types.h>
|
||||
|
||||
#include "UnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/ArrayProperty.h"
|
||||
|
||||
using namespace Corrade;
|
||||
using namespace Magnum;
|
||||
|
||||
class ArrayPropertySerialiser : public UnrealPropertySerialiser<ArrayProperty> {
|
||||
public:
|
||||
using ptr = Containers::Pointer<ArrayPropertySerialiser>;
|
||||
|
||||
private:
|
||||
auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "BoolPropertySerialiser.h"
|
||||
|
||||
auto BoolPropertySerialiser::types() -> Containers::ArrayView<const Containers::String> {
|
||||
using namespace Containers::Literals;
|
||||
static const Containers::Array<Containers::String> types{InPlaceInit, {"BoolProperty"_s}};
|
||||
return types;
|
||||
}
|
||||
|
||||
auto BoolPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
if(value_length != 0) {
|
||||
LOG_ERROR_FORMAT("Invalid value length for bool property {}. Expected 0, got {} instead.", name, value_length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Short value;
|
||||
if(!reader.readShort(value)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read bool property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(value > 1 || value < 0) {
|
||||
LOG_ERROR_FORMAT("Bool property {}'s value is invalid. Expected 1 or 0, got {} instead.", name, value);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prop = Containers::pointer<BoolProperty>();
|
||||
prop->value = value;
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto BoolPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto bool_prop = dynamic_cast<BoolProperty*>(prop.get());
|
||||
if(!bool_prop) {
|
||||
LOG_ERROR("The property is not a valid bool property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
writer.writeValueToArray<Short>(Short(bool_prop->value));
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "AbstractUnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/BoolProperty.h"
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
class BoolPropertySerialiser : public AbstractUnrealPropertySerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<BoolPropertySerialiser>;
|
||||
|
||||
auto types() -> Containers::ArrayView<const Containers::String> override;
|
||||
|
||||
auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
|
||||
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "BytePropertySerialiser.h"
|
||||
|
||||
auto BytePropertySerialiser::types() -> Containers::ArrayView<const Containers::String> {
|
||||
using namespace Containers::Literals;
|
||||
static const Containers::Array<Containers::String> types{InPlaceInit, {"ByteProperty"_s}};
|
||||
return types;
|
||||
}
|
||||
|
||||
auto BytePropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<ByteProperty>();
|
||||
|
||||
if(value_length != UnsignedLong(-1)) {
|
||||
if(!reader.readUEString(prop->enumType)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read byte property {}'s enum type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in byte property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!reader.readUEString(prop->enumValue)) {
|
||||
LOG_ERROR("Couldn't read byte property's enum value.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prop->valueLength = value_length;
|
||||
|
||||
//UnsignedInt count = 0;
|
||||
//if(!reader.readUnsignedInt(count)) {
|
||||
// return nullptr;
|
||||
//}
|
||||
|
||||
//if(!reader.readArray(prop->value, count)) {
|
||||
// return nullptr;
|
||||
//}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto byte_prop = dynamic_cast<ByteProperty*>(prop.get());
|
||||
if(!byte_prop) {
|
||||
LOG_ERROR("The property is not a valid byte property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//writer.writeValueToArray<char>('\0');
|
||||
//bytes_written += writer.writeValueToArray<UnsignedInt>(byte_prop->value.size());
|
||||
//bytes_written += writer.writeDataToArray<char>(byte_prop->value);
|
||||
|
||||
if(byte_prop->valueLength != UnsignedLong(-1)) {
|
||||
writer.writeUEStringToArray(byte_prop->enumType);
|
||||
writer.writeValueToArray<char>('\0');
|
||||
}
|
||||
|
||||
bytes_written += writer.writeUEStringToArray(byte_prop->enumValue);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "AbstractUnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/ByteProperty.h"
|
||||
|
||||
class BytePropertySerialiser : public AbstractUnrealPropertySerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<BytePropertySerialiser>;
|
||||
|
||||
auto types() -> Containers::ArrayView<const Containers::String> override;
|
||||
|
||||
auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
|
||||
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "ColourPropertySerialiser.h"
|
||||
|
||||
auto ColourPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<ColourStructProperty>();
|
||||
|
||||
if(!reader.readFloat(prop->r) || !reader.readFloat(prop->g) ||
|
||||
!reader.readFloat(prop->b) || !reader.readFloat(prop->a))
|
||||
{
|
||||
LOG_ERROR_FORMAT("Couldn't read colour property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto ColourPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto colour_prop = dynamic_cast<ColourStructProperty*>(prop.get());
|
||||
if(!colour_prop) {
|
||||
LOG_ERROR("The property is not a valid colour property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_written += writer.writeValueToArray<Float>(colour_prop->r) + writer.writeValueToArray<Float>(colour_prop->g) +
|
||||
writer.writeValueToArray<Float>(colour_prop->b) + writer.writeValueToArray<Float>(colour_prop->a);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "UnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/ColourStructProperty.h"
|
||||
|
||||
using namespace Corrade;
|
||||
|
||||
class ColourPropertySerialiser : public UnrealPropertySerialiser<ColourStructProperty> {
|
||||
public:
|
||||
using ptr = Containers::Pointer<ColourPropertySerialiser>;
|
||||
|
||||
private:
|
||||
auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "DateTimePropertySerialiser.h"
|
||||
|
||||
auto DateTimePropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<DateTimeStructProperty>();
|
||||
|
||||
if(!reader.readUnsignedLong(prop->timestamp)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read date/time property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto DateTimePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto dt_prop = dynamic_cast<DateTimeStructProperty*>(prop.get());
|
||||
if(!dt_prop) {
|
||||
LOG_ERROR("The property is not a valid date/time property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_written += writer.writeValueToArray<UnsignedLong>(dt_prop->timestamp);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "UnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/DateTimeStructProperty.h"
|
||||
|
||||
class DateTimePropertySerialiser : public UnrealPropertySerialiser<DateTimeStructProperty> {
|
||||
public:
|
||||
using ptr = Containers::Pointer<DateTimePropertySerialiser>;
|
||||
|
||||
private:
|
||||
auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "EnumPropertySerialiser.h"
|
||||
|
||||
auto EnumPropertySerialiser::types() -> Containers::ArrayView<const Containers::String> {
|
||||
using namespace Containers::Literals;
|
||||
static const Containers::Array<Containers::String> types{InPlaceInit, {"EnumProperty"_s}};
|
||||
return types;
|
||||
}
|
||||
|
||||
auto EnumPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<EnumProperty>();
|
||||
|
||||
if(!reader.readUEString(prop->enumType)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read enum property {}'s enum type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in enum property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readUEString(prop->value)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read enum property {}'s enum value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto EnumPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto enum_prop = dynamic_cast<EnumProperty*>(prop.get());
|
||||
if(!enum_prop) {
|
||||
LOG_ERROR("The property is not a valid enum property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
writer.writeUEStringToArray(enum_prop->enumType);
|
||||
writer.writeValueToArray<char>('\0');
|
||||
bytes_written += writer.writeUEStringToArray(enum_prop->value);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "AbstractUnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/EnumProperty.h"
|
||||
|
||||
class EnumPropertySerialiser : public AbstractUnrealPropertySerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<EnumPropertySerialiser>;
|
||||
|
||||
auto types() -> Containers::ArrayView<const Containers::String> override;
|
||||
|
||||
auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
|
||||
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "FloatPropertySerialiser.h"
|
||||
|
||||
auto FloatPropertySerialiser::types() -> Containers::ArrayView<const Containers::String> {
|
||||
using namespace Containers::Literals;
|
||||
static const Containers::Array<Containers::String> types{InPlaceInit, {"FloatProperty"_s}};
|
||||
return types;
|
||||
}
|
||||
|
||||
auto FloatPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<FloatProperty>();
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in float property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readFloat(prop->value)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read float property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto FloatPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto float_prop = dynamic_cast<FloatProperty*>(prop.get());
|
||||
if(!float_prop) {
|
||||
LOG_ERROR("The property is not a valid float property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
writer.writeValueToArray<char>('\0');
|
||||
bytes_written += writer.writeValueToArray<Float>(float_prop->value);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "AbstractUnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/FloatProperty.h"
|
||||
|
||||
class FloatPropertySerialiser : public AbstractUnrealPropertySerialiser {
|
||||
public:
|
||||
using ptr = Containers::Pointer<FloatPropertySerialiser>;
|
||||
|
||||
auto types() -> Containers::ArrayView<const Containers::String> override;
|
||||
|
||||
auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
|
||||
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "GuidPropertySerialiser.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
auto GuidPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<GuidStructProperty>();
|
||||
|
||||
if(!reader.readStaticArray(prop->guid)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read GUID property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto GuidPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto guid_prop = dynamic_cast<GuidStructProperty*>(prop.get());
|
||||
if(!guid_prop) {
|
||||
LOG_ERROR("The property is not a valid byte property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_written += writer.writeDataToArray<char>({guid_prop->guid.data(), guid_prop->guid.size()});
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "UnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/GuidStructProperty.h"
|
||||
|
||||
class GuidPropertySerialiser : public UnrealPropertySerialiser<GuidStructProperty> {
|
||||
public:
|
||||
using ptr = Containers::Pointer<GuidPropertySerialiser>;
|
||||
|
||||
private:
|
||||
auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "IntPropertySerialiser.h"
|
||||
|
||||
auto IntPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<IntProperty>();
|
||||
|
||||
if(value_length == UnsignedLong(-1)) {
|
||||
if(!reader.readInt(prop->value)) {
|
||||
LOG_ERROR("Couldn't read int property's value.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prop->valueLength = UnsignedLong(-1);
|
||||
return prop;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in int property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readInt(prop->value)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read int property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prop->name.emplace(name);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto IntPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto int_prop = dynamic_cast<IntProperty*>(prop.get());
|
||||
if(!int_prop) {
|
||||
LOG_ERROR("The property is not a valid int property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(prop->valueLength != UnsignedLong(-1)) {
|
||||
writer.writeValueToArray<char>('\0');
|
||||
}
|
||||
|
||||
bytes_written += writer.writeValueToArray<Int>(int_prop->value);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include "UnrealPropertySerialiser.h"
|
||||
|
||||
#include "../Types/IntProperty.h"
|
||||
|
||||
class IntPropertySerialiser : public UnrealPropertySerialiser<IntProperty> {
|
||||
public:
|
||||
using ptr = Containers::Pointer<IntPropertySerialiser>;
|
||||
|
||||
private:
|
||||
auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length,
|
||||
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
|
||||
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer,
|
||||
PropertySerialiser& serialiser) -> bool override;
|
||||
};
|
|
@ -0,0 +1,154 @@
|
|||
// MassBuilderSaveTool
|
||||
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
#include "../Types/NoneProperty.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "MapPropertySerialiser.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type,
|
||||
UnsignedLong value_length, BinaryReader& reader,
|
||||
PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
|
||||
{
|
||||
auto prop = Containers::pointer<MapProperty>();
|
||||
|
||||
if(!reader.readUEString(prop->keyType)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read map property {}'s key type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readUEString(prop->valueType)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read map property {}'s value type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in map property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedInt null;
|
||||
if(!reader.readUnsignedInt(null) || null != 0u) {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null int in map property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedInt count;
|
||||
if(!reader.readUnsignedInt(count)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read map property {}'s item count.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Begin dirty code because the MapProperty format doesn't seem to match any of the GVAS reading stuff I've found,
|
||||
// so I'm just gonna write stuff that matches the only MapProperty I can find in MB's save files.
|
||||
|
||||
arrayReserve(prop->map, count);
|
||||
|
||||
for(UnsignedInt i = 0; i < count; i++) {
|
||||
MapProperty::KeyValuePair pair;
|
||||
|
||||
if(prop->keyType == "IntProperty"_s || prop->keyType == "StrProperty"_s) {
|
||||
pair.key = serialiser.readItem(reader, prop->keyType, -1, name);
|
||||
if(pair.key == nullptr) {
|
||||
LOG_ERROR_FORMAT("Couldn't read a valid key in map property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else { // Add other branches depending on key type, should more maps appear in the future.
|
||||
LOG_ERROR_FORMAT("Key type {} not implemented.", prop->keyType);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnrealPropertyBase::ptr value_item;
|
||||
if(prop->valueType == "StructProperty"_s) {
|
||||
while((value_item = serialiser.read(reader)) != nullptr) {
|
||||
arrayAppend(pair.values, std::move(value_item));
|
||||
|
||||
if(pair.values.back()->name == "None"_s &&
|
||||
pair.values.back()->propertyType == "NoneProperty"_s &&
|
||||
dynamic_cast<NoneProperty*>(pair.values.back().get()) != nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(prop->valueType == "ByteProperty"_s) {
|
||||
if((value_item = serialiser.readItem(reader, prop->valueType, -1, name)) == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
arrayAppend(pair.values, std::move(value_item));
|
||||
}
|
||||
|
||||
arrayAppend(prop->map, std::move(pair));
|
||||
}
|
||||
|
||||
// End dirty code
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
|
||||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto map_prop = dynamic_cast<MapProperty*>(prop.get());
|
||||
if(!map_prop) {
|
||||
LOG_ERROR("The property is not a valid map property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
writer.writeUEStringToArray(map_prop->keyType);
|
||||
writer.writeUEStringToArray(map_prop->valueType);
|
||||
writer.writeValueToArray<char>('\0');
|
||||
|
||||
UnsignedLong value_start = writer.arrayPosition();
|
||||
writer.writeValueToArray<UnsignedInt>(0u);
|
||||
|
||||
writer.writeValueToArray<UnsignedInt>(UnsignedInt(map_prop->map.size()));
|
||||
|
||||
UnsignedLong dummy_bytes_written = 0;
|
||||
for(auto& pair : map_prop->map) {
|
||||
if(!serialiser.writeItem(pair.key, map_prop->keyType, dummy_bytes_written, writer)) {
|
||||
LOG_ERROR("Couldn't write a key.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto& value : pair.values) {
|
||||
if(map_prop->valueType == "StructProperty"_s) {
|
||||
if(!serialiser.write(value, dummy_bytes_written, writer)) {
|
||||
LOG_ERROR("Couldn't write a value.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!serialiser.writeItem(value, map_prop->valueType, dummy_bytes_written, writer)) {
|
||||
LOG_ERROR("Couldn't write a value.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bytes_written += (writer.arrayPosition() - value_start);
|
||||
|
||||
return true;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue