Compare commits
77 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 |
|
@ -1,4 +1,4 @@
|
|||
build*/
|
||||
*build*/
|
||||
.idea/
|
||||
*.kdev4
|
||||
*~
|
||||
|
|
|
@ -21,16 +21,19 @@ 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)
|
||||
|
@ -47,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)
|
||||
|
@ -81,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)
|
||||
|
@ -89,12 +99,18 @@ 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_DEBUG 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)
|
||||
|
|
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`
|
||||
|
|
|
@ -11,4 +11,16 @@
|
|||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
<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>
|
||||
|
|
|
@ -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.3.2)
|
||||
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,85 +28,101 @@ 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/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/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)
|
||||
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)
|
||||
Corrade::Containers
|
||||
Corrade::Utility
|
||||
Magnum::Magnum
|
||||
Logger
|
||||
)
|
||||
|
||||
add_executable(MassBuilderSaveTool WIN32
|
||||
main.cpp
|
||||
|
@ -127,6 +143,7 @@ add_executable(MassBuilderSaveTool WIN32
|
|||
ProfileManager/ProfileManager.cpp
|
||||
Profile/Profile.h
|
||||
Profile/Profile.cpp
|
||||
Profile/PropertyNames.h
|
||||
Profile/ResourceIDs.h
|
||||
MassManager/MassManager.h
|
||||
MassManager/MassManager.cpp
|
||||
|
@ -143,6 +160,7 @@ add_executable(MassBuilderSaveTool WIN32
|
|||
Mass/Mass_Weapons.cpp
|
||||
Mass/Mass_Styles.cpp
|
||||
Mass/Mass_DecalsAccessories.cpp
|
||||
Mass/PropertyNames.h
|
||||
Mass/Weapon.h
|
||||
Mass/Weapon.cpp
|
||||
Mass/WeaponPart.h
|
||||
|
@ -164,14 +182,17 @@ add_executable(MassBuilderSaveTool WIN32
|
|||
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="Dickish Cyclops"
|
||||
SUPPORTED_GAME_VERSION="0.8.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})
|
||||
|
@ -186,15 +207,16 @@ 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;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -43,6 +43,8 @@ static const std::map<Int, Containers::StringView> mission_id_map {{
|
|||
{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"_s},
|
||||
|
@ -50,6 +52,7 @@ static const std::map<Int, Containers::StringView> mission_id_map {{
|
|||
{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"_s},
|
||||
|
|
|
@ -92,16 +92,23 @@ static const Corrade::Containers::Array<StoryProgressPoint> story_progress
|
|||
{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},
|
||||
{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},
|
||||
}
|
||||
};
|
||||
|
|
|
@ -213,110 +213,142 @@ static const std::map<Int, Containers::StringView> shield_shells {
|
|||
|
||||
// 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},
|
||||
{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},
|
||||
{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},
|
||||
{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},
|
||||
{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},
|
||||
{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 {
|
||||
|
@ -358,16 +390,18 @@ static const std::map<Int, Containers::StringView> elauncher_generators {
|
|||
{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},
|
||||
{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},
|
||||
|
@ -376,6 +410,7 @@ static const std::map<Int, Containers::StringView> elauncher_pods {
|
|||
{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},
|
||||
|
@ -384,6 +419,7 @@ static const std::map<Int, Containers::StringView> elauncher_pods {
|
|||
{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},
|
||||
|
@ -392,5 +428,6 @@ static const std::map<Int, Containers::StringView> elauncher_pods {
|
|||
{304, "Shelled Launcher (Photon)"_s},
|
||||
{305, "Widearm Launcher (Photon)"_s},
|
||||
{306, "Wingspan Launcher (Photon)"_s},
|
||||
{399, "P Base EPod (Photon)"_s},
|
||||
};
|
||||
// endregion
|
||||
|
|
|
@ -21,17 +21,14 @@
|
|||
#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/ByteProperty.h"
|
||||
#include "../UESaveFile/Types/ColourStructProperty.h"
|
||||
#include "../UESaveFile/Types/FloatProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
#include "../UESaveFile/Types/RotatorStructProperty.h"
|
||||
#include "../UESaveFile/Types/StringProperty.h"
|
||||
#include "../UESaveFile/Types/VectorStructProperty.h"
|
||||
#include "../UESaveFile/Types/Vector2DStructProperty.h"
|
||||
|
||||
#include "Mass.h"
|
||||
|
||||
|
@ -51,28 +48,28 @@ auto Mass::lastError() -> Containers::StringView {
|
|||
|
||||
auto Mass::getNameFromFile(Containers::StringView path) -> Containers::Optional<Containers::String> {
|
||||
if(!Utility::Path::exists(path)) {
|
||||
Utility::Error{} << path << "couldn't be found."_s;
|
||||
LOG_ERROR_FORMAT("{} couldn't be found.", path);
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
UESaveFile mass{path};
|
||||
|
||||
if(!mass.valid()) {
|
||||
Utility::Error{} << "The unit file seems to be corrupt."_s;
|
||||
LOG_ERROR_FORMAT("{} is invalid: {}", path, mass.lastError());
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
auto unit_data = mass.at<GenericStructProperty>("UnitData"_s);
|
||||
auto unit_data = mass.at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Couldn't find unit data in the file."_s;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, path);
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
auto name_prop = unit_data->at<StringProperty>("Name_45_A037C5D54E53456407BDF091344529BB"_s);
|
||||
auto name_prop = unit_data->at<StringProperty>(MASS_NAME);
|
||||
|
||||
if(!name_prop) {
|
||||
Utility::Error{} << "Couldn't find the name in the file."_s;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, path);
|
||||
return Containers::NullOpt;
|
||||
}
|
||||
|
||||
|
@ -80,47 +77,63 @@ auto Mass::getNameFromFile(Containers::StringView path) -> Containers::Optional<
|
|||
}
|
||||
|
||||
void Mass::refreshValues() {
|
||||
Utility::Debug{} << "=Refreshing values for" << _filename << Utility::Debug::nospace << "=";
|
||||
LOG_INFO_FORMAT("Refreshing values for {}.", _filename);
|
||||
|
||||
Containers::ScopeGuard guard{[]{ Utility::Error{} << "Refresh failed."; }};
|
||||
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))) {
|
||||
Utility::Warning{} << _filename << "does not exist in" << _folder;
|
||||
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()) {
|
||||
Utility::Error{} << _mass->lastError();
|
||||
LOG_ERROR(_mass->lastError());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_INFO("Reloading the GVAS data.");
|
||||
if(!_mass->reloadData()) {
|
||||
Utility::Error{} << _mass->lastError();
|
||||
LOG_ERROR(_mass->lastError());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("Checking the save file type.");
|
||||
if(_mass->saveType() != "/Game/Core/Save/bpSaveGameUnit.bpSaveGameUnit_C"_s) {
|
||||
Utility::Error{} << _filename << "is not a valid unit save.";
|
||||
}
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("{} is not a valid unit save.", _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto name_prop = unit_data->at<StringProperty>("Name_45_A037C5D54E53456407BDF091344529BB"_s);
|
||||
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) {
|
||||
Utility::Error{} << "Couldn't find a M.A.S.S. name in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename);
|
||||
_name = Containers::NullOpt;
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
|
@ -203,18 +216,16 @@ void Mass::refreshValues() {
|
|||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Getting the associated account.");
|
||||
auto account_prop = _mass->at<StringProperty>("Account"_s);
|
||||
if(!account_prop) {
|
||||
Utility::Error{} << "Couldn't find an account ID in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ACCOUNT, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
_account = account_prop->value;
|
||||
|
||||
guard.release();
|
||||
guard = Containers::ScopeGuard{[]{Utility::Debug{} << "Refresh successful.";}};
|
||||
|
||||
_state = State::Valid;
|
||||
}
|
||||
|
||||
|
@ -223,6 +234,7 @@ auto Mass::filename() -> Containers::StringView {
|
|||
}
|
||||
|
||||
auto Mass::name() -> Containers::StringView {
|
||||
CORRADE_INTERNAL_ASSERT(_name);
|
||||
return *_name;
|
||||
}
|
||||
|
||||
|
@ -232,6 +244,7 @@ auto Mass::setName(Containers::StringView new_name) -> bool {
|
|||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
|
||||
if(!unit_data) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
@ -239,6 +252,7 @@ auto Mass::setName(Containers::StringView new_name) -> bool {
|
|||
auto name_prop = unit_data->at<StringProperty>("Name_45_A037C5D54E53456407BDF091344529BB"_s);
|
||||
|
||||
if(!name_prop) {
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
@ -266,17 +280,20 @@ void Mass::setDirty(bool dirty) {
|
|||
}
|
||||
|
||||
void Mass::getTuning() {
|
||||
getTuningCategory("Engine"_s, _tuning.engineId, "Gears"_s, _tuning.gearIds);
|
||||
getTuningCategory(MASS_ENGINE, _tuning.engineId,
|
||||
MASS_GEARS, _tuning.gearIds);
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getTuningCategory("OS"_s, _tuning.osId, "Modules"_s, _tuning.moduleIds);
|
||||
getTuningCategory(MASS_OS, _tuning.osId,
|
||||
MASS_MODULES, _tuning.moduleIds);
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
getTuningCategory("Architect"_s, _tuning.archId, "Techs"_s, _tuning.techIds);
|
||||
getTuningCategory(MASS_ARCHITECT, _tuning.archId,
|
||||
MASS_TECHS, _tuning.techIds);
|
||||
if(_state == State::Invalid) {
|
||||
return;
|
||||
}
|
||||
|
@ -313,10 +330,10 @@ auto Mass::account() -> Containers::StringView {
|
|||
auto Mass::updateAccount(Containers::StringView new_account) -> bool {
|
||||
_account = new_account;
|
||||
|
||||
auto account = _mass->at<StringProperty>("Account"_s);
|
||||
auto account = _mass->at<StringProperty>(MASS_ACCOUNT);
|
||||
if(!account) {
|
||||
_lastError = "Couldn't find the " MASS_ACCOUNT " property."_s;
|
||||
_state = State::Invalid;
|
||||
_lastError = "Couldn't find the account property."_s;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -333,9 +350,11 @@ auto Mass::updateAccount(Containers::StringView new_account) -> bool {
|
|||
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) {
|
||||
Utility::Error{} << "Couldn't find" << big_node_prop_name << "in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", big_node_prop_name, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
@ -343,13 +362,14 @@ void Mass::getTuningCategory(Containers::StringView big_node_prop_name, Int& big
|
|||
|
||||
auto node_ids = _mass->at<ArrayProperty>(small_nodes_prop_name);
|
||||
if(!node_ids) {
|
||||
Utility::Error{} << "Couldn't find" << small_nodes_prop_name << "in" << _filename;
|
||||
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()) {
|
||||
Utility::Error{} << "Node ID arrays are not of the same size. Expected" << small_nodes_ids.size() << Utility::Debug::nospace << ", got" << node_ids->items.size() << "instead.";
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/ByteProperty.h"
|
||||
#include "../UESaveFile/Types/GenericStructProperty.h"
|
||||
|
@ -32,22 +34,25 @@ auto Mass::armourParts() -> Containers::ArrayView<ArmourPart> {
|
|||
}
|
||||
|
||||
void Mass::getArmourParts() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting armour parts.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto armour_array = unit_data->at<ArrayProperty>("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"_s);
|
||||
auto armour_array = unit_data->at<ArrayProperty>(MASS_ARMOUR_PARTS);
|
||||
if(!armour_array) {
|
||||
Utility::Error{} << "Couldn't find the armour parts array in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ARMOUR_PARTS, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(armour_array->items.size() != _armour.parts.size()) {
|
||||
Utility::Error{} << "Armour arrays are not of the same size. Expected" << _armour.parts.size() << Utility::Debug::nospace << ", got" << armour_array->items.size() << "instead.";
|
||||
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;
|
||||
}
|
||||
|
@ -56,26 +61,28 @@ void Mass::getArmourParts() {
|
|||
auto part_prop = armour_array->at<GenericStructProperty>(i);
|
||||
auto& part = _armour.parts[i];
|
||||
|
||||
auto& armour_slot = part_prop->at<ByteProperty>("Slot_3_408BA56F4C9605C7E805CF91B642249C"_s)->enumValue;
|
||||
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;
|
||||
Utility::Warning{} << "Invalid armour slot enum value in getArmourParts()."_s;
|
||||
return;
|
||||
}
|
||||
|
||||
part.id = part_prop->at<IntProperty>("ID_5_ACD101864D3481DE96EDACACC09BDD25"_s)->value;
|
||||
part.id = part_prop->at<IntProperty>(MASS_ARMOUR_ID)->value;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"_s);
|
||||
auto part_styles = part_prop->at<ArrayProperty>(MASS_ARMOUR_STYLES);
|
||||
if(!part_styles) {
|
||||
Utility::Error{} << "Part styles not found for part number" << i << "in" << _filename;
|
||||
LOG_ERROR_FORMAT("Part styles not found for part number {}.", i);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(part_styles->items.size() != part.styles.size()) {
|
||||
Utility::Error{} << "Part style arrays are not of the same size. Expected" << part.styles.size() << Utility::Debug::nospace << ", got" << part_styles->items.size() << "instead.";
|
||||
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;
|
||||
}
|
||||
|
@ -84,9 +91,9 @@ void Mass::getArmourParts() {
|
|||
part.styles[j] = part_styles->at<IntProperty>(j)->value;
|
||||
}
|
||||
|
||||
auto decals_array = part_prop->at<ArrayProperty>("Decals_42_F358794A4F18497970F56BA9627D3603"_s);
|
||||
auto decals_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_DECALS);
|
||||
if(!decals_array) {
|
||||
Utility::Error{} << "Part decals not found for part number" << i << "in" << _filename;
|
||||
LOG_ERROR_FORMAT("Part decals not found for part number {}.", i);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
@ -95,9 +102,9 @@ void Mass::getArmourParts() {
|
|||
|
||||
getDecals(part.decals, decals_array);
|
||||
|
||||
auto accs_array = part_prop->at<ArrayProperty>("Accessories_52_D902DD4241FA0050C2529596255153F3"_s);
|
||||
auto accs_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_ACCESSORIES);
|
||||
if(!accs_array) {
|
||||
Utility::Error{} << "Part accessories not found for part number" << i << "in" << _filename;
|
||||
LOG_WARNING_FORMAT("Part accessories not found for part number {}.", i);
|
||||
part.accessories = Containers::Array<Accessory>{};
|
||||
continue;
|
||||
}
|
||||
|
@ -111,11 +118,25 @@ void Mass::getArmourParts() {
|
|||
}
|
||||
|
||||
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>("UnitData"_s);
|
||||
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>("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"_s);
|
||||
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) {
|
||||
|
@ -130,7 +151,7 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool {
|
|||
|
||||
for(UnsignedInt i = 0; i < armour_array->items.size(); i++) {
|
||||
part_prop = armour_array->at<GenericStructProperty>(i);
|
||||
if(slot_str == part_prop->at<ByteProperty>("Slot_3_408BA56F4C9605C7E805CF91B642249C"_s)->enumValue) {
|
||||
if(slot_str == part_prop->at<ByteProperty>(MASS_ARMOUR_SLOT)->enumValue) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
@ -147,21 +168,22 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool {
|
|||
#include "../Maps/ArmourSlots.hpp"
|
||||
#undef c
|
||||
}
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
part_prop->at<IntProperty>("ID_5_ACD101864D3481DE96EDACACC09BDD25"_s)->value = part.id;
|
||||
part_prop->at<IntProperty>(MASS_ARMOUR_ID)->value = part.id;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"_s);
|
||||
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>("Decals_42_F358794A4F18497970F56BA9627D3603"_s);
|
||||
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>("Accessories_52_D902DD4241FA0050C2529596255153F3"_s);
|
||||
auto accs_array = part_prop->at<ArrayProperty>(MASS_ARMOUR_ACCESSORIES);
|
||||
writeAccessories(part.accessories, accs_array);
|
||||
}
|
||||
|
||||
|
@ -182,24 +204,27 @@ auto Mass::bulletLauncherAttachments() -> Containers::ArrayView<BulletLauncherAt
|
|||
}
|
||||
|
||||
void Mass::getBulletLauncherAttachments() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting the bullet launcher attachment data.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto attach_style_prop = unit_data->at<ByteProperty>("WeaponBLAttachmentStyle_65_5943FCE8406F18D2C3F69285EB23A699"_s);
|
||||
auto attach_array = unit_data->at<ArrayProperty>("WeaponBLAttachment_61_442D08F547510A4CEE1501BBAF297BA0"_s);
|
||||
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;
|
||||
Utility::Error{} << "Couldn't find bullet launcher attachments in" << _filename;
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
@ -211,25 +236,25 @@ void Mass::getBulletLauncherAttachments() {
|
|||
auto attachment_prop = attach_array->at<GenericStructProperty>(i);
|
||||
auto& attachment = _armour.blAttachment[i];
|
||||
|
||||
Containers::StringView socket = attachment_prop->at<StringProperty>("Socket_9_B9DBF30D4A1F0032A2BE2F8B342B35A9"_s)->value;
|
||||
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
|
||||
{
|
||||
Utility::Error{} << "Invalid BL attachment socket.";
|
||||
LOG_ERROR_FORMAT("Invalid attachment socket {}.", socket);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto rel_loc_prop = attachment_prop->at<VectorStructProperty>("RelativeLocation_10_2F6E75DF4C40622658340E9A22D38B02"_s);
|
||||
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>("OffsetLocation_11_F42B3DA3436948FF85752DB33722382F"_s);
|
||||
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>("RelativeRotation_12_578140464621245132CFF2A2AD85E735"_s);
|
||||
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>("OffsetRotation_13_B5980BCD47905D842D1490A1A520B064"_s);
|
||||
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>("RelativeScale_16_37BC80EF42699F79533F7AA7B3094E38"_s);
|
||||
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};
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +265,8 @@ void Mass::getBulletLauncherAttachments() {
|
|||
#include "../Maps/BulletLauncherAttachmentStyles.hpp"
|
||||
#undef c
|
||||
{
|
||||
Utility::Error{} << "Unknown BL attachment style enumerator.";
|
||||
LOG_ERROR_FORMAT("Invalid attachment style {}.", attach_style);
|
||||
_state = State::Invalid;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -249,26 +275,31 @@ void Mass::getBulletLauncherAttachments() {
|
|||
}
|
||||
|
||||
auto Mass::writeBulletLauncherAttachments() -> bool {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Writing bullet launcher attachments.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No unit data in " + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto attach_style_prop = unit_data->at<ByteProperty>("WeaponBLAttachmentStyle_65_5943FCE8406F18D2C3F69285EB23A699"_s);
|
||||
auto attach_array = unit_data->at<ArrayProperty>("WeaponBLAttachment_61_442D08F547510A4CEE1501BBAF297BA0"_s);
|
||||
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) {
|
||||
_armour.blAttachmentStyle = BulletLauncherAttachmentStyle::NotFound;
|
||||
_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;
|
||||
_lastError = "Couldn't find the attachments in " + _filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -279,33 +310,34 @@ auto Mass::writeBulletLauncherAttachments() -> bool {
|
|||
auto attachment_prop = attach_array->at<GenericStructProperty>(i);
|
||||
auto& attachment = _armour.blAttachment[i];
|
||||
|
||||
auto& socket = attachment_prop->at<StringProperty>("Socket_9_B9DBF30D4A1F0032A2BE2F8B342B35A9"_s)->value;
|
||||
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>("RelativeLocation_10_2F6E75DF4C40622658340E9A22D38B02"_s);
|
||||
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>("OffsetLocation_11_F42B3DA3436948FF85752DB33722382F"_s);
|
||||
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>("RelativeRotation_12_578140464621245132CFF2A2AD85E735"_s);
|
||||
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>("OffsetRotation_13_B5980BCD47905D842D1490A1A520B064"_s);
|
||||
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>("RelativeScale_16_37BC80EF42699F79533F7AA7B3094E38"_s);
|
||||
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();
|
||||
|
@ -314,7 +346,7 @@ auto Mass::writeBulletLauncherAttachments() -> bool {
|
|||
|
||||
if(!attach_style_prop) {
|
||||
attach_style_prop = new ByteProperty;
|
||||
attach_style_prop->name.emplace("WeaponBLAttachmentStyle_65_5943FCE8406F18D2C3F69285EB23A699"_s);
|
||||
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));
|
||||
|
@ -329,6 +361,7 @@ auto Mass::writeBulletLauncherAttachments() -> bool {
|
|||
#undef c
|
||||
default:
|
||||
_lastError = "Unknown BL attachment style.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -345,22 +378,25 @@ auto Mass::armourCustomStyles() -> Containers::ArrayView<CustomStyle> {
|
|||
}
|
||||
|
||||
void Mass::getArmourCustomStyles() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting the custom armour styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto armour_styles = unit_data->at<ArrayProperty>("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"_s);
|
||||
auto armour_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_ARMOUR_STYLES);
|
||||
if(!armour_styles) {
|
||||
Utility::Error{} << "Couldn't find custom armour styles in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_CUSTOM_ARMOUR_STYLES, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(armour_styles->items.size() != _armour.customStyles.size()) {
|
||||
Utility::Error{} << "Custom armour style arrays are not of the same size. Expected" << _armour.customStyles.size() << Utility::Debug::nospace << ", got" << armour_styles->items.size() << "instead.";
|
||||
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;
|
||||
}
|
||||
|
@ -369,21 +405,26 @@ void Mass::getArmourCustomStyles() {
|
|||
}
|
||||
|
||||
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>("UnitData"_s);
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "Couldn't find unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto armour_styles = unit_data->at<ArrayProperty>("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"_s);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
// 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"
|
||||
|
@ -34,21 +35,21 @@ void Mass::getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_a
|
|||
CORRADE_INTERNAL_ASSERT(decal_prop);
|
||||
auto& decal = decals[i];
|
||||
|
||||
decal.id = decal_prop->at<IntProperty>("ID_3_694C0B35404D8A3168AEC89026BC8CF9"_s)->value;
|
||||
auto colour_prop = decal_prop->at<ColourStructProperty>("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"_s);
|
||||
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>("Position_41_022C8FE84E1AAFE587261E88F2C72250"_s);
|
||||
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>("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"_s);
|
||||
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>("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"_s);
|
||||
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>("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"_s);
|
||||
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>("Scale_32_959D1C2747AFD8D62808468235CBBA40"_s)->value;
|
||||
decal.rotation = decal_prop->at<FloatProperty>("Rotation_27_12D7C314493D203D5C2326A03C5F910F"_s)->value;
|
||||
decal.flip = decal_prop->at<BoolProperty>("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1"_s)->value;
|
||||
decal.wrap = decal_prop->at<BoolProperty>("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62"_s)->value;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,31 +59,31 @@ void Mass::writeDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal
|
|||
CORRADE_INTERNAL_ASSERT(decal_prop);
|
||||
auto& decal = decals[i];
|
||||
|
||||
decal_prop->at<IntProperty>("ID_3_694C0B35404D8A3168AEC89026BC8CF9"_s)->value = decal.id;
|
||||
auto colour_prop = decal_prop->at<ColourStructProperty>("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"_s);
|
||||
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>("Position_41_022C8FE84E1AAFE587261E88F2C72250"_s);
|
||||
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>("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"_s);
|
||||
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>("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"_s);
|
||||
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>("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"_s);
|
||||
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>("Scale_32_959D1C2747AFD8D62808468235CBBA40"_s)->value = decal.scale;
|
||||
decal_prop->at<FloatProperty>("Rotation_27_12D7C314493D203D5C2326A03C5F910F"_s)->value = decal.rotation;
|
||||
decal_prop->at<BoolProperty>("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1"_s)->value = decal.flip;
|
||||
decal_prop->at<BoolProperty>("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62"_s)->value = decal.wrap;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,21 +93,21 @@ void Mass::getAccessories(Containers::ArrayView<Accessory> accessories, ArrayPro
|
|||
CORRADE_INTERNAL_ASSERT(acc_prop);
|
||||
auto& accessory = accessories[i];
|
||||
|
||||
accessory.attachIndex = acc_prop->at<IntProperty>("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20"_s)->value;
|
||||
accessory.id = acc_prop->at<IntProperty>("ID_4_5757B32647BAE263266259B8A7DFFFC1"_s)->value;
|
||||
auto acc_styles = acc_prop->at<ArrayProperty>("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"_s);
|
||||
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>("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"_s);
|
||||
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>("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"_s);
|
||||
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>("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"_s);
|
||||
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>("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"_s);
|
||||
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>("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"_s);
|
||||
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};
|
||||
}
|
||||
}
|
||||
|
@ -117,29 +118,29 @@ void Mass::writeAccessories(Containers::ArrayView<Accessory> accessories, ArrayP
|
|||
CORRADE_INTERNAL_ASSERT(acc_prop);
|
||||
auto& accessory = accessories[i];
|
||||
|
||||
acc_prop->at<IntProperty>("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20"_s)->value = accessory.attachIndex;
|
||||
acc_prop->at<IntProperty>("ID_4_5757B32647BAE263266259B8A7DFFFC1"_s)->value = accessory.id;
|
||||
auto acc_styles = acc_prop->at<ArrayProperty>("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"_s);
|
||||
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>("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"_s);
|
||||
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>("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"_s);
|
||||
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>("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"_s);
|
||||
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>("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"_s);
|
||||
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>("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"_s);
|
||||
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();
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
// 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"
|
||||
|
@ -29,132 +31,137 @@ auto Mass::jointSliders() -> Joints& {
|
|||
}
|
||||
|
||||
void Mass::getJointSliders() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting joint sliders.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Can't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"_s);
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame_prop) {
|
||||
Utility::Error{} << "Can't find frame data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto length = frame_prop->at<FloatProperty>("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"_s);
|
||||
auto length = frame_prop->at<FloatProperty>(MASS_JOINT_NECK);
|
||||
_frame.joints.neck = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_BODY);
|
||||
_frame.joints.body = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_SHOULDER);
|
||||
_frame.joints.shoulders = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_HIP);
|
||||
_frame.joints.hips = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_ARM_UPPER);
|
||||
_frame.joints.upperArms = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_ARM_LOWER);
|
||||
_frame.joints.lowerArms = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_LEG_UPPER);
|
||||
_frame.joints.upperLegs = (length ? length->value : 0.0f);
|
||||
length = frame_prop->at<FloatProperty>("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"_s);
|
||||
length = frame_prop->at<FloatProperty>(MASS_JOINT_LEG_LOWER);
|
||||
_frame.joints.lowerLegs = (length ? length->value : 0.0f);
|
||||
}
|
||||
|
||||
auto Mass::writeJointSliders() -> bool {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Writing joint sliders");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"_s);
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
|
||||
if(!frame_prop) {
|
||||
_state = State::Invalid;
|
||||
_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>("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"_s);
|
||||
auto length = frame_prop->atMove<FloatProperty>(MASS_JOINT_NECK);
|
||||
if(_frame.joints.neck != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"_s);
|
||||
length->name.emplace(MASS_JOINT_NECK);
|
||||
}
|
||||
length->value = _frame.joints.neck;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_BODY);
|
||||
if(_frame.joints.body != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"_s);
|
||||
length->name.emplace(MASS_JOINT_BODY);
|
||||
}
|
||||
length->value = _frame.joints.body;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_SHOULDER);
|
||||
if(_frame.joints.shoulders != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"_s);
|
||||
length->name.emplace(MASS_JOINT_SHOULDER);
|
||||
}
|
||||
length->value = _frame.joints.shoulders;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_ARM_UPPER);
|
||||
if(_frame.joints.upperArms != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"_s);
|
||||
length->name.emplace(MASS_JOINT_ARM_UPPER);
|
||||
}
|
||||
length->value = _frame.joints.upperArms;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_ARM_LOWER);
|
||||
if(_frame.joints.lowerArms != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"_s);
|
||||
length->name.emplace(MASS_JOINT_ARM_LOWER);
|
||||
}
|
||||
length->value = _frame.joints.lowerArms;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_HIP);
|
||||
if(_frame.joints.hips != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"_s);
|
||||
length->name.emplace(MASS_JOINT_HIP);
|
||||
}
|
||||
length->value = _frame.joints.hips;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_LEG_UPPER);
|
||||
if(_frame.joints.upperLegs != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"_s);
|
||||
length->name.emplace(MASS_JOINT_LEG_UPPER);
|
||||
}
|
||||
length->value = _frame.joints.upperLegs;
|
||||
arrayAppend(temp, std::move(length));
|
||||
}
|
||||
|
||||
length = frame_prop->atMove<FloatProperty>("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"_s);
|
||||
length = frame_prop->atMove<FloatProperty>(MASS_JOINT_LEG_LOWER);
|
||||
if(_frame.joints.lowerLegs != 0.0f) {
|
||||
if(!length) {
|
||||
length.emplace();
|
||||
length->name.emplace("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"_s);
|
||||
length->name.emplace(MASS_JOINT_LEG_LOWER);
|
||||
}
|
||||
length->value = _frame.joints.lowerLegs;
|
||||
arrayAppend(temp, std::move(length));
|
||||
|
@ -179,29 +186,32 @@ auto Mass::frameStyles() -> Containers::ArrayView<Int> {
|
|||
}
|
||||
|
||||
void Mass::getFrameStyles() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting frame styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Can't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"_s);
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame_prop) {
|
||||
Utility::Error{} << "Can't find frame data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_styles = frame_prop->at<ArrayProperty>("Styles_32_00A3B3284B37F1E7819458844A20EB48"_s);
|
||||
auto frame_styles = frame_prop->at<ArrayProperty>(MASS_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
Utility::Error{} << "Can't find frame styles in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME_STYLES, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(frame_styles->items.size() != _frame.styles.size()) {
|
||||
Utility::Error{} << "Frame style arrays are not of the same size. Expected" << _frame.styles.size() << Utility::Debug::nospace << ", got" << frame_styles->items.size() << "instead.";
|
||||
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;
|
||||
}
|
||||
|
@ -212,24 +222,29 @@ void Mass::getFrameStyles() {
|
|||
}
|
||||
|
||||
auto Mass::writeFrameStyles() -> bool {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Writing frame styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"_s);
|
||||
auto frame = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No frame data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame_styles = frame->at<ArrayProperty>("Styles_32_00A3B3284B37F1E7819458844A20EB48"_s);
|
||||
auto frame_styles = frame->at<ArrayProperty>(MASS_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No frame styles in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -250,23 +265,25 @@ auto Mass::eyeFlareColour() -> Color4& {
|
|||
}
|
||||
|
||||
void Mass::getEyeFlareColour() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting the eye flare colour.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Can't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"_s);
|
||||
auto frame_prop = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame_prop) {
|
||||
Utility::Error{} << "Can't find frame data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto eye_flare_prop = frame_prop->at<ColourStructProperty>("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"_s);
|
||||
auto eye_flare_prop = frame_prop->at<ColourStructProperty>(MASS_EYE_FLARE);
|
||||
if(!eye_flare_prop) {
|
||||
Utility::Error{} << "Can't find eye flare data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_EYE_FLARE, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
@ -275,24 +292,29 @@ void Mass::getEyeFlareColour() {
|
|||
}
|
||||
|
||||
auto Mass::writeEyeFlareColour() -> bool {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Writing the eye flare colour.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"_s);
|
||||
auto frame = unit_data->at<GenericStructProperty>(MASS_FRAME);
|
||||
if(!frame) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No frame data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto eye_flare_prop = frame->at<ColourStructProperty>("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"_s);
|
||||
auto eye_flare_prop = frame->at<ColourStructProperty>(MASS_EYE_FLARE);
|
||||
if(!eye_flare_prop) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No eye flare property in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -314,22 +336,25 @@ auto Mass::frameCustomStyles() -> Containers::ArrayView<CustomStyle> {
|
|||
}
|
||||
|
||||
void Mass::getFrameCustomStyles() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting the frame's custom styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Can't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame_styles = unit_data->at<ArrayProperty>("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"_s);
|
||||
auto frame_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
Utility::Error{} << "Can't find frame styles in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_CUSTOM_FRAME_STYLES, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(frame_styles->items.size() != _frame.customStyles.size()) {
|
||||
Utility::Error{} << "Frame custom style arrays are not of the same size. Expected" << _frame.customStyles.size() << Utility::Debug::nospace << ", got" << frame_styles->items.size() << "instead.";
|
||||
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;
|
||||
}
|
||||
|
@ -338,22 +363,27 @@ void Mass::getFrameCustomStyles() {
|
|||
}
|
||||
|
||||
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>("UnitData"_s);
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No unit data in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frame_styles = unit_data->at<ArrayProperty>("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"_s);
|
||||
auto frame_styles = unit_data->at<ArrayProperty>(MASS_CUSTOM_FRAME_STYLES);
|
||||
if(!frame_styles) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No frame styles in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
// 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"
|
||||
|
@ -30,15 +32,18 @@ auto Mass::globalStyles() -> Containers::ArrayView<CustomStyle> {
|
|||
}
|
||||
|
||||
void Mass::getGlobalStyles() {
|
||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||
LOG_INFO("Getting global styles.");
|
||||
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Can't find unit data in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
auto global_styles = unit_data->at<ArrayProperty>("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"_s);
|
||||
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;
|
||||
}
|
||||
|
@ -51,22 +56,27 @@ void Mass::getGlobalStyles() {
|
|||
}
|
||||
|
||||
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>("UnitData"_s);
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No unit data found in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto global_styles = unit_data->at<ArrayProperty>("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"_s);
|
||||
auto global_styles = unit_data->at<ArrayProperty>(MASS_GLOBAL_STYLES);
|
||||
if(!global_styles) {
|
||||
_state = State::Invalid;
|
||||
_lastError = "No global styles found in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_state = State::Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -78,52 +88,53 @@ void Mass::getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayPrope
|
|||
auto style_prop = style_array->at<GenericStructProperty>(i);
|
||||
auto& style = styles[i];
|
||||
|
||||
style.name = style_prop->at<StringProperty>("Name_27_1532115A46EF2B2FA283908DF561A86B"_s)->value;
|
||||
auto colour_prop = style_prop->at<ColourStructProperty>("Color_5_F0D383DF40474C9464AE48A0984A212E"_s);
|
||||
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>("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9"_s)->value;
|
||||
style.gloss = style_prop->at<FloatProperty>("Gloss_11_9769599842CC275A401C4282A236E240"_s)->value;
|
||||
style.glow = colour_prop->a == 0.0f ? false : true;
|
||||
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>("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311"_s)->value;
|
||||
style.opacity = style_prop->at<FloatProperty>("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131"_s)->value;
|
||||
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>("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48"_s)->value,
|
||||
style_prop->at<FloatProperty>("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F"_s)->value
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OFFSETX)->value,
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_PATTERN_OFFSETY)->value
|
||||
};
|
||||
style.rotation = style_prop->at<FloatProperty>("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D"_s)->value;
|
||||
style.scale = style_prop->at<FloatProperty>("Scale_26_19DF0708409262183E1247B317137671"_s)->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 = "Mass::setCustomStyle(): style_array is null."_s;
|
||||
_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>("Name_27_1532115A46EF2B2FA283908DF561A86B"_s)->value = style.name;
|
||||
auto colour_prop = style_prop->at<ColourStructProperty>("Color_5_F0D383DF40474C9464AE48A0984A212E"_s);
|
||||
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>("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9"_s)->value = style.metallic;
|
||||
style_prop->at<FloatProperty>("Gloss_11_9769599842CC275A401C4282A236E240"_s)->value = style.gloss;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_METALLIC)->value = style.metallic;
|
||||
style_prop->at<FloatProperty>(MASS_STYLE_GLOSS)->value = style.gloss;
|
||||
|
||||
style_prop->at<IntProperty>("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311"_s)->value = style.patternId;
|
||||
style_prop->at<FloatProperty>("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131"_s)->value = style.opacity;
|
||||
style_prop->at<FloatProperty>("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48"_s)->value = style.offset.x();
|
||||
style_prop->at<FloatProperty>("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F"_s)->value = style.offset.y();
|
||||
style_prop->at<FloatProperty>("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D"_s)->value = style.rotation;
|
||||
style_prop->at<FloatProperty>("Scale_26_19DF0708409262183E1247B317137671"_s)->value = style.scale;
|
||||
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();
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
// 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"
|
||||
|
@ -31,11 +33,13 @@ auto Mass::meleeWeapons() -> Containers::ArrayView<Weapon> {
|
|||
}
|
||||
|
||||
void Mass::getMeleeWeapons() {
|
||||
getWeaponType("WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7"_s, _weapons.melee);
|
||||
LOG_INFO("Getting melee weapons.");
|
||||
getWeaponType(MASS_WEAPONS_MELEE, _weapons.melee);
|
||||
}
|
||||
|
||||
auto Mass::writeMeleeWeapons() -> bool {
|
||||
return writeWeaponType("WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7"_s, _weapons.melee);
|
||||
LOG_INFO("Writing melee weapons.");
|
||||
return writeWeaponType(MASS_WEAPONS_MELEE, _weapons.melee);
|
||||
}
|
||||
|
||||
auto Mass::shields() -> Containers::ArrayView<Weapon> {
|
||||
|
@ -43,11 +47,13 @@ auto Mass::shields() -> Containers::ArrayView<Weapon> {
|
|||
}
|
||||
|
||||
void Mass::getShields() {
|
||||
getWeaponType("Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E"_s, _weapons.shields);
|
||||
LOG_INFO("Getting shields.");
|
||||
getWeaponType(MASS_WEAPONS_SHIELD, _weapons.shields);
|
||||
}
|
||||
|
||||
auto Mass::writeShields() -> bool {
|
||||
return writeWeaponType("Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E"_s, _weapons.shields);
|
||||
LOG_INFO("Writing shields.");
|
||||
return writeWeaponType(MASS_WEAPONS_SHIELD, _weapons.shields);
|
||||
}
|
||||
|
||||
auto Mass::bulletShooters() -> Containers::ArrayView<Weapon> {
|
||||
|
@ -55,11 +61,13 @@ auto Mass::bulletShooters() -> Containers::ArrayView<Weapon> {
|
|||
}
|
||||
|
||||
void Mass::getBulletShooters() {
|
||||
getWeaponType("WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED"_s, _weapons.bulletShooters);
|
||||
LOG_INFO("Getting bullet shooters.");
|
||||
getWeaponType(MASS_WEAPONS_BSHOOTER, _weapons.bulletShooters);
|
||||
}
|
||||
|
||||
auto Mass::writeBulletShooters() -> bool {
|
||||
return writeWeaponType("WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED"_s, _weapons.bulletShooters);
|
||||
LOG_INFO("Writing bullet shooters.");
|
||||
return writeWeaponType(MASS_WEAPONS_BSHOOTER, _weapons.bulletShooters);
|
||||
}
|
||||
|
||||
auto Mass::energyShooters() -> Containers::ArrayView<Weapon> {
|
||||
|
@ -67,11 +75,13 @@ auto Mass::energyShooters() -> Containers::ArrayView<Weapon> {
|
|||
}
|
||||
|
||||
void Mass::getEnergyShooters() {
|
||||
getWeaponType("WeaponES_37_1A295D544528623880A0B1AC2C7DEE99"_s, _weapons.energyShooters);
|
||||
LOG_INFO("Getting energy shooters.");
|
||||
getWeaponType(MASS_WEAPONS_ESHOOTER, _weapons.energyShooters);
|
||||
}
|
||||
|
||||
auto Mass::writeEnergyShooters() -> bool {
|
||||
return writeWeaponType("WeaponES_37_1A295D544528623880A0B1AC2C7DEE99"_s, _weapons.energyShooters);
|
||||
LOG_INFO("Writing energy shooters.");
|
||||
return writeWeaponType(MASS_WEAPONS_ESHOOTER, _weapons.energyShooters);
|
||||
}
|
||||
|
||||
auto Mass::bulletLaunchers() -> Containers::ArrayView<Weapon> {
|
||||
|
@ -79,11 +89,13 @@ auto Mass::bulletLaunchers() -> Containers::ArrayView<Weapon> {
|
|||
}
|
||||
|
||||
void Mass::getBulletLaunchers() {
|
||||
getWeaponType("WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E"_s, _weapons.bulletLaunchers);
|
||||
LOG_INFO("Getting bullet launchers.");
|
||||
getWeaponType(MASS_WEAPONS_BLAUNCHER, _weapons.bulletLaunchers);
|
||||
}
|
||||
|
||||
auto Mass::writeBulletLaunchers() -> bool {
|
||||
return writeWeaponType("WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E"_s, _weapons.bulletLaunchers);
|
||||
LOG_INFO("Writing bullet launchers.");
|
||||
return writeWeaponType(MASS_WEAPONS_BLAUNCHER, _weapons.bulletLaunchers);
|
||||
}
|
||||
|
||||
auto Mass::energyLaunchers() -> Containers::ArrayView<Weapon> {
|
||||
|
@ -91,30 +103,33 @@ auto Mass::energyLaunchers() -> Containers::ArrayView<Weapon> {
|
|||
}
|
||||
|
||||
void Mass::getEnergyLaunchers() {
|
||||
getWeaponType("WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995"_s, _weapons.energyLaunchers);
|
||||
LOG_INFO("Getting energy launchers.");
|
||||
getWeaponType(MASS_WEAPONS_ELAUNCHER, _weapons.energyLaunchers);
|
||||
}
|
||||
|
||||
auto Mass::writeEnergyLaunchers() -> bool {
|
||||
return writeWeaponType("WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995"_s, _weapons.energyLaunchers);
|
||||
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>("UnitData"_s);
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Can't find unit data in" << _filename;
|
||||
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) {
|
||||
Utility::Error{} << "Can't find" << prop_name << "in" << _filename;
|
||||
LOG_ERROR_FORMAT("Couldn't find {} in {}.", prop_name, _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(prop->items.size() != weapon_array.size()) {
|
||||
Utility::Error{} << "Weapon arrays are not of the same size. Expected" << weapon_array.size() << Utility::Debug::nospace << ", got" << prop->items.size() << "instead.";
|
||||
LOG_ERROR_FORMAT("Weapon arrays are not of the same size. Expected {}, got {} instead.",
|
||||
weapon_array.size(), prop->items.size());
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
@ -123,38 +138,39 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView
|
|||
auto weapon_prop = prop->at<GenericStructProperty>(i);
|
||||
auto& weapon = weapon_array[i];
|
||||
|
||||
weapon.name = weapon_prop->at<StringProperty>("Name_13_7BF0D31F4E50C50C47231BB36A485D92"_s)->value;
|
||||
auto& weapon_type = weapon_prop->at<ByteProperty>("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976"_s)->enumValue;
|
||||
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;
|
||||
Utility::Warning{} << "Invalid weapon type enum value in getWeaponType()."_s;
|
||||
return;
|
||||
}
|
||||
|
||||
auto parts_prop = weapon_prop->at<ArrayProperty>("Element_6_8E4617CC4B2C1F1490435599784EC6E0"_s);
|
||||
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>("ID_2_A74D75434308158E5926178822DD28EE"_s)->value;
|
||||
part.id = part_prop->at<IntProperty>(MASS_WEAPON_PART_ID)->value;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>("Styles_17_994C97C34A90667BE5B716BFD0B97588"_s);
|
||||
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>("Decals_13_8B81112B453D7230C0CDE982185E14F1"_s);
|
||||
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>("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"_s);
|
||||
auto part_accs = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_ACCESSORIES);
|
||||
if(!part_accs) {
|
||||
part.accessories = Containers::Array<Accessory>{0};
|
||||
continue;
|
||||
|
@ -166,62 +182,69 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView
|
|||
getAccessories(part.accessories, part_accs);
|
||||
}
|
||||
|
||||
auto custom_styles = weapon_prop->at<ArrayProperty>("Styles_10_8C3C82444B986AD7A99595AD4985912D"_s);
|
||||
auto custom_styles = weapon_prop->at<ArrayProperty>(MASS_CUSTOM_WEAPON_STYLES);
|
||||
if(!custom_styles) {
|
||||
Utility::Error{} << "Can't find weapon custom styles in" << _filename;
|
||||
LOG_ERROR_FORMAT("Can't find weapon custom styles in {}", _filename);
|
||||
_state = State::Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if(custom_styles->items.size() != weapon.customStyles.size()) {
|
||||
Utility::Error{} << "Weapon custom style arrays are not of the same size. Expected" << weapon.customStyles.size() << Utility::Debug::nospace << ", got" << custom_styles->items.size() << "instead.";
|
||||
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>("Attach_15_D00AABBD4AD6A04778D56D81E51927B3"_s)->value;
|
||||
auto& damage_type = weapon_prop->at<ByteProperty>("DamageType_18_E1FFA53540591A9087EC698117A65C83"_s)->enumValue;
|
||||
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;
|
||||
Utility::Warning{} << "Invalid damage type enum value in getWeaponType()."_s;
|
||||
return;
|
||||
}
|
||||
weapon.dualWield = weapon_prop->at<BoolProperty>("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222"_s)->value;
|
||||
auto& effect_colour_mode = weapon_prop->at<ByteProperty>("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014"_s)->enumValue;
|
||||
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;
|
||||
Utility::Warning{} << "Invalid effect colour mode in getWeaponType()."_s;
|
||||
return;
|
||||
}
|
||||
auto effect_colour = weapon_prop->at<ColourStructProperty>("ColorEfx_26_D921B62946C493E487455A831F4520AC"_s);
|
||||
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>("UnitData"_s);
|
||||
auto unit_data = _mass->at<GenericStructProperty>(MASS_UNIT_DATA);
|
||||
if(!unit_data) {
|
||||
_state = State::Invalid;
|
||||
_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) {
|
||||
_state = State::Invalid;
|
||||
_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;
|
||||
_lastError = "Weapon type array size mismatch."_s;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -229,19 +252,23 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi
|
|||
auto weapon_prop = prop->at<GenericStructProperty>(i);
|
||||
auto& weapon = weapon_array[i];
|
||||
|
||||
weapon_prop->at<StringProperty>("Name_13_7BF0D31F4E50C50C47231BB36A485D92"_s)->value = weapon.name;
|
||||
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>("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976"_s)->enumValue = strenum; break;
|
||||
#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:
|
||||
Utility::Warning{} << "Invalid weapon type enum value in writeWeaponType()."_s;
|
||||
_lastError = Utility::format("Invalid weapon type at index {}.", i);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto parts_prop = weapon_prop->at<ArrayProperty>("Element_6_8E4617CC4B2C1F1490435599784EC6E0"_s);
|
||||
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;
|
||||
_lastError = "Weapon parts array size mismatch."_s;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -249,40 +276,45 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi
|
|||
auto part_prop = parts_prop->at<GenericStructProperty>(j);
|
||||
auto& part = weapon.parts[j];
|
||||
|
||||
part_prop->at<IntProperty>("ID_2_A74D75434308158E5926178822DD28EE"_s)->value = part.id;
|
||||
part_prop->at<IntProperty>(MASS_WEAPON_PART_ID)->value = part.id;
|
||||
|
||||
auto part_styles = part_prop->at<ArrayProperty>("Styles_17_994C97C34A90667BE5B716BFD0B97588"_s);
|
||||
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>("Decals_13_8B81112B453D7230C0CDE982185E14F1"_s);
|
||||
auto part_decals = part_prop->at<ArrayProperty>(MASS_WEAPON_PART_DECALS);
|
||||
writeDecals(part.decals, part_decals);
|
||||
|
||||
auto part_accs = part_prop->at<ArrayProperty>("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"_s);
|
||||
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;
|
||||
_lastError = "Accessories array size mismatch."_s;
|
||||
return false;
|
||||
}
|
||||
|
||||
writeAccessories(part.accessories, part_accs);
|
||||
}
|
||||
|
||||
auto custom_styles = weapon_prop->at<ArrayProperty>("Styles_10_8C3C82444B986AD7A99595AD4985912D"_s);
|
||||
auto custom_styles = weapon_prop->at<ArrayProperty>(MASS_CUSTOM_WEAPON_STYLES);
|
||||
if(!custom_styles) {
|
||||
_state = State::Invalid;
|
||||
_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;
|
||||
_lastError = "Custom styles array size mismatch."_s;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -290,25 +322,29 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi
|
|||
writeCustomStyle(weapon.customStyles[j], j, custom_styles);
|
||||
}
|
||||
|
||||
weapon_prop->at<BoolProperty>("Attach_15_D00AABBD4AD6A04778D56D81E51927B3"_s)->value = weapon.attached;
|
||||
weapon_prop->at<BoolProperty>(MASS_WEAPON_ATTACH)->value = weapon.attached;
|
||||
switch(weapon.damageType) {
|
||||
#define c(enumerator, strenum) case DamageType::enumerator: weapon_prop->at<ByteProperty>("DamageType_18_E1FFA53540591A9087EC698117A65C83"_s)->enumValue = strenum; break;
|
||||
#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:
|
||||
Utility::Warning{} << "Unknown damage type enum value in writeWeaponType()."_s;
|
||||
_lastError = Utility::format("Invalid damage type at index {}.", i);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
weapon_prop->at<BoolProperty>("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222"_s)->value = weapon.dualWield;
|
||||
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>("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014"_s)->enumValue = enumstr; \
|
||||
weapon_prop->at<ByteProperty>(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue = enumstr; \
|
||||
break;
|
||||
#include "../Maps/EffectColourModes.hpp"
|
||||
#undef c
|
||||
default:
|
||||
Utility::Warning{} << "Unknown effect colour mode in writeWeaponType()."_s;
|
||||
_lastError = Utility::format("Invalid damage type at index {}.", i);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
auto effect_colour = weapon_prop->at<ColourStructProperty>("ColorEfx_26_D921B62946C493E487455A831F4520AC"_s);
|
||||
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();
|
||||
|
|
|
@ -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"
|
|
@ -19,16 +19,20 @@
|
|||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "MassManager.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
MassManager::MassManager(Containers::StringView save_path, Containers::StringView account, bool demo, Containers::StringView staging_dir):
|
||||
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}
|
||||
{
|
||||
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));
|
||||
mass_filename = Utility::Path::join(_saveDirectory,
|
||||
Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account));
|
||||
new(&_hangars[i]) Mass{mass_filename};
|
||||
}
|
||||
|
||||
|
@ -45,6 +49,8 @@ auto MassManager::hangar(Int hangar) -> Mass& {
|
|||
|
||||
void MassManager::refreshHangar(Int hangar) {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar index out of range.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -56,7 +62,8 @@ void MassManager::refreshHangar(Int hangar) {
|
|||
|
||||
auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bool {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar out of range in MassManager::importMass()"_s;
|
||||
_lastError = "Hangar index out of range.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -64,6 +71,7 @@ auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bo
|
|||
|
||||
if(it == _stagedMasses.end()) {
|
||||
_lastError = "Couldn't find "_s + staged_fn + " in the staged M.A.S.S.es."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -87,6 +95,7 @@ auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bo
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -95,12 +104,14 @@ auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bo
|
|||
|
||||
auto MassManager::exportMass(Int hangar) -> bool {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar out of range in MassManager::exportMass()"_s;
|
||||
_lastError = "Hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -110,6 +121,7 @@ auto MassManager::exportMass(Int hangar) -> bool {
|
|||
|
||||
if(!Utility::Path::copy(source, dest)) {
|
||||
_lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -118,12 +130,14 @@ auto MassManager::exportMass(Int hangar) -> bool {
|
|||
|
||||
auto MassManager::moveMass(Int source, Int destination) -> bool {
|
||||
if(source < 0 || source >= 32) {
|
||||
_lastError = "Source hangar out of range."_s;
|
||||
_lastError = "Source hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(destination < 0 || destination >= 32) {
|
||||
_lastError = "Destination hangar out of range."_s;
|
||||
_lastError = "Destination hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -153,12 +167,14 @@ auto MassManager::moveMass(Int source, Int destination) -> bool {
|
|||
|
||||
auto MassManager::deleteMass(Int hangar) -> bool {
|
||||
if(hangar < 0 || hangar >= 32) {
|
||||
_lastError = "Hangar out of range."_s;
|
||||
_lastError = "Hangar index out of range."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _hangars[hangar].filename()))) {
|
||||
_lastError = Utility::format("Deletion failed: {}", std::strerror(errno));
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -173,10 +189,11 @@ void MassManager::refreshStagedMasses() {
|
|||
_stagedMasses.clear();
|
||||
|
||||
using Utility::Path::ListFlag;
|
||||
auto file_list = Utility::Path::list(_stagingAreaDirectory, ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
|
||||
auto file_list = Utility::Path::list(_stagingAreaDirectory,
|
||||
ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
|
||||
|
||||
if(!file_list) {
|
||||
Utility::Error{} << _stagingAreaDirectory << "couldn't be opened";
|
||||
LOG_ERROR_FORMAT("{} couldn't be opened.", _stagingAreaDirectory);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -184,24 +201,24 @@ void MassManager::refreshStagedMasses() {
|
|||
return !file.hasSuffix(".sav"_s);
|
||||
});
|
||||
|
||||
auto list_view = file_list->except(file_list->end() - iter);
|
||||
auto list_view = file_list->exceptSuffix(file_list->end() - iter);
|
||||
|
||||
Utility::Debug{} << "Scanning for staged M.A.S.S.es...";
|
||||
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) {
|
||||
Utility::Debug{} << "Found staged M.A.S.S.:" << *name;
|
||||
LOG_INFO_FORMAT("Found staged M.A.S.S.: {}", *name);
|
||||
_stagedMasses[file] = *name;
|
||||
}
|
||||
else {
|
||||
Utility::Warning{} << "Skipped:" << file;
|
||||
LOG_WARNING_FORMAT("Skipped {}.", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MassManager::refreshStagedMass(Containers::StringView filename) {
|
||||
Utility::Debug{} << "Refreshing staged unit with filename" << 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);
|
||||
|
@ -223,11 +240,13 @@ void MassManager::refreshStagedMass(Containers::StringView filename) {
|
|||
auto MassManager::deleteStagedMass(Containers::StringView filename) -> bool {
|
||||
if(_stagedMasses.find(filename) == _stagedMasses.cend()) {
|
||||
_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::Path::remove(Utility::Path::join(_stagingAreaDirectory, filename))) {
|
||||
_lastError = filename + " couldn't be deleted: " + std::strerror(errno);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include <Corrade/Containers/Array.h>
|
||||
#include <Corrade/Containers/Pair.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
|
||||
#include "PropertyNames.h"
|
||||
#include "../Logger/Logger.h"
|
||||
#include "../UESaveFile/Types/ArrayProperty.h"
|
||||
#include "../UESaveFile/Types/ResourceItemValue.h"
|
||||
#include "../UESaveFile/Types/IntProperty.h"
|
||||
|
@ -35,8 +34,11 @@ using namespace Containers::Literals;
|
|||
Profile::Profile(Containers::StringView path):
|
||||
_profile(path)
|
||||
{
|
||||
LOG_INFO_FORMAT("Reading profile at {}.", path);
|
||||
|
||||
if(!_profile.valid()) {
|
||||
_lastError = _profile.lastError();
|
||||
_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,7 +51,7 @@ Profile::Profile(Containers::StringView path):
|
|||
_type = ProfileType::FullGame;
|
||||
}
|
||||
|
||||
auto account_prop = _profile.at<StringProperty>("Account"_s);
|
||||
auto account_prop = _profile.at<StringProperty>(PROFILE_ACCOUNT);
|
||||
if(!account_prop) {
|
||||
_lastError = "Couldn't find an account ID in "_s + _filename;
|
||||
_valid = false;
|
||||
|
@ -57,13 +59,6 @@ Profile::Profile(Containers::StringView path):
|
|||
}
|
||||
_account = account_prop->value;
|
||||
|
||||
if(_account.hasPrefix("PMCSlot"_s)) {
|
||||
_version = ProfileVersion::Normal;
|
||||
}
|
||||
else {
|
||||
_version = ProfileVersion::Legacy;
|
||||
}
|
||||
|
||||
refreshValues();
|
||||
}
|
||||
|
||||
|
@ -87,72 +82,77 @@ auto Profile::isDemo() const -> bool {
|
|||
return _type == ProfileType::Demo;
|
||||
}
|
||||
|
||||
auto Profile::version() const -> ProfileVersion {
|
||||
return _version;
|
||||
}
|
||||
|
||||
auto Profile::isLegacy() const -> bool {
|
||||
return _version == ProfileVersion::Legacy;
|
||||
}
|
||||
|
||||
auto Profile::account() const -> Containers::StringView {
|
||||
return _account;
|
||||
}
|
||||
|
||||
void Profile::refreshValues() {
|
||||
if(!_profile.reloadData()) {
|
||||
_lastError = _profile.lastError();
|
||||
LOG_ERROR(_profile.lastError());
|
||||
_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) {
|
||||
Utility::Error{} << _filename << "is not a valid profile save.";
|
||||
LOG_ERROR_FORMAT("{} is not a valid profile save.", _filename);
|
||||
_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto name_prop = _profile.at<StringProperty>("CompanyName"_s);
|
||||
LOG_INFO("Getting the company name.");
|
||||
auto name_prop = _profile.at<StringProperty>(PROFILE_NAME);
|
||||
if(!name_prop) {
|
||||
_lastError = "No company name in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_valid = false;
|
||||
return;
|
||||
}
|
||||
_name = name_prop->value;
|
||||
|
||||
auto prop = _profile.at<IntProperty>("ActiveFrameSlot"_s);
|
||||
LOG_INFO("Getting the active frame slot.");
|
||||
auto prop = _profile.at<IntProperty>(PROFILE_ACTIVE_FRAME_SLOT);
|
||||
_activeFrameSlot = prop ? prop->value : 0;
|
||||
|
||||
prop = _profile.at<IntProperty>("Credit"_s);
|
||||
LOG_INFO("Getting the credits.");
|
||||
prop = _profile.at<IntProperty>(PROFILE_CREDITS);
|
||||
_credits = prop ? prop->value : 0;
|
||||
|
||||
prop = _profile.at<IntProperty>("StoryProgress"_s);
|
||||
LOG_INFO("Getting the story progress.");
|
||||
prop = _profile.at<IntProperty>(PROFILE_STORY_PROGRESS);
|
||||
_storyProgress = prop ? prop->value : 0;
|
||||
|
||||
prop = _profile.at<IntProperty>("LastMissionID"_s);
|
||||
LOG_INFO("Getting the last mission ID.");
|
||||
prop = _profile.at<IntProperty>(PROFILE_LAST_MISSION_ID);
|
||||
_lastMissionId = prop ? prop->value : 0;
|
||||
|
||||
_verseSteel = getResource("ResourceMaterial"_s, VerseSteel);
|
||||
_undinium = getResource("ResourceMaterial"_s, Undinium);
|
||||
_necriumAlloy = getResource("ResourceMaterial"_s, NecriumAlloy);
|
||||
_lunarite = getResource("ResourceMaterial"_s, Lunarite);
|
||||
_asterite = getResource("ResourceMaterial"_s, Asterite);
|
||||
LOG_INFO("Getting the materials.");
|
||||
_verseSteel = getResource(PROFILE_MATERIAL, VerseSteel);
|
||||
_undinium = getResource(PROFILE_MATERIAL, Undinium);
|
||||
_necriumAlloy = getResource(PROFILE_MATERIAL, NecriumAlloy);
|
||||
_lunarite = getResource(PROFILE_MATERIAL, Lunarite);
|
||||
_asterite = getResource(PROFILE_MATERIAL, Asterite);
|
||||
_halliteFragma = getResource(PROFILE_MATERIAL, HalliteFragma);
|
||||
|
||||
_ednil = getResource("ResourceMaterial"_s, Ednil);
|
||||
_nuflalt = getResource("ResourceMaterial"_s, Nuflalt);
|
||||
_aurelene = getResource("ResourceMaterial"_s, Aurelene);
|
||||
_soldus = getResource("ResourceMaterial"_s, Soldus);
|
||||
_synthesisedN = getResource("ResourceMaterial"_s, SynthesisedN);
|
||||
_ednil = getResource(PROFILE_MATERIAL, Ednil);
|
||||
_nuflalt = getResource(PROFILE_MATERIAL, Nuflalt);
|
||||
_aurelene = getResource(PROFILE_MATERIAL, Aurelene);
|
||||
_soldus = getResource(PROFILE_MATERIAL, Soldus);
|
||||
_synthesisedN = getResource(PROFILE_MATERIAL, SynthesisedN);
|
||||
_nanoc = getResource(PROFILE_MATERIAL, Nanoc);
|
||||
|
||||
_alcarbonite = getResource("ResourceMaterial"_s, Alcarbonite);
|
||||
_keriphene = getResource("ResourceMaterial"_s, Keriphene);
|
||||
_nitinolCM = getResource("ResourceMaterial"_s, NitinolCM);
|
||||
_quarkium = getResource("ResourceMaterial"_s, Quarkium);
|
||||
_alterene = getResource("ResourceMaterial"_s, Alterene);
|
||||
_alcarbonite = getResource(PROFILE_MATERIAL, Alcarbonite);
|
||||
_keriphene = getResource(PROFILE_MATERIAL, Keriphene);
|
||||
_nitinolCM = getResource(PROFILE_MATERIAL, NitinolCM);
|
||||
_quarkium = getResource(PROFILE_MATERIAL, Quarkium);
|
||||
_alterene = getResource(PROFILE_MATERIAL, Alterene);
|
||||
_cosmium = getResource(PROFILE_MATERIAL, Cosmium);
|
||||
|
||||
_mixedComposition = getResource("ResourceQuarkData"_s, MixedComposition);
|
||||
_voidResidue = getResource("ResourceQuarkData"_s, VoidResidue);
|
||||
_muscularConstruction = getResource("ResourceQuarkData"_s, MuscularConstruction);
|
||||
_mineralExoskeletology = getResource("ResourceQuarkData"_s, MineralExoskeletology);
|
||||
_carbonisedSkin = getResource("ResourceQuarkData"_s, CarbonisedSkin);
|
||||
_mixedComposition = getResource(PROFILE_QUARK_DATA, MixedComposition);
|
||||
_voidResidue = getResource(PROFILE_QUARK_DATA, VoidResidue);
|
||||
_muscularConstruction = getResource(PROFILE_QUARK_DATA, MuscularConstruction);
|
||||
_mineralExoskeletology = getResource(PROFILE_QUARK_DATA, MineralExoskeletology);
|
||||
_carbonisedSkin = getResource(PROFILE_QUARK_DATA, CarbonisedSkin);
|
||||
_isolatedVoidParticle = getResource(PROFILE_QUARK_DATA, IsolatedVoidParticle);
|
||||
|
||||
_valid = true;
|
||||
}
|
||||
|
@ -162,9 +162,10 @@ auto Profile::companyName() const -> Containers::StringView {
|
|||
}
|
||||
|
||||
auto Profile::renameCompany(Containers::StringView new_name) -> bool {
|
||||
auto name_prop = _profile.at<StringProperty>("CompanyName"_s);
|
||||
auto name_prop = _profile.at<StringProperty>(PROFILE_NAME);
|
||||
if(!name_prop) {
|
||||
_lastError = "No company name in "_s + _filename;
|
||||
LOG_ERROR(_lastError);
|
||||
_valid = false;
|
||||
return false;
|
||||
}
|
||||
|
@ -188,11 +189,12 @@ auto Profile::credits() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setCredits(Int amount) -> bool {
|
||||
auto credits_prop = _profile.at<IntProperty>("Credit"_s);
|
||||
auto credits_prop = _profile.at<IntProperty>(PROFILE_CREDITS);
|
||||
|
||||
if(!credits_prop) {
|
||||
credits_prop = new IntProperty;
|
||||
credits_prop->name.emplace("Credit"_s);
|
||||
credits_prop->valueLength = sizeof(Int);
|
||||
_profile.appendProperty(IntProperty::ptr{credits_prop});
|
||||
}
|
||||
|
||||
|
@ -216,6 +218,7 @@ auto Profile::setStoryProgress(Int progress) -> bool {
|
|||
if(!story_progress_prop) {
|
||||
story_progress_prop = new IntProperty;
|
||||
story_progress_prop->name.emplace("StoryProgress"_s);
|
||||
story_progress_prop->valueLength = sizeof(Int);
|
||||
_profile.appendProperty(IntProperty::ptr{story_progress_prop});
|
||||
}
|
||||
|
||||
|
@ -238,7 +241,7 @@ auto Profile::verseSteel() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setVerseSteel(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, VerseSteel, amount);
|
||||
return setResource(PROFILE_MATERIAL, VerseSteel, amount);
|
||||
}
|
||||
|
||||
auto Profile::undinium() const -> Int {
|
||||
|
@ -246,7 +249,7 @@ auto Profile::undinium() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setUndinium(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Undinium, amount);
|
||||
return setResource(PROFILE_MATERIAL, Undinium, amount);
|
||||
}
|
||||
|
||||
auto Profile::necriumAlloy() const -> Int {
|
||||
|
@ -254,7 +257,7 @@ auto Profile::necriumAlloy() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setNecriumAlloy(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, NecriumAlloy, amount);
|
||||
return setResource(PROFILE_MATERIAL, NecriumAlloy, amount);
|
||||
}
|
||||
|
||||
auto Profile::lunarite() const -> Int {
|
||||
|
@ -262,7 +265,7 @@ auto Profile::lunarite() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setLunarite(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Lunarite, amount);
|
||||
return setResource(PROFILE_MATERIAL, Lunarite, amount);
|
||||
}
|
||||
|
||||
auto Profile::asterite() const -> Int {
|
||||
|
@ -270,7 +273,17 @@ auto Profile::asterite() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setAsterite(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Asterite, amount);
|
||||
return setResource(PROFILE_MATERIAL, Asterite, amount);
|
||||
}
|
||||
|
||||
Int
|
||||
Profile::halliteFragma() const {
|
||||
return _halliteFragma;
|
||||
}
|
||||
|
||||
bool
|
||||
Profile::setHalliteFragma(Int amount) {
|
||||
return setResource(PROFILE_MATERIAL, HalliteFragma, amount);
|
||||
}
|
||||
|
||||
auto Profile::ednil() const -> Int {
|
||||
|
@ -278,7 +291,7 @@ auto Profile::ednil() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setEdnil(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Ednil, amount);
|
||||
return setResource(PROFILE_MATERIAL, Ednil, amount);
|
||||
}
|
||||
|
||||
auto Profile::nuflalt() const -> Int {
|
||||
|
@ -286,7 +299,7 @@ auto Profile::nuflalt() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setNuflalt(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Nuflalt, amount);
|
||||
return setResource(PROFILE_MATERIAL, Nuflalt, amount);
|
||||
}
|
||||
|
||||
auto Profile::aurelene() const -> Int {
|
||||
|
@ -294,7 +307,7 @@ auto Profile::aurelene() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setAurelene(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Aurelene, amount);
|
||||
return setResource(PROFILE_MATERIAL, Aurelene, amount);
|
||||
}
|
||||
|
||||
auto Profile::soldus() const -> Int {
|
||||
|
@ -302,7 +315,7 @@ auto Profile::soldus() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setSoldus(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Soldus, amount);
|
||||
return setResource(PROFILE_MATERIAL, Soldus, amount);
|
||||
}
|
||||
|
||||
auto Profile::synthesisedN() const -> Int {
|
||||
|
@ -310,7 +323,17 @@ auto Profile::synthesisedN() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setSynthesisedN(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, SynthesisedN, amount);
|
||||
return setResource(PROFILE_MATERIAL, SynthesisedN, amount);
|
||||
}
|
||||
|
||||
Int
|
||||
Profile::nanoc() const {
|
||||
return _nanoc;
|
||||
}
|
||||
|
||||
bool
|
||||
Profile::setNanoc(Int amount) {
|
||||
return setResource(PROFILE_MATERIAL, Nanoc, amount);
|
||||
}
|
||||
|
||||
auto Profile::alcarbonite() const -> Int {
|
||||
|
@ -318,7 +341,7 @@ auto Profile::alcarbonite() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setAlcarbonite(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Alcarbonite, amount);
|
||||
return setResource(PROFILE_MATERIAL, Alcarbonite, amount);
|
||||
}
|
||||
|
||||
auto Profile::keriphene() const -> Int {
|
||||
|
@ -326,7 +349,7 @@ auto Profile::keriphene() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setKeriphene(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Keriphene, amount);
|
||||
return setResource(PROFILE_MATERIAL, Keriphene, amount);
|
||||
}
|
||||
|
||||
auto Profile::nitinolCM() const -> Int {
|
||||
|
@ -334,7 +357,7 @@ auto Profile::nitinolCM() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setNitinolCM(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, NitinolCM, amount);
|
||||
return setResource(PROFILE_MATERIAL, NitinolCM, amount);
|
||||
}
|
||||
|
||||
auto Profile::quarkium() const -> Int {
|
||||
|
@ -342,7 +365,7 @@ auto Profile::quarkium() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setQuarkium(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Quarkium, amount);
|
||||
return setResource(PROFILE_MATERIAL, Quarkium, amount);
|
||||
}
|
||||
|
||||
auto Profile::alterene() const -> Int {
|
||||
|
@ -350,7 +373,17 @@ auto Profile::alterene() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setAlterene(Int amount) -> bool {
|
||||
return setResource("ResourceMaterial"_s, Alterene, amount);
|
||||
return setResource(PROFILE_MATERIAL, Alterene, amount);
|
||||
}
|
||||
|
||||
Int
|
||||
Profile::cosmium() const {
|
||||
return _cosmium;
|
||||
}
|
||||
|
||||
bool
|
||||
Profile::setCosmium(Int amount) {
|
||||
return setResource(PROFILE_MATERIAL, Cosmium, amount);
|
||||
}
|
||||
|
||||
auto Profile::mixedComposition() const -> Int {
|
||||
|
@ -358,7 +391,7 @@ auto Profile::mixedComposition() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setMixedComposition(Int amount) -> bool {
|
||||
return setResource("ResourceQuarkData"_s, MixedComposition, amount);
|
||||
return setResource(PROFILE_QUARK_DATA, MixedComposition, amount);
|
||||
}
|
||||
|
||||
auto Profile::voidResidue() const -> Int {
|
||||
|
@ -366,7 +399,7 @@ auto Profile::voidResidue() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setVoidResidue(Int amount) -> bool {
|
||||
return setResource("ResourceQuarkData"_s, VoidResidue, amount);
|
||||
return setResource(PROFILE_QUARK_DATA, VoidResidue, amount);
|
||||
}
|
||||
|
||||
auto Profile::muscularConstruction() const -> Int {
|
||||
|
@ -374,7 +407,7 @@ auto Profile::muscularConstruction() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setMuscularConstruction(Int amount) -> bool {
|
||||
return setResource("ResourceQuarkData"_s, MuscularConstruction, amount);
|
||||
return setResource(PROFILE_QUARK_DATA, MuscularConstruction, amount);
|
||||
}
|
||||
|
||||
auto Profile::mineralExoskeletology() const -> Int {
|
||||
|
@ -382,7 +415,7 @@ auto Profile::mineralExoskeletology() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setMineralExoskeletology(Int amount) -> bool {
|
||||
return setResource("ResourceQuarkData"_s, MineralExoskeletology, amount);
|
||||
return setResource(PROFILE_QUARK_DATA, MineralExoskeletology, amount);
|
||||
}
|
||||
|
||||
auto Profile::carbonisedSkin() const -> Int {
|
||||
|
@ -390,7 +423,17 @@ auto Profile::carbonisedSkin() const -> Int {
|
|||
}
|
||||
|
||||
auto Profile::setCarbonisedSkin(Int amount) -> bool {
|
||||
return setResource("ResourceQuarkData"_s, CarbonisedSkin, amount);
|
||||
return setResource(PROFILE_QUARK_DATA, CarbonisedSkin, amount);
|
||||
}
|
||||
|
||||
Int
|
||||
Profile::isolatedVoidParticle() const {
|
||||
return _isolatedVoidParticle;
|
||||
}
|
||||
|
||||
bool
|
||||
Profile::setIsolatedVoidParticle(Int amount) {
|
||||
return setResource(PROFILE_QUARK_DATA, IsolatedVoidParticle, amount);
|
||||
}
|
||||
|
||||
auto Profile::getResource(Containers::StringView container, MaterialID id) -> Int {
|
||||
|
@ -413,9 +456,10 @@ auto Profile::setResource(Containers::StringView container, MaterialID id, Int a
|
|||
auto mats_prop = _profile.at<ArrayProperty>(container);
|
||||
|
||||
if(!mats_prop) {
|
||||
_lastError = "Couldn't find "_s + container + " in "_s + _filename;
|
||||
_valid = false;
|
||||
return false;
|
||||
mats_prop = new ArrayProperty;
|
||||
mats_prop->name.emplace(container);
|
||||
mats_prop->itemType = "StructProperty";
|
||||
_profile.appendProperty(ArrayProperty::ptr{mats_prop});
|
||||
}
|
||||
|
||||
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
|
||||
|
@ -428,6 +472,9 @@ auto Profile::setResource(Containers::StringView container, MaterialID id, Int a
|
|||
ResourceItemValue* res_prop;
|
||||
if(it == mats_prop->items.end()) {
|
||||
res_prop = new ResourceItemValue;
|
||||
if(mats_prop->items.isEmpty()) {
|
||||
res_prop->name.emplace(container);
|
||||
}
|
||||
res_prop->id = id;
|
||||
ResourceItemValue::ptr prop{res_prop};
|
||||
arrayAppend(mats_prop->items, std::move(prop));
|
||||
|
|
|
@ -33,11 +33,6 @@ enum class ProfileType : UnsignedByte {
|
|||
FullGame
|
||||
};
|
||||
|
||||
enum class ProfileVersion : UnsignedByte {
|
||||
Legacy, // pre-0.8
|
||||
Normal // 0.8 and later
|
||||
};
|
||||
|
||||
class Profile {
|
||||
public:
|
||||
explicit Profile(Containers::StringView path);
|
||||
|
@ -51,9 +46,6 @@ class Profile {
|
|||
auto type() const -> ProfileType;
|
||||
auto isDemo() const -> bool;
|
||||
|
||||
auto version() const -> ProfileVersion;
|
||||
auto isLegacy() const -> bool;
|
||||
|
||||
auto account() const -> Containers::StringView;
|
||||
|
||||
void refreshValues();
|
||||
|
@ -86,6 +78,9 @@ class Profile {
|
|||
auto asterite() const -> Int;
|
||||
auto setAsterite(Int amount) -> bool;
|
||||
|
||||
Int halliteFragma() const;
|
||||
bool setHalliteFragma(Int amount);
|
||||
|
||||
auto ednil() const -> Int;
|
||||
auto setEdnil(Int amount) -> bool;
|
||||
|
||||
|
@ -101,6 +96,9 @@ class Profile {
|
|||
auto synthesisedN() const -> Int;
|
||||
auto setSynthesisedN(Int amount) -> bool;
|
||||
|
||||
Int nanoc() const;
|
||||
bool setNanoc(Int amount);
|
||||
|
||||
auto alcarbonite() const -> Int;
|
||||
auto setAlcarbonite(Int amount) -> bool;
|
||||
|
||||
|
@ -116,6 +114,9 @@ class Profile {
|
|||
auto alterene() const -> Int;
|
||||
auto setAlterene(Int amount) -> bool;
|
||||
|
||||
Int cosmium() const;
|
||||
bool setCosmium(Int amount);
|
||||
|
||||
auto mixedComposition() const -> Int;
|
||||
auto setMixedComposition(Int amount) -> bool;
|
||||
|
||||
|
@ -131,6 +132,9 @@ class Profile {
|
|||
auto carbonisedSkin() const -> Int;
|
||||
auto setCarbonisedSkin(Int amount) -> bool;
|
||||
|
||||
Int isolatedVoidParticle() const;
|
||||
bool setIsolatedVoidParticle(Int amount);
|
||||
|
||||
private:
|
||||
auto getResource(Containers::StringView container, MaterialID id) -> Int;
|
||||
auto setResource(Containers::StringView container, MaterialID id, Int amount) -> bool;
|
||||
|
@ -138,7 +142,6 @@ class Profile {
|
|||
Containers::String _filename;
|
||||
|
||||
ProfileType _type;
|
||||
ProfileVersion _version;
|
||||
|
||||
UESaveFile _profile;
|
||||
|
||||
|
@ -148,29 +151,33 @@ class Profile {
|
|||
Int _storyProgress = 0;
|
||||
Int _lastMissionId = 0;
|
||||
|
||||
Int _verseSteel = 0;
|
||||
Int _undinium = 0;
|
||||
Int _necriumAlloy = 0;
|
||||
Int _lunarite = 0;
|
||||
Int _asterite = 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;
|
||||
|
||||
|
|
|
@ -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"
|
|
@ -21,27 +21,31 @@
|
|||
using namespace Magnum;
|
||||
|
||||
enum MaterialID : Int {
|
||||
VerseSteel = 0xC3500,
|
||||
Undinium = 0xC3501,
|
||||
NecriumAlloy = 0xC3502,
|
||||
Lunarite = 0xC3503,
|
||||
Asterite = 0xC3504,
|
||||
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
|
||||
CarbonisedSkin = 0xDBBA4,
|
||||
IsolatedVoidParticle = 0xDBBA5,
|
||||
};
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
|
||||
#include <Corrade/Containers/ScopeGuard.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
|
@ -29,6 +27,8 @@
|
|||
|
||||
#include <zip.h>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "ProfileManager.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
@ -53,6 +53,8 @@ auto ProfileManager::profiles() -> Containers::ArrayView<Profile> {
|
|||
}
|
||||
|
||||
auto ProfileManager::refreshProfiles() -> bool {
|
||||
LOG_INFO("Refreshing profiles.");
|
||||
|
||||
_profiles = Containers::Array<Profile>{};
|
||||
|
||||
using Utility::Path::ListFlag;
|
||||
|
@ -61,31 +63,30 @@ auto ProfileManager::refreshProfiles() -> bool {
|
|||
|
||||
if(!files) {
|
||||
_lastError = _saveDirectory + " can't be opened.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto predicate = [](Containers::StringView file)->bool{
|
||||
std::regex legacy_regex("(Demo)?Profile[0-9]{17}\\.sav", std::regex::nosubs);
|
||||
std::regex new_regex("(Demo)?ProfilePMCSlot[0-9]{3}\\.sav", std::regex::nosubs);
|
||||
std::cmatch m;
|
||||
return !std::regex_match(file.data(), m, legacy_regex) && !std::regex_match(file.data(), m, new_regex);
|
||||
return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav"));
|
||||
};
|
||||
|
||||
auto files_view = files->except(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||
|
||||
for(const auto& file : files_view) {
|
||||
Profile profile{Utility::Path::join(_saveDirectory, file)};
|
||||
|
||||
if(!profile.valid()) {
|
||||
Utility::Warning{} << "Profile"_s << file << "is invalid:"_s << profile.lastError();
|
||||
LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
arrayAppend(_profiles, std::move(profile));
|
||||
}
|
||||
|
||||
if(_profiles.empty()) {
|
||||
if(_profiles.isEmpty()) {
|
||||
_lastError = "No valid profiles were found."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -101,6 +102,7 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo
|
|||
_lastError = Utility::format("Couldn't delete {} (filename: {}).",
|
||||
_profiles[index].companyName(),
|
||||
_profiles[index].filename());
|
||||
LOG_ERROR(_lastError);
|
||||
refreshProfiles();
|
||||
return false;
|
||||
}
|
||||
|
@ -129,7 +131,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
|
|||
std::tm* time = std::localtime(×tamp);
|
||||
auto& profile = _profiles[index];
|
||||
|
||||
auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup",
|
||||
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);
|
||||
|
@ -140,26 +142,28 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
|
|||
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::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, 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;
|
||||
}
|
||||
|
||||
auto comment = Utility::format("{}|{}{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
|
||||
auto comment = Utility::format("{}|{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
|
||||
profile.companyName(),
|
||||
profile.isDemo() ? "demo"_s : "full"_s,
|
||||
profile.isLegacy() ? ""_s : "_new"_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.data(), comment.size());
|
||||
|
@ -189,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;
|
||||
}
|
||||
|
||||
|
@ -210,14 +215,15 @@ void ProfileManager::refreshBackups() {
|
|||
|
||||
if(!files) {
|
||||
_lastError = _backupsDirectory + " can't be opened.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
auto predicate = [](Containers::StringView file)->bool{
|
||||
return !file.hasSuffix(".mbprofbackup"_s);
|
||||
return !(file.hasSuffix(".mbprofbackup"_s) || file.hasSuffix(".backup.mbst"));
|
||||
};
|
||||
|
||||
auto files_view = files->except(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
|
||||
|
||||
int error_code = 0;
|
||||
zip_t* zip = nullptr;
|
||||
|
@ -252,21 +258,11 @@ void ProfileManager::refreshBackups() {
|
|||
|
||||
backup.company = info[0];
|
||||
|
||||
if(info[1] == "full") {
|
||||
if(info[1].hasPrefix("full")) {
|
||||
backup.type = ProfileType::FullGame;
|
||||
backup.version = ProfileVersion::Legacy;
|
||||
}
|
||||
else if(info[1] == "demo") {
|
||||
else if(info[1].hasPrefix("demo")) {
|
||||
backup.type = ProfileType::Demo;
|
||||
backup.version = ProfileVersion::Legacy;
|
||||
}
|
||||
else if(info[1] == "full_new") {
|
||||
backup.type = ProfileType::FullGame;
|
||||
backup.version = ProfileVersion::Normal;
|
||||
}
|
||||
else if(info[1] == "demo_new") {
|
||||
backup.type = ProfileType::Demo;
|
||||
backup.version = ProfileVersion::Normal;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
|
@ -297,6 +293,7 @@ void ProfileManager::refreshBackups() {
|
|||
auto ProfileManager::deleteBackup(std::size_t index) -> bool {
|
||||
if(!Utility::Path::remove(Utility::Path::join(_backupsDirectory, _backups[index].filename))) {
|
||||
_lastError = "Couldn't delete " + _backups[index].filename;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -323,6 +320,7 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
|
|||
zip_error_t error;
|
||||
zip_error_init_with_code(&error, error_code);
|
||||
_lastError = zip_error_strerror(&error);
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -332,6 +330,7 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
|
|||
FILE* out = std::fopen(Utility::Path::join(_saveDirectory, file).data(), "wb");
|
||||
if(out == nullptr) {
|
||||
_lastError = Utility::format(error_format.data(), file, std::strerror(errno));
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -340,6 +339,7 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
|
|||
zip_file_t* zf = zip_fopen(zip, file.data(), ZIP_FL_ENC_GUESS);
|
||||
if(zf == nullptr) {
|
||||
_lastError = Utility::format(error_format.data(), file, zip_strerror(zip));
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -351,12 +351,14 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
|
|||
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::format(error_format.data(), file, "not enough bytes written.");
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(bytes_read == -1) {
|
||||
_lastError = Utility::format(error_format.data(), file, "couldn't read bytes from archive.");
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ struct Backup {
|
|||
Containers::String filename;
|
||||
Containers::String company;
|
||||
ProfileType type;
|
||||
ProfileVersion version;
|
||||
struct {
|
||||
int year;
|
||||
int month;
|
||||
|
|
|
@ -27,12 +27,15 @@
|
|||
#include <Magnum/ImGuiIntegration/Integration.h>
|
||||
#include <Magnum/ImGuiIntegration/Context.hpp>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
|
@ -47,10 +50,11 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
Configuration{}.setTitle("M.A.S.S. Builder Save Tool " SAVETOOL_VERSION " (\"" SAVETOOL_CODENAME "\")")
|
||||
.setSize({960, 720})}
|
||||
{
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
tweak.enable(""_s, "../../"_s);
|
||||
#endif
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
tweak.enable("", "../../");
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
@ -60,25 +64,20 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add,
|
||||
GL::Renderer::BlendEquation::Add);
|
||||
|
||||
Utility::Debug{} << "Renderer initialisation successful.";
|
||||
|
||||
Utility::Debug{} << "===Configuring SDL2===";
|
||||
|
||||
{
|
||||
Utility::Debug d{};
|
||||
d << "Enabling clickthrough...";
|
||||
|
||||
if(SDL_VERSION_ATLEAST(2, 0, 5)) {
|
||||
if(SDL_SetHintWithPriority(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1", SDL_HINT_OVERRIDE) == SDL_TRUE) {
|
||||
d << "success!"_s;
|
||||
} else {
|
||||
d << "error: hint couldn't be set."_s;
|
||||
}
|
||||
} else {
|
||||
d << "error: SDL2 is too old (version < 2.0.5)."_s;
|
||||
}
|
||||
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());
|
||||
|
@ -89,26 +88,27 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
_updateEventId = _initEventId + 1;
|
||||
_fileEventId = _initEventId + 2;
|
||||
|
||||
LOG_INFO("Initialising the timer subsystem.");
|
||||
if(SDL_InitSubSystem(SDL_INIT_TIMER) != 0) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", SDL_GetError(), window());
|
||||
LOG_ERROR(SDL_GetError());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
SDL_GetError(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
Utility::Debug{} << "SDL2 configuration successful.";
|
||||
|
||||
Utility::Debug{} << "===Initialising the Save Tool===";
|
||||
|
||||
initialiseGui();
|
||||
|
||||
if(!initialiseToolDirectories()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.data(), window());
|
||||
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());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
_lastError.data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
return interval;
|
||||
}, this);
|
||||
if(_gameCheckTimerId == 0) {
|
||||
LOG_ERROR(SDL_GetError());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
return;
|
||||
|
@ -127,19 +128,7 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
|
||||
initialiseConfiguration();
|
||||
|
||||
switch(_framelimit) {
|
||||
case Framelimit::Vsync:
|
||||
setSwapInterval(1);
|
||||
break;
|
||||
case Framelimit::HalfVsync:
|
||||
setSwapInterval(2);
|
||||
break;
|
||||
case Framelimit::FpsCap:
|
||||
setSwapInterval(0);
|
||||
setMinimalLoopPeriod(1000/_fpsCap);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_INFO("Initialising update checker.");
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
if(_checkUpdatesOnStartup) {
|
||||
_queue.addToast(Toast::Type::Default, "Checking for updates..."_s);
|
||||
|
@ -152,64 +141,61 @@ SaveTool::SaveTool(const Arguments& arguments):
|
|||
GL::DebugOutput::setEnabled(GL::DebugOutput::Source::Api, GL::DebugOutput::Type::Other, {131185}, false);
|
||||
}
|
||||
|
||||
Utility::Debug{} << "Initialisation successful.";
|
||||
Utility::Debug{} << "===Running main loop===";
|
||||
|
||||
if(_skipDisclaimer) {
|
||||
_uiState = UiState::Initialising;
|
||||
_initThread = std::thread{[this]{ initialiseManager(); }};
|
||||
}
|
||||
|
||||
_timeline.start();
|
||||
}
|
||||
|
||||
SaveTool::~SaveTool() {
|
||||
Utility::Debug{} << "===Perfoming cleanup===";
|
||||
LOG_INFO("Cleaning up.");
|
||||
|
||||
Utility::Debug{} << "Shutting libcurl down...";
|
||||
LOG_INFO("Shutting libcurl down.");
|
||||
curl_global_cleanup();
|
||||
|
||||
SDL_RemoveTimer(_gameCheckTimerId);
|
||||
|
||||
Utility::Debug{} << "Saving the configuration...";
|
||||
LOG_INFO("Saving the configuration.");
|
||||
|
||||
_conf.setValue("cheat_mode"_s, _cheatMode);
|
||||
_conf.setValue("unsafe_mode"_s, _unsafeMode);
|
||||
_conf.setValue("advanced_mode"_s, _advancedMode);
|
||||
_conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup);
|
||||
_conf.setValue("skip_disclaimer"_s, _skipDisclaimer);
|
||||
|
||||
switch(_framelimit) {
|
||||
case Framelimit::Vsync:
|
||||
_conf.setValue("frame_limit"_s, "vsync"_s);
|
||||
break;
|
||||
case Framelimit::HalfVsync:
|
||||
_conf.setValue("frame_limit"_s, "half_vsync"_s);
|
||||
break;
|
||||
case Framelimit::FpsCap:
|
||||
_conf.setValue<UnsignedInt>("frame_limit"_s, _fpsCap);
|
||||
break;
|
||||
}
|
||||
_conf.setValue("swap_interval"_s, _swapInterval);
|
||||
_conf.setValue("fps_cap"_s, _fpsCap);
|
||||
|
||||
_conf.save();
|
||||
|
||||
Utility::Debug{} << "Exiting...";
|
||||
LOG_INFO("Exiting.");
|
||||
}
|
||||
|
||||
void SaveTool::drawEvent() {
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
tweak.update();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
|
||||
|
||||
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) {
|
||||
|
@ -297,7 +283,7 @@ void SaveTool::drawGui() {
|
|||
drawAbout();
|
||||
}
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
if(_demoWindow) {
|
||||
ImGui::ShowDemoWindow(&_demoWindow);
|
||||
}
|
||||
|
@ -309,7 +295,7 @@ void SaveTool::drawGui() {
|
|||
if(_metricsWindow) {
|
||||
ImGui::ShowMetricsWindow(&_metricsWindow);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
_queue.draw(windowSize());
|
||||
}
|
||||
|
@ -328,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();
|
||||
|
|
|
@ -23,11 +23,15 @@
|
|||
#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 <SDL.h>
|
||||
#include <SDL_timer.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
@ -39,8 +43,6 @@
|
|||
#include "../ToastQueue/ToastQueue.h"
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#include <Corrade/Utility/Tweakable.h>
|
||||
|
||||
#define tw CORRADE_TWEAKABLE
|
||||
#endif
|
||||
|
||||
|
@ -166,23 +168,16 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
template<typename Functor, typename... Args>
|
||||
auto drawUnsafeWidget(Functor func, Args... args) -> bool {
|
||||
GameState game_state = _gameState; // Copying the value to reduce the risk of a data race.
|
||||
if(!_unsafeMode && game_state != GameState::NotRunning) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(game_state != GameState::NotRunning);
|
||||
bool result = func(std::forward<Args>(args)...);
|
||||
|
||||
if(!_unsafeMode && game_state != GameState::NotRunning) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
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...
|
||||
|
||||
template<typename... Args>
|
||||
void drawUnsafeText(Containers::StringView text, Args... args) { // Alternative to the above, for ImGui::Text*() variants.
|
||||
if(!_unsafeMode && _gameState != GameState::NotRunning) {
|
||||
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 {
|
||||
|
@ -217,11 +212,11 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
} _uiState{UiState::Disclaimer};
|
||||
|
||||
bool _aboutPopup{false};
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
bool _demoWindow{false};
|
||||
bool _styleEditor{false};
|
||||
bool _metricsWindow{false};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ToastQueue _queue;
|
||||
|
||||
|
@ -267,16 +262,11 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
};
|
||||
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
|
||||
|
||||
enum class Framelimit: UnsignedByte {
|
||||
Vsync,
|
||||
HalfVsync,
|
||||
FpsCap
|
||||
} _framelimit{Framelimit::Vsync};
|
||||
UnsignedInt _fpsCap{60};
|
||||
int _swapInterval = 1;
|
||||
float _fpsCap = 60.0f;
|
||||
|
||||
bool _skipDisclaimer{false};
|
||||
bool _checkUpdatesOnStartup{true};
|
||||
bool _unsafeMode{false};
|
||||
|
||||
bool _updateAvailable{false};
|
||||
Containers::String _latestVersion;
|
||||
|
@ -301,4 +291,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
|
|||
bool _eLaunchersDirty{false};
|
||||
|
||||
bool _cheatMode{false};
|
||||
bool _advancedMode{false};
|
||||
|
||||
Timeline _timeline;
|
||||
};
|
||||
|
|
|
@ -14,9 +14,13 @@
|
|||
// 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>
|
||||
|
||||
|
@ -44,7 +48,7 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
|||
return;
|
||||
} // TODO: actually do something when config files will finally be handled
|
||||
|
||||
if(!Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
|
||||
if(!Utility::String::endsWith(filename, Utility::format("Profile{}.sav", _currentProfile->account()).data())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,7 +61,8 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
|||
}
|
||||
|
||||
void SaveTool::fileUpdateEvent(SDL_Event& event) {
|
||||
Containers::String filename{static_cast<char*>(event.user.data1), std::strlen(static_cast<char*>(event.user.data1)), nullptr};
|
||||
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);
|
||||
|
@ -96,7 +101,11 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) {
|
|||
if(is_current_profile) {
|
||||
_currentProfile = nullptr;
|
||||
_uiState = UiState::ProfileManager;
|
||||
_profileManager->refreshProfiles();
|
||||
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))) {
|
||||
|
|
|
@ -19,10 +19,14 @@
|
|||
#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"
|
||||
|
||||
|
@ -35,8 +39,8 @@ void SaveTool::initEvent(SDL_Event& event) {
|
|||
ImGui::CloseCurrentPopup();
|
||||
break;
|
||||
case ProfileManagerFailure:
|
||||
Utility::Error{} << "Error initialising ProfileManager:" << _profileManager->lastError();
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().data(), window());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error ",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
default:
|
||||
|
@ -45,7 +49,7 @@ void SaveTool::initEvent(SDL_Event& event) {
|
|||
}
|
||||
|
||||
void SaveTool::initialiseConfiguration() {
|
||||
Utility::Debug{} << "Reading configuration file...";
|
||||
LOG_INFO("Reading configuration file.");
|
||||
|
||||
if(_conf.hasValue("cheat_mode"_s)) {
|
||||
_cheatMode = _conf.value<bool>("cheat_mode"_s);
|
||||
|
@ -54,11 +58,11 @@ void SaveTool::initialiseConfiguration() {
|
|||
_conf.setValue("cheat_mode"_s, _cheatMode);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("unsafe_mode"_s)) {
|
||||
_unsafeMode = _conf.value<bool>("unsafe_mode"_s);
|
||||
if(_conf.hasValue("advanced_mode"_s)) {
|
||||
_advancedMode = _conf.value<bool>("advanced_mode"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("unsafe_mode"_s, _unsafeMode);
|
||||
_conf.setValue("advanced_mode"_s, _advancedMode);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("startup_update_check"_s)) {
|
||||
|
@ -75,38 +79,51 @@ void SaveTool::initialiseConfiguration() {
|
|||
_conf.setValue("skip_disclaimer"_s, _skipDisclaimer);
|
||||
}
|
||||
|
||||
if(_conf.hasValue("frame_limit"_s)) {
|
||||
std::string frame_limit = _conf.value("frame_limit"_s);
|
||||
if(frame_limit == "vsync"_s) {
|
||||
_framelimit = Framelimit::Vsync;
|
||||
}
|
||||
else if(frame_limit == "half_vsync"_s) {
|
||||
_framelimit = Framelimit::HalfVsync;
|
||||
}
|
||||
else {
|
||||
_framelimit = Framelimit::FpsCap;
|
||||
_fpsCap = std::stoul(frame_limit);
|
||||
}
|
||||
if(_conf.hasValue("swap_interval"_s)) {
|
||||
_swapInterval = _conf.value<int>("swap_interval"_s);
|
||||
}
|
||||
else {
|
||||
_conf.setValue("frame_limit"_s, "vsync"_s);
|
||||
_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() {
|
||||
Utility::Debug{} << "Initialising ImGui...";
|
||||
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()), reg_font.size(), 20.0f, &font_config);
|
||||
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 };
|
||||
|
@ -116,11 +133,13 @@ void SaveTool::initialiseGui() {
|
|||
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);
|
||||
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()), brand_font.size(), 16.0f, &icon_config, brand_range);
|
||||
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;
|
||||
|
@ -128,7 +147,8 @@ void SaveTool::initialiseGui() {
|
|||
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);
|
||||
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());
|
||||
|
||||
|
@ -142,6 +162,8 @@ void SaveTool::initialiseGui() {
|
|||
}
|
||||
|
||||
void SaveTool::initialiseManager() {
|
||||
LOG_INFO("Initialising the profile manager.");
|
||||
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = _initEventId;
|
||||
|
@ -158,7 +180,7 @@ void SaveTool::initialiseManager() {
|
|||
}
|
||||
|
||||
auto SaveTool::initialiseToolDirectories() -> bool {
|
||||
Utility::Debug{} << "Initialising Save Tool directories...";
|
||||
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");
|
||||
|
@ -168,17 +190,17 @@ auto SaveTool::initialiseToolDirectories() -> bool {
|
|||
//_stylesDir = Utility::Directory::join(_armouryDir, "styles");
|
||||
|
||||
if(!Utility::Path::exists(_backupsDir)) {
|
||||
Utility::Debug{} << "Backups directory not found, creating...";
|
||||
LOG_WARNING("Backups directory not found, creating...");
|
||||
if(!Utility::Path::make(_backupsDir)) {
|
||||
Utility::Error{} << (_lastError = "Couldn't create the backups directory.");
|
||||
LOG_ERROR(_lastError = "Couldn't create the backups directory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Utility::Path::exists(_stagingDir)) {
|
||||
Utility::Debug{} << "Staging directory not found, creating...";
|
||||
LOG_WARNING("Staging directory not found, creating...");
|
||||
if(!Utility::Path::make(_stagingDir)) {
|
||||
Utility::Error{} << (_lastError = "Couldn't create the backups directory.");
|
||||
LOG_ERROR(_lastError = "Couldn't create the staging directory.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -219,20 +241,21 @@ auto SaveTool::initialiseToolDirectories() -> bool {
|
|||
}
|
||||
|
||||
auto SaveTool::findGameDataDirectory() -> bool {
|
||||
Utility::Debug{} << "Searching for the game's save directory...";
|
||||
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)
|
||||
{
|
||||
Utility::Error{} << (_lastError = "SHGetKnownFolderPath() failed in SaveTool::findGameDataDirectory()"_s);
|
||||
_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)) {
|
||||
Utility::Error{} << (_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s);
|
||||
LOG_ERROR(_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -244,10 +267,12 @@ auto SaveTool::findGameDataDirectory() -> bool {
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -14,20 +14,19 @@
|
|||
// 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/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
#include <Corrade/Utility/String.h>
|
||||
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "../Maps/LastMissionId.h"
|
||||
#include "../Maps/StoryProgress.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawManager() {
|
||||
ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y},
|
||||
|
@ -52,7 +51,7 @@ void SaveTool::drawManager() {
|
|||
}
|
||||
|
||||
if(ImGui::BeginChild("##ProfileInfo",
|
||||
{ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f},
|
||||
{ImGui::GetContentRegionAvail().x * 0.60f, 0.0f},
|
||||
true, ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
if(ImGui::BeginMenuBar()) {
|
||||
|
@ -146,8 +145,8 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView<char> name_view) -> bool {
|
|||
callback, nullptr);
|
||||
ImGui::SameLine();
|
||||
GameState game_state = _gameState;
|
||||
if((!_unsafeMode && game_state != GameState::NotRunning) ||
|
||||
!(len >= 6 && len <= 32) ||
|
||||
if(game_state != GameState::NotRunning ||
|
||||
!(len >= 6 && len <= 32) ||
|
||||
!(name_view[0] != ' ' && name_view[len - 1] != ' '))
|
||||
{
|
||||
ImGui::BeginDisabled();
|
||||
|
@ -158,8 +157,8 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView<char> name_view) -> bool {
|
|||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if((!_unsafeMode && game_state != GameState::NotRunning) ||
|
||||
!(len >= 6 && len <= 32) ||
|
||||
if(game_state != GameState::NotRunning ||
|
||||
!(len >= 6 && len <= 32) ||
|
||||
!(name_view[0] != ' ' && name_view[len - 1] != ' '))
|
||||
{
|
||||
ImGui::EndDisabled();
|
||||
|
@ -204,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});
|
||||
|
@ -249,7 +248,7 @@ void SaveTool::drawGeneralInfo() {
|
|||
}
|
||||
drawTooltip("Story progress directly affects unlocked levels.");
|
||||
if(ImGui::BeginPopup("StoryProgressMenu")) {
|
||||
if(!_unsafeMode && _gameState != GameState::NotRunning) {
|
||||
if(_gameState != GameState::NotRunning) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
for(const auto& sp : story_progress) {
|
||||
|
@ -310,7 +309,9 @@ void SaveTool::drawResearchInventory() {
|
|||
drawMaterialRow("Asterite", 5,
|
||||
[this]{ return _currentProfile->asterite(); },
|
||||
[this](Int amount){ return _currentProfile->setAsterite(amount); });
|
||||
drawUnavailableMaterialRow("Hallite fragma", 6);
|
||||
drawMaterialRow("Hallite fragma", 6,
|
||||
[this]{ return _currentProfile->halliteFragma(); },
|
||||
[this](Int amount){ return _currentProfile->setHalliteFragma(amount); });
|
||||
drawUnavailableMaterialRow("Unnoctinium", 7);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
@ -332,7 +333,9 @@ void SaveTool::drawResearchInventory() {
|
|||
drawMaterialRow("Synthesized N", 5,
|
||||
[this]{ return _currentProfile->synthesisedN(); },
|
||||
[this](Int amount){ return _currentProfile->setSynthesisedN(amount); });
|
||||
drawUnavailableMaterialRow("Nanoc", 6);
|
||||
drawMaterialRow("Nanoc", 6,
|
||||
[this]{ return _currentProfile->nanoc(); },
|
||||
[this](Int amount){ return _currentProfile->setNanoc(amount); });
|
||||
drawUnavailableMaterialRow("Abyssillite", 7);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
@ -354,7 +357,9 @@ void SaveTool::drawResearchInventory() {
|
|||
drawMaterialRow("Alterene", 5,
|
||||
[this]{ return _currentProfile->alterene(); },
|
||||
[this](Int amount){ return _currentProfile->setAlterene(amount); });
|
||||
drawUnavailableMaterialRow("Cosmium", 6);
|
||||
drawMaterialRow("Cosmium", 6,
|
||||
[this]{ return _currentProfile->cosmium(); },
|
||||
[this](Int amount){ return _currentProfile->setCosmium(amount); });
|
||||
drawUnavailableMaterialRow("Purified quarkium", 7);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
@ -376,7 +381,9 @@ void SaveTool::drawResearchInventory() {
|
|||
drawMaterialRow("Carbonized skin", 5,
|
||||
[this]{ return _currentProfile->carbonisedSkin(); },
|
||||
[this](Int amount){ return _currentProfile->setCarbonisedSkin(amount); });
|
||||
drawUnavailableMaterialRow("Isolated void particle", 6);
|
||||
drawMaterialRow("Isolated void particle", 6,
|
||||
[this]{ return _currentProfile->isolatedVoidParticle(); },
|
||||
[this](Int amount){ return _currentProfile->setIsolatedVoidParticle(amount); });
|
||||
drawUnavailableMaterialRow("Weaponised physiology", 7);
|
||||
|
||||
ImGui::EndTable();
|
||||
|
@ -473,7 +480,7 @@ void SaveTool::drawMassManager() {
|
|||
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
if((_unsafeMode || _gameState == GameState::NotRunning) && ImGui::BeginDragDropTarget()) {
|
||||
if(_gameState == GameState::NotRunning && ImGui::BeginDragDropTarget()) {
|
||||
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) {
|
||||
if(payload->DataSize != sizeof(Containers::String)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
|
||||
|
@ -580,7 +587,7 @@ void SaveTool::drawMassManager() {
|
|||
ImGui::TableSetColumnIndex(0);
|
||||
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::GetContentRegionAvailWidth()) {
|
||||
if((ImGui::CalcTextSize(staged_formatted.data()).x + ImGui::GetStyle().FramePadding.x) > ImGui::GetContentRegionAvail().x) {
|
||||
drawTooltip(staged_formatted.data());
|
||||
}
|
||||
if(ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) {
|
||||
|
@ -644,7 +651,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
|
|||
return 0;
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
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);
|
||||
|
@ -688,7 +695,7 @@ auto SaveTool::drawDeleteStagedMassPopup(Containers::StringView filename) -> ImG
|
|||
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).data());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
|
|
@ -15,15 +15,15 @@
|
|||
// 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 "../FontAwesome/IconsFontAwesome5.h"
|
||||
|
||||
#include "SaveTool.h"
|
||||
|
||||
void SaveTool::drawMassViewer() {
|
||||
|
@ -90,7 +90,7 @@ void SaveTool::drawMassViewer() {
|
|||
_selectedWeaponPart = 0;
|
||||
_selectedWeaponDecal = 0;
|
||||
_selectedWeaponAccessory = 0;
|
||||
};
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ void SaveTool::drawGlobalStyles() {
|
|||
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(i);
|
||||
ImGui::PushID(int(i));
|
||||
DCSResult result;
|
||||
result = drawCustomStyle(_currentMass->globalStyles()[i]);
|
||||
switch(result) {
|
||||
|
@ -393,9 +393,12 @@ auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult {
|
|||
void SaveTool::drawDecalEditor(Decal& decal) {
|
||||
ImGui::Text("ID: %i", decal.id);
|
||||
|
||||
if(ImGui::BeginTable("##DecalTable", 2, ImGuiTableFlags_BordersInnerV)) {
|
||||
if(ImGui::BeginTable("##DecalTable", _advancedMode ? 2 : 1, ImGuiTableFlags_BordersInnerV)) {
|
||||
ImGui::TableSetupColumn("##Normal", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
if(_advancedMode) {
|
||||
ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
|
@ -439,52 +442,53 @@ void SaveTool::drawDecalEditor(Decal& decal) {
|
|||
ImGui::Checkbox("##Wrap", &decal.wrap);
|
||||
ImGui::EndGroup();
|
||||
|
||||
if(_advancedMode) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
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::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::BeginGroup();
|
||||
drawAlignedText("Position:");
|
||||
drawAlignedText("U axis:");
|
||||
drawAlignedText("V axis:");
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
|
||||
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::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("##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::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();
|
||||
}
|
||||
|
@ -495,7 +499,7 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
ImGui::TextUnformatted("Accessory: <none>");
|
||||
}
|
||||
else if(accessories.find(accessory.id) != accessories.cend()) {
|
||||
ImGui::Text("Accessory #%i - %s", accessory.id, accessories.at(accessory.id).data());
|
||||
ImGui::Text("Accessory #%.4i - %s", accessory.id, accessories.at(accessory.id).name.data());
|
||||
}
|
||||
else {
|
||||
ImGui::Text("Accessory #%i", accessory.id);
|
||||
|
@ -505,12 +509,10 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
ImGui::SameLine();
|
||||
|
||||
static Int tab = 0;
|
||||
static Containers::Optional<AccessorySize> size = Containers::NullOpt;
|
||||
if(ImGui::SmallButton("Change")) {
|
||||
ImGui::OpenPopup("##AccessoryPopup");
|
||||
if(accessory.id < 1000) {
|
||||
tab = 0;
|
||||
}
|
||||
else if(accessory.id >= 3000) {
|
||||
if(accessory.id >= 3000) {
|
||||
tab = 3;
|
||||
}
|
||||
else if(accessory.id >= 2000) {
|
||||
|
@ -519,9 +521,18 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
else if(accessory.id >= 1000) {
|
||||
tab = 1;
|
||||
}
|
||||
else {
|
||||
tab = 0;
|
||||
}
|
||||
}
|
||||
if(ImGui::BeginPopup("##AccessoryPopup")) {
|
||||
Float selectable_width = 90.0f;
|
||||
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})) {
|
||||
|
@ -549,10 +560,56 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
|
||||
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)) {
|
||||
if(ImGui::Selectable(acc.second.data(), acc.first == accessory.id)) {
|
||||
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;
|
||||
}
|
||||
|
@ -578,9 +635,13 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Styles:");
|
||||
drawAlignedText("Base position:");
|
||||
if(_advancedMode) {
|
||||
drawAlignedText("Base position:");
|
||||
}
|
||||
drawAlignedText("Position offset:");
|
||||
drawAlignedText("Base rotation:");
|
||||
if(_advancedMode) {
|
||||
drawAlignedText("Base rotation:");
|
||||
}
|
||||
drawAlignedText("Rotation offset:");
|
||||
drawAlignedText("Scale:");
|
||||
ImGui::EndGroup();
|
||||
|
@ -611,15 +672,17 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
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();
|
||||
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");
|
||||
|
@ -633,15 +696,17 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
|||
ImGui::SameLine();
|
||||
drawHelpMarker("+/-500.0 = +/-250 in-game");
|
||||
|
||||
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();
|
||||
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");
|
||||
|
|
|
@ -43,7 +43,7 @@ void SaveTool::drawArmour() {
|
|||
};
|
||||
|
||||
for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) {
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushID(int(i));
|
||||
|
||||
auto& part = _currentMass->armourParts()[i];
|
||||
|
||||
|
@ -61,7 +61,7 @@ void SaveTool::drawArmour() {
|
|||
if(ImGui::CollapsingHeader(header)) {
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() * 0.491f);
|
||||
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) {
|
||||
|
@ -103,7 +103,7 @@ void SaveTool::drawArmour() {
|
|||
|
||||
ImGui::PushID(j);
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() - 2.0f);
|
||||
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)) {
|
||||
|
@ -126,7 +126,7 @@ void SaveTool::drawArmour() {
|
|||
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], j);
|
||||
ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], int(j));
|
||||
}
|
||||
|
||||
drawDecalEditor(part.decals[_selectedArmourDecals[i]]);
|
||||
|
@ -141,7 +141,7 @@ void SaveTool::drawArmour() {
|
|||
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], j);
|
||||
ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], int(j));
|
||||
}
|
||||
|
||||
drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles());
|
||||
|
@ -301,7 +301,7 @@ void SaveTool::drawCustomArmourStyles() {
|
|||
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(i);
|
||||
ImGui::PushID(int(i));
|
||||
DCSResult result;
|
||||
result = drawCustomStyle(_currentMass->armourCustomStyles()[i]);
|
||||
switch(result) {
|
||||
|
@ -310,7 +310,7 @@ void SaveTool::drawCustomArmourStyles() {
|
|||
break;
|
||||
case DCS_Save:
|
||||
_modifiedBySaveTool = true;
|
||||
if(_currentMass->writeArmourCustomStyle(i)) {
|
||||
if(!_currentMass->writeArmourCustomStyle(i)) {
|
||||
_modifiedBySaveTool = false;
|
||||
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ void SaveTool::drawFrameInfo() {
|
|||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if(ImGui::BeginChild("##JointSliders", {(ImGui::GetContentRegionAvailWidth() / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 300.0f}, true, ImGuiWindowFlags_MenuBar)) {
|
||||
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");
|
||||
|
||||
|
@ -43,7 +43,7 @@ void SaveTool::drawFrameInfo() {
|
|||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if(ImGui::BeginChild("##FrameStyles", {(ImGui::GetContentRegionAvailWidth() / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 0.0f}, true, ImGuiWindowFlags_MenuBar)) {
|
||||
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");
|
||||
|
||||
|
@ -291,7 +291,7 @@ void SaveTool::drawCustomFrameStyles() {
|
|||
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(i);
|
||||
ImGui::PushID(int(i));
|
||||
DCSResult result;
|
||||
result = drawCustomStyle(_currentMass->frameCustomStyles()[i]);
|
||||
switch(result) {
|
||||
|
|
|
@ -33,7 +33,7 @@ void SaveTool::drawWeapons() {
|
|||
|
||||
if(!ImGui::BeginTable("##WeaponsList", 1,
|
||||
ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH,
|
||||
{ImGui::GetContentRegionAvailWidth() * 0.2f, -footer_height_to_reserve}))
|
||||
{ImGui::GetContentRegionAvail().x * 0.2f, -footer_height_to_reserve}))
|
||||
{
|
||||
ImGui::EndGroup();
|
||||
return;
|
||||
|
@ -269,7 +269,7 @@ void SaveTool::drawWeaponCategory(Containers::StringView name, Containers::Array
|
|||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushID(int(i));
|
||||
|
||||
if(ImGui::Selectable(weapon.name.data(), _currentWeapon == &weapon)) {
|
||||
_currentWeapon = &weapon;
|
||||
|
@ -309,7 +309,7 @@ void SaveTool::drawWeaponCategory(Containers::StringView name, Containers::Array
|
|||
|
||||
ImGui::PopID();
|
||||
|
||||
if(weapon.attached == true) {
|
||||
if(weapon.attached) {
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu);
|
||||
}
|
||||
}
|
||||
|
@ -454,7 +454,9 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) {
|
|||
return nullptr;
|
||||
}();
|
||||
|
||||
CORRADE_INTERNAL_ASSERT(map);
|
||||
if(!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(map->find(part.id) != map->cend()) {
|
||||
ImGui::TextUnformatted(map->at(part.id).data());
|
||||
|
@ -477,6 +479,9 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) {
|
|||
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();
|
||||
|
@ -531,7 +536,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) {
|
|||
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, i);
|
||||
ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, int(i));
|
||||
}
|
||||
|
||||
drawDecalEditor(part.decals[_selectedWeaponDecal]);
|
||||
|
@ -546,7 +551,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) {
|
|||
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, i);
|
||||
ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, int(i));
|
||||
}
|
||||
|
||||
drawAccessoryEditor(part.accessories[_selectedWeaponAccessory], weapon.customStyles);
|
||||
|
|
|
@ -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() {
|
||||
|
@ -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().data(), window());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +85,7 @@ void SaveTool::drawProfileManager() {
|
|||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushID(int(i));
|
||||
if(ImGui::Selectable(profile.companyName().data(), false,
|
||||
ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap))
|
||||
{
|
||||
|
@ -93,9 +96,7 @@ void SaveTool::drawProfileManager() {
|
|||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("%s%s",
|
||||
profile.isDemo() ? "Demo" : "Full",
|
||||
profile.isLegacy() ? " (legacy)" : "");
|
||||
ImGui::TextUnformatted(profile.isDemo() ? "Demo" : "Full");
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
|
||||
|
@ -137,7 +138,7 @@ 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()[backup_index].company.data(),
|
||||
_profileManager->backups()[backup_index].timestamp.year,
|
||||
|
@ -159,7 +160,11 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
if(!_profileManager->restoreBackup(backup_index)) {
|
||||
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
|
||||
}
|
||||
_profileManager->refreshProfiles();
|
||||
if(!_profileManager->refreshProfiles()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
_profileManager->lastError().data(), window());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
@ -176,7 +181,7 @@ 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()[backup_index].company.data(),
|
||||
_profileManager->backups()[backup_index].timestamp.year,
|
||||
|
@ -230,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,
|
||||
|
@ -276,12 +281,10 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
|
|||
backup.timestamp.second);
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("%s%s",
|
||||
backup.type == ProfileType::Demo ? "Demo" : "Full",
|
||||
backup.version == ProfileVersion::Legacy ? " (legacy)" : "");
|
||||
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);
|
||||
|
@ -338,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();
|
||||
|
@ -371,7 +380,7 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
|
|||
delete_builds = false;
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
|
||||
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());
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
|
||||
#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) {
|
||||
|
@ -25,20 +29,26 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
|
|||
|
||||
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});
|
||||
_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));
|
||||
_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;
|
||||
}
|
||||
|
||||
|
@ -69,11 +79,11 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
|
|||
bool prerelease = false;
|
||||
|
||||
bool operator==(const Version& other) const {
|
||||
return fullVersion == other.fullVersion;
|
||||
return fullVersion == other.fullVersion && prerelease == other.prerelease;
|
||||
}
|
||||
bool operator>(const Version& other) const {
|
||||
if((fullVersion > other.fullVersion) ||
|
||||
(fullVersion == other.fullVersion && prerelease == false && other.prerelease == true))
|
||||
(fullVersion == other.fullVersion && !prerelease && other.prerelease))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -81,31 +91,35 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
operator Containers::String() const {
|
||||
explicit operator Containers::String() const {
|
||||
return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : "");
|
||||
}
|
||||
};
|
||||
|
||||
static const Version current_ver{SAVETOOL_VERSION};
|
||||
|
||||
Containers::String response{static_cast<char*>(event.user.data1), strlen(static_cast<char*>(event.user.data1)), nullptr};
|
||||
auto components = response.split('\n');
|
||||
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,
|
||||
_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 = latest_ver;
|
||||
_releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}", components.front());
|
||||
_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 == true)) {
|
||||
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 == false) {
|
||||
_queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +149,7 @@ void SaveTool::checkForUpdates() {
|
|||
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, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L);
|
||||
|
||||
auto code = curl_easy_perform(curl);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "SaveTool.h"
|
||||
|
||||
#include <Corrade/version.h>
|
||||
#include <Corrade/Containers/StringView.h>
|
||||
|
||||
#include <Magnum/version.h>
|
||||
#include <Magnum/versionIntegration.h>
|
||||
|
@ -25,6 +24,8 @@
|
|||
|
||||
#include <zipconf.h>
|
||||
|
||||
#include <curl/curlver.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||
|
||||
|
@ -32,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,
|
||||
|
@ -84,7 +85,7 @@ 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)) {
|
||||
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::TextEx(licence.data(), licence.data() + licence.size(), ImGuiTextFlags_None);
|
||||
|
@ -114,7 +115,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static auto corrade_licence = _rs.getRaw("COPYING.Corrade");
|
||||
if(ImGui::BeginChild("##CorradeLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
if(ImGui::BeginChild("##CorradeLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(corrade_licence.data(), corrade_licence.data() + corrade_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
|
@ -142,7 +143,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static auto magnum_licence = _rs.getRaw("COPYING.Magnum");
|
||||
if(ImGui::BeginChild("##MagnumLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
if(ImGui::BeginChild("##MagnumLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(magnum_licence.data(), magnum_licence.data() + magnum_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
|
@ -168,7 +169,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static auto imgui_licence = _rs.getRaw("LICENSE.ImGui");
|
||||
if(ImGui::BeginChild("##ImGuiLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
if(ImGui::BeginChild("##ImGuiLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(imgui_licence.data(), imgui_licence.data() + imgui_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
|
@ -194,7 +195,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: zlib");
|
||||
|
||||
static auto sdl_licence = _rs.getRaw("LICENSE.SDL");
|
||||
if(ImGui::BeginChild("##SDLLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
if(ImGui::BeginChild("##SDLLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(sdl_licence.data(), sdl_licence.data() + sdl_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
|
@ -220,7 +221,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: 3-clause BSD");
|
||||
|
||||
static auto libzip_licence = _rs.getRaw("LICENSE.libzip");
|
||||
if(ImGui::BeginChild("##libzipLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
if(ImGui::BeginChild("##libzipLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(libzip_licence.data(), libzip_licence.data() + libzip_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
|
@ -245,7 +246,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: MIT");
|
||||
|
||||
static auto efsw_licence = _rs.getRaw("LICENSE.efsw");
|
||||
if(ImGui::BeginChild("##efswLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
if(ImGui::BeginChild("##efswLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
ImGui::TextEx(efsw_licence.data(), efsw_licence.data() + efsw_licence.size(), ImGuiTextFlags_None);
|
||||
ImGui::PopFont();
|
||||
|
@ -256,6 +257,7 @@ void SaveTool::drawAbout() {
|
|||
}
|
||||
|
||||
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();
|
||||
|
@ -270,7 +272,7 @@ void SaveTool::drawAbout() {
|
|||
ImGui::TextUnformatted("Licence: MIT/X derivative");
|
||||
|
||||
static auto curl_licence = _rs.getRaw("LICENSE.curl");
|
||||
if(ImGui::BeginChild("##libcurlLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
|
||||
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();
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
// 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/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")) {
|
||||
|
@ -55,58 +55,59 @@ void SaveTool::drawMainMenu() {
|
|||
ImGui::Separator();
|
||||
|
||||
if(ImGui::BeginMenu(ICON_FA_COG " Settings")) {
|
||||
drawAlignedText("Frame limiter:");
|
||||
ImGui::BeginGroup();
|
||||
drawAlignedText("Vertical sync:");
|
||||
if(_swapInterval == 0) {
|
||||
drawAlignedText("FPS cap:");
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
static UnsignedByte selection = static_cast<UnsignedByte>(_framelimit);
|
||||
static const char* framelimit_labels[3] = {
|
||||
"V-sync",
|
||||
"Half V-sync",
|
||||
"FPS cap, no V-sync"
|
||||
ImGui::BeginGroup();
|
||||
|
||||
static const char* framelimit_labels[] = {
|
||||
"Off",
|
||||
"Every VBLANK",
|
||||
"Every second VBLANK",
|
||||
"Every third VBLANK",
|
||||
};
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
|
||||
if(ImGui::BeginCombo("##FrameLimit", framelimit_labels[selection])) {
|
||||
if(ImGui::Selectable(framelimit_labels[0], _framelimit == Framelimit::Vsync)) {
|
||||
selection = 0;
|
||||
_framelimit = Framelimit::Vsync;
|
||||
setSwapInterval(1);
|
||||
}
|
||||
if(ImGui::Selectable(framelimit_labels[1], _framelimit == Framelimit::HalfVsync)) {
|
||||
selection = 1;
|
||||
_framelimit = Framelimit::HalfVsync;
|
||||
setSwapInterval(2);
|
||||
}
|
||||
if(ImGui::Selectable(framelimit_labels[2], _framelimit == Framelimit::FpsCap)) {
|
||||
selection = 2;
|
||||
_framelimit = Framelimit::FpsCap;
|
||||
setSwapInterval(0);
|
||||
setMinimalLoopPeriod(1000 / _fpsCap);
|
||||
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(_framelimit == Framelimit::FpsCap) {
|
||||
static constexpr UnsignedInt min_fps = 15;
|
||||
static constexpr UnsignedInt max_fps = 150;
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
|
||||
if(ImGui::SliderScalar("##FpsSlider", ImGuiDataType_U32, &_fpsCap, &min_fps, &max_fps, "%u FPS", ImGuiSliderFlags_AlwaysClamp)) {
|
||||
setMinimalLoopPeriod(1000 / _fpsCap);
|
||||
}
|
||||
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("Unsafe mode", &_unsafeMode);
|
||||
ImGui::Checkbox("Advanced mode", &_advancedMode);
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
drawHelpMarker("This allows changing the state of save files in the game's save folder even when the game is running.",
|
||||
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);
|
||||
|
@ -155,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();
|
||||
|
@ -168,7 +169,7 @@ void SaveTool::drawMainMenu() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
if(ImGui::BeginMenu("Debug tools")) {
|
||||
ImGui::MenuItem("ImGui demo window", nullptr, &_demoWindow);
|
||||
ImGui::MenuItem("ImGui style editor", nullptr, &_styleEditor);
|
||||
|
@ -176,7 +177,7 @@ void SaveTool::drawMainMenu() {
|
|||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(ImGui::BeginMenu("Help")) {
|
||||
if(ImGui::BeginMenu(ICON_FA_KEYBOARD " Keyboard shortcuts")) {
|
||||
|
|
|
@ -62,7 +62,7 @@ class Toast {
|
|||
|
||||
private:
|
||||
Type _type{Type::Default};
|
||||
Containers::StringView _message;
|
||||
Containers::String _message;
|
||||
std::chrono::milliseconds _timeout;
|
||||
std::chrono::steady_clock::time_point _creationTime;
|
||||
Animation::Track<UnsignedInt, Phase> _phaseTrack;
|
||||
|
|
|
@ -19,14 +19,15 @@
|
|||
#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) {
|
||||
Utility::Error{} << "Couldn't open" << filename << "for reading:\n"
|
||||
<< std::strerror(errno);
|
||||
LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "BinaryWriter.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
@ -23,8 +25,7 @@ using namespace Containers::Literals;
|
|||
BinaryWriter::BinaryWriter(Containers::StringView filename) {
|
||||
_file = std::fopen(filename.data(), "wb");
|
||||
if(!_file) {
|
||||
Utility::Error{} << "Couldn't open"_s << filename << "for reading:\n"_s
|
||||
<< std::strerror(errno);
|
||||
LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +117,7 @@ auto BinaryWriter::writeArray(Containers::ArrayView<const char> array) -> bool {
|
|||
|
||||
auto BinaryWriter::writeUEString(Containers::StringView str) -> bool {
|
||||
if(str.size() > UINT32_MAX) {
|
||||
Utility::Error{} << "BinaryWriter::writeUEString(): string is too big."_s;
|
||||
LOG_ERROR_FORMAT("String is too big. Expected size() < UINT32_MAX, got {} instead.", str.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "BinaryReader.h"
|
||||
#include "BinaryWriter.h"
|
||||
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "PropertySerialiser.h"
|
||||
|
||||
PropertySerialiser::PropertySerialiser() {
|
||||
|
@ -164,7 +166,7 @@ auto PropertySerialiser::deserialise(Containers::String name, Containers::String
|
|||
prop = serialiser->deserialise(name, type, value_length, reader, *this);
|
||||
|
||||
if(!prop) {
|
||||
!Utility::Error{} << "No prop in" << __func__;
|
||||
LOG_ERROR("No property.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "ArrayPropertySerialiser.h"
|
||||
|
||||
|
@ -28,16 +29,19 @@ auto ArrayPropertySerialiser::deserialiseProperty(Containers::StringView name, C
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -53,6 +57,7 @@ auto ArrayPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, U
|
|||
{
|
||||
auto array_prop = dynamic_cast<ArrayProperty*>(prop.get());
|
||||
if(!array_prop) {
|
||||
LOG_ERROR("The property is not a valid array property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "BoolPropertySerialiser.h"
|
||||
|
||||
|
@ -30,15 +31,18 @@ auto BoolPropertySerialiser::deserialise(Containers::StringView name, Containers
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -52,8 +56,8 @@ auto BoolPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLo
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "BytePropertySerialiser.h"
|
||||
|
||||
|
@ -33,16 +34,19 @@ auto BytePropertySerialiser::deserialise(Containers::StringView name, Containers
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -64,8 +68,8 @@ auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLo
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "ColourPropertySerialiser.h"
|
||||
|
||||
|
@ -28,6 +29,7 @@ auto ColourPropertySerialiser::deserialiseProperty(Containers::StringView name,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -38,8 +40,8 @@ auto ColourPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "DateTimePropertySerialiser.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ auto DateTimePropertySerialiser::deserialiseProperty(Containers::StringView name
|
|||
auto prop = Containers::pointer<DateTimeStructProperty>();
|
||||
|
||||
if(!reader.readUnsignedLong(prop->timestamp)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read date/time property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,8 +38,8 @@ auto DateTimePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "EnumPropertySerialiser.h"
|
||||
|
||||
|
@ -32,15 +33,18 @@ auto EnumPropertySerialiser::deserialise(Containers::StringView name, Containers
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -51,8 +55,8 @@ auto EnumPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLo
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "FloatPropertySerialiser.h"
|
||||
|
||||
|
@ -33,10 +34,12 @@ auto FloatPropertySerialiser::deserialise(Containers::StringView name, Container
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -47,8 +50,8 @@ auto FloatPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedL
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "GuidPropertySerialiser.h"
|
||||
|
||||
|
@ -28,7 +29,7 @@ auto GuidPropertySerialiser::deserialiseProperty(Containers::StringView name, Co
|
|||
auto prop = Containers::pointer<GuidStructProperty>();
|
||||
|
||||
if(!reader.readStaticArray(prop->guid)) {
|
||||
Utility::Error{} << "Couldn't read GUID in"_s << __func__;
|
||||
LOG_ERROR_FORMAT("Couldn't read GUID property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -39,8 +40,8 @@ auto GuidPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Un
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "IntPropertySerialiser.h"
|
||||
|
||||
|
@ -27,6 +28,7 @@ auto IntPropertySerialiser::deserialiseProperty(Containers::StringView name, Con
|
|||
|
||||
if(value_length == UnsignedLong(-1)) {
|
||||
if(!reader.readInt(prop->value)) {
|
||||
LOG_ERROR("Couldn't read int property's value.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,10 +38,12 @@ auto IntPropertySerialiser::deserialiseProperty(Containers::StringView name, Con
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -52,8 +56,8 @@ auto IntPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
|
||||
#include "../Types/NoneProperty.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "MapPropertySerialiser.h"
|
||||
|
||||
|
@ -31,25 +31,30 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -64,10 +69,12 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -105,6 +112,7 @@ auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns
|
|||
{
|
||||
auto map_prop = dynamic_cast<MapProperty*>(prop.get());
|
||||
if(!map_prop) {
|
||||
LOG_ERROR("The property is not a valid map property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -120,17 +128,20 @@ auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
|
||||
#include "../Types/IntProperty.h"
|
||||
#include "../Types/NoneProperty.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "ResourcePropertySerialiser.h"
|
||||
|
||||
|
@ -31,49 +31,46 @@ auto ResourcePropertySerialiser::deserialiseProperty(Containers::StringView name
|
|||
{
|
||||
auto prop = Containers::pointer<ResourceItemValue>();
|
||||
|
||||
Containers::String str;
|
||||
if(!reader.readUEString(str) || str != "ID_4_AAE08F17428E229EC7A2209F51081A21"_s) {
|
||||
auto id_prop = serialiser.read(reader);
|
||||
if(!id_prop) {
|
||||
LOG_ERROR("Couldn't read the ID property."_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readUEString(str) || str != "IntProperty"_s) {
|
||||
if((*id_prop->name) != "ID_4_AAE08F17428E229EC7A2209F51081A21"_s ||
|
||||
id_prop->propertyType != "IntProperty"_s ||
|
||||
dynamic_cast<IntProperty*>(id_prop.get()) == nullptr)
|
||||
{
|
||||
LOG_ERROR("The ID property is invalid."_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readUnsignedLong(value_length) || value_length != 4ull) {
|
||||
prop->id = dynamic_cast<IntProperty*>(id_prop.get())->value;
|
||||
|
||||
auto value_prop = serialiser.read(reader);
|
||||
if(!value_prop) {
|
||||
LOG_ERROR("Couldn't read the value property."_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
if((*value_prop->name) != "Quantity_3_560F09B5485C365D3041888910019CE3"_s ||
|
||||
value_prop->propertyType != "IntProperty"_s ||
|
||||
dynamic_cast<IntProperty*>(value_prop.get()) == nullptr)
|
||||
{
|
||||
LOG_ERROR("The value property is invalid."_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readInt(prop->id)) {
|
||||
return nullptr;
|
||||
}
|
||||
prop->quantity = dynamic_cast<IntProperty*>(value_prop.get())->value;
|
||||
|
||||
if(!reader.readUEString(str) || str != "Quantity_3_560F09B5485C365D3041888910019CE3"_s) {
|
||||
return nullptr;
|
||||
}
|
||||
auto none_prop = serialiser.read(reader);
|
||||
|
||||
if(!reader.readUEString(str) || str != "IntProperty"_s) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readUnsignedLong(value_length) || value_length != 4ull) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readInt(prop->quantity)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!reader.readUEString(str) || str != "None"_s) {
|
||||
if(!none_prop ||
|
||||
(*none_prop->name) != "None"_s ||
|
||||
none_prop->propertyType != "NoneProperty"_s ||
|
||||
!dynamic_cast<NoneProperty*>(none_prop.get()))
|
||||
{
|
||||
LOG_ERROR("Couldn't find a terminating NoneProperty."_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -85,6 +82,7 @@ auto ResourcePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop
|
|||
{
|
||||
auto res_prop = dynamic_cast<ResourceItemValue*>(prop.get());
|
||||
if(!res_prop) {
|
||||
LOG_ERROR("The property is not a valid ResourceItemValue property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "RotatorPropertySerialiser.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ auto RotatorPropertySerialiser::deserialiseProperty(Containers::StringView name,
|
|||
auto prop = Containers::pointer<RotatorStructProperty>();
|
||||
|
||||
if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y) || !reader.readFloat(prop->z)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read rotator property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,8 +38,8 @@ auto RotatorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop,
|
|||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto rotator = dynamic_cast<RotatorStructProperty*>(prop.get());
|
||||
|
||||
if(!rotator) {
|
||||
LOG_ERROR("The property is not a valid rotator property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "SetPropertySerialiser.h"
|
||||
|
||||
|
@ -26,21 +27,25 @@ auto SetPropertySerialiser::deserialiseProperty(Containers::StringView name, Con
|
|||
{
|
||||
Containers::String item_type;
|
||||
if(!reader.readUEString(item_type)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read set property {}'s item type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in set property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedInt four_bytes;
|
||||
if(!reader.readUnsignedInt(four_bytes) || four_bytes != 0u) {
|
||||
LOG_ERROR_FORMAT("Couldn't read four null bytes in set property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UnsignedInt item_count;
|
||||
if(!reader.readUnsignedInt(item_count)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read set property {}'s item count.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -56,6 +61,7 @@ auto SetPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns
|
|||
{
|
||||
auto set_prop = dynamic_cast<SetProperty*>(prop.get());
|
||||
if(!set_prop) {
|
||||
LOG_ERROR("The property is not a valid set property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "StringPropertySerialiser.h"
|
||||
|
||||
|
@ -36,11 +37,13 @@ auto StringPropertySerialiser::deserialise(Containers::StringView name, Containe
|
|||
if(value_length != UnsignedLong(-1)) {
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in string property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!reader.readUEString(prop->value)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read string property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -53,8 +56,8 @@ auto StringPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, Unsigned
|
|||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto str_prop = dynamic_cast<StringProperty*>(prop.get());
|
||||
|
||||
if(!str_prop) {
|
||||
LOG_ERROR("The property is not a valid string property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../PropertySerialiser.h"
|
||||
|
||||
#include "../Types/GenericStructProperty.h"
|
||||
#include "../Types/NoneProperty.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "StructSerialiser.h"
|
||||
|
||||
|
@ -36,16 +36,19 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri
|
|||
{
|
||||
Containers::String item_type;
|
||||
if(!reader.readUEString(item_type)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read struct property {}'s item type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Containers::StaticArray<16, char> guid{ValueInit};
|
||||
if(!reader.readStaticArray(guid)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read struct property {}'s GUID.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char terminator;
|
||||
if(!reader.readChar(terminator) || terminator != '\0') {
|
||||
LOG_ERROR_FORMAT("Couldn't read a null byte in struct property {}.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -67,10 +70,11 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri
|
|||
}
|
||||
|
||||
if(!prop) {
|
||||
LOG_ERROR("Invalid property");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static_cast<StructProperty*>(prop.get())->structGuid = guid;
|
||||
dynamic_cast<StructProperty*>(prop.get())->structGuid = guid;
|
||||
|
||||
arrayAppend(array, std::move(prop));
|
||||
}
|
||||
|
@ -84,20 +88,23 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri
|
|||
{
|
||||
Containers::String item_type;
|
||||
if(!reader.readUEString(item_type)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read struct property {}'s item type.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(item_type == "None") {
|
||||
return Containers::pointer<NoneProperty>();
|
||||
return NoneProperty::ptr{};
|
||||
}
|
||||
|
||||
Containers::StaticArray<16, char> guid{ValueInit};
|
||||
if(!reader.readStaticArray(guid)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read struct property {}'s GUID.", 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;
|
||||
}
|
||||
|
||||
|
@ -123,6 +130,7 @@ auto StructSerialiser::serialise(Containers::ArrayView<UnrealPropertyBase::ptr>
|
|||
|
||||
auto struct_prop = dynamic_cast<StructProperty*>(props.front().get());
|
||||
if(!struct_prop) {
|
||||
LOG_ERROR("The property is not a valid struct property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -135,13 +143,14 @@ auto StructSerialiser::serialise(Containers::ArrayView<UnrealPropertyBase::ptr>
|
|||
UnsignedLong bytes_written_here = 0;
|
||||
for(auto& prop : props) {
|
||||
struct_prop = dynamic_cast<StructProperty*>(prop.get());
|
||||
|
||||
if(!struct_prop) {
|
||||
LOG_ERROR("The property is not a valid struct property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written_here, writer)) {
|
||||
if(!writeStructValue(struct_prop, bytes_written_here, writer, serialiser)) {
|
||||
LOG_ERROR("Couldn't write the struct value.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -158,8 +167,8 @@ auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& by
|
|||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto struct_prop = dynamic_cast<StructProperty*>(prop.get());
|
||||
|
||||
if(!struct_prop) {
|
||||
LOG_ERROR("The property is not a valid struct property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -171,6 +180,7 @@ auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& by
|
|||
UnsignedLong dummy_bytes_written = 0;
|
||||
UnsignedLong vl_start = writer.arrayPosition();
|
||||
if(!writeStructValue(struct_prop, dummy_bytes_written, writer, serialiser)) {
|
||||
LOG_ERROR("Couldn't write the struct value.");
|
||||
return false;
|
||||
}
|
||||
bytes_written += writer.arrayPosition() - vl_start;
|
||||
|
@ -206,13 +216,14 @@ auto StructSerialiser::writeStructValue(StructProperty* prop, UnsignedLong& byte
|
|||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto struct_prop = dynamic_cast<GenericStructProperty*>(prop);
|
||||
|
||||
if(!struct_prop) {
|
||||
LOG_ERROR("The property is not a valid struct property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto& item : struct_prop->properties) {
|
||||
if(!serialiser.write(item, bytes_written, writer)) {
|
||||
LOG_ERROR("Couldn't write the struct's data.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "Vector2DPropertySerialiser.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ auto Vector2DPropertySerialiser::deserialiseProperty(Containers::StringView name
|
|||
auto prop = Containers::pointer<Vector2DStructProperty>();
|
||||
|
||||
if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read 2D vector property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,8 +38,8 @@ auto Vector2DPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop
|
|||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto vector = dynamic_cast<Vector2DStructProperty*>(prop.get());
|
||||
|
||||
if(!vector) {
|
||||
LOG_ERROR("The property is not a valid 2D vector property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../BinaryReader.h"
|
||||
#include "../BinaryWriter.h"
|
||||
#include "../../Logger/Logger.h"
|
||||
|
||||
#include "VectorPropertySerialiser.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ auto VectorPropertySerialiser::deserialiseProperty(Containers::StringView name,
|
|||
auto prop = Containers::pointer<VectorStructProperty>();
|
||||
|
||||
if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y) || !reader.readFloat(prop->z)) {
|
||||
LOG_ERROR_FORMAT("Couldn't read vector property {}'s value.", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -36,8 +38,8 @@ auto VectorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop,
|
|||
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
|
||||
{
|
||||
auto vector = dynamic_cast<VectorStructProperty*>(prop.get());
|
||||
|
||||
if(!vector) {
|
||||
LOG_ERROR("The property is not a valid vector property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include "BinaryReader.h"
|
||||
#include "BinaryWriter.h"
|
||||
#include "../Logger/Logger.h"
|
||||
|
||||
#include "UESaveFile.h"
|
||||
|
||||
|
@ -67,10 +67,13 @@ auto UESaveFile::props() -> Containers::ArrayView<UnrealPropertyBase::ptr> {
|
|||
}
|
||||
|
||||
auto UESaveFile::saveToFile() -> bool {
|
||||
LOG_INFO_FORMAT("Writing to {}.", _filepath);
|
||||
|
||||
bool temp_file = _filepath.hasSuffix(".tmp");
|
||||
|
||||
if(!temp_file && !Utility::Path::copy(_filepath, _filepath + ".bak"_s)) {
|
||||
_lastError = "Couldn't create a backup for " + _filepath;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -78,6 +81,7 @@ auto UESaveFile::saveToFile() -> bool {
|
|||
|
||||
if(!writer.open()) {
|
||||
_lastError = "Couldn't open the file for saving."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -91,13 +95,15 @@ auto UESaveFile::saveToFile() -> bool {
|
|||
!writer.writeUEString(_engineVersion.buildId))
|
||||
{
|
||||
_lastError = "Couldn't write the header."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!writer.writeUnsignedInt(_customFormatVersion) ||
|
||||
!writer.writeUnsignedInt(_customFormatData.size()))
|
||||
{
|
||||
_lastError = "Couldn't write the header."_s;
|
||||
_lastError = "Couldn't write the custom format data."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -105,13 +111,15 @@ auto UESaveFile::saveToFile() -> bool {
|
|||
if(!writer.writeStaticArray(Containers::StaticArrayView<16, const char>{_customFormatData[i].id}) ||
|
||||
!writer.writeUnsignedInt(_customFormatData[i].value))
|
||||
{
|
||||
_lastError = "Couldn't write the header."_s;
|
||||
_lastError = "Couldn't write the custom format data."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!writer.writeUEString(_saveType)) {
|
||||
_lastError = "Couldn't write the header."_s;
|
||||
_lastError = "Couldn't write the save type."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -119,11 +127,13 @@ auto UESaveFile::saveToFile() -> bool {
|
|||
UnsignedLong bytes_written = 0;
|
||||
if(!_propSerialiser->write(prop, bytes_written, writer)) {
|
||||
_lastError = "Couldn't write the property "_s + *prop->name + " to the array."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!writer.flushToFile()) {
|
||||
_lastError = "Couldn't write the property "_s + *prop->name + " to the file."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -132,13 +142,13 @@ auto UESaveFile::saveToFile() -> bool {
|
|||
|
||||
writer.closeFile();
|
||||
|
||||
if(!temp_file && !Utility::Path::copy(_filepath + ".tmp"_s, _filepath)) {
|
||||
if(!Utility::Path::copy(_filepath + ".tmp"_s, _filepath)) {
|
||||
_lastError = "Couldn't save the file properly.";
|
||||
LOG_ERROR(_lastError);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
Utility::Path::remove(_filepath + ".tmp"_s);
|
||||
}
|
||||
|
||||
Utility::Path::remove(_filepath + ".tmp"_s);
|
||||
|
||||
_noReloadAfterSave = true;
|
||||
|
||||
|
@ -146,9 +156,13 @@ auto UESaveFile::saveToFile() -> bool {
|
|||
}
|
||||
|
||||
void UESaveFile::loadData() {
|
||||
LOG_INFO_FORMAT("Reading data from {}.", _filepath);
|
||||
|
||||
_valid = false;
|
||||
|
||||
if(!Utility::Path::exists(_filepath)) {
|
||||
_lastError = "The file couldn't be found.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -156,19 +170,22 @@ void UESaveFile::loadData() {
|
|||
|
||||
if(!reader.open()) {
|
||||
_lastError = _filepath + " couldn't be opened."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
Containers::Array<char> magic;
|
||||
if(!reader.readArray(magic, 4)) {
|
||||
_lastError = "Couldn't read magic bytes in "_s + _filepath;
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
Containers::String invalid = _filepath + " isn't a valid UE4 save."_s;
|
||||
|
||||
if(std::strncmp(magic.data(), _magicBytes.data(), 4) != 0) {
|
||||
_lastError = std::move(invalid);
|
||||
_lastError = Utility::format("Magic bytes don't match. Expected {{{}, {}, {}, {}}}, got {{{}, {}, {}, {}}} instead",
|
||||
_magicBytes[0], _magicBytes[1], _magicBytes[2], _magicBytes[3],
|
||||
magic[0], magic[1], magic[2], magic[3]);
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -180,19 +197,22 @@ void UESaveFile::loadData() {
|
|||
!reader.readUnsignedInt(_engineVersion.build) ||
|
||||
!reader.readUEString(_engineVersion.buildId))
|
||||
{
|
||||
_lastError = std::move(invalid);
|
||||
_lastError = "Couldn't read version data.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!reader.readUnsignedInt(_customFormatVersion)) {
|
||||
_lastError = std::move(invalid);
|
||||
_lastError = "Couldn't read the custom format version.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
UnsignedInt custom_format_data_size = 0;
|
||||
|
||||
if(!reader.readUnsignedInt(custom_format_data_size)) {
|
||||
_lastError = std::move(invalid);
|
||||
_lastError = "Couldn't read the custom format data size.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -204,15 +224,17 @@ void UESaveFile::loadData() {
|
|||
if(!reader.readStaticArray(entry.id) ||
|
||||
!reader.readInt(entry.value))
|
||||
{
|
||||
_lastError = std::move(invalid);
|
||||
_lastError = "Couldn't read the custom format data";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
arrayAppend(_customFormatData, entry);
|
||||
_customFormatData[i] = std::move(entry);
|
||||
}
|
||||
|
||||
if(!reader.readUEString(_saveType)) {
|
||||
_lastError = std::move(invalid);
|
||||
_lastError = "Couldn't read the save type.";
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -221,8 +243,15 @@ void UESaveFile::loadData() {
|
|||
arrayAppend(_properties, std::move(prop));
|
||||
}
|
||||
|
||||
if(_properties.isEmpty()) {
|
||||
_lastError = "No properties were found."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
if(_properties.back()->name != "None"_s && _properties.back()->propertyType != "NoneProperty"_s) {
|
||||
_lastError = "Couldn't find a final NoneProperty."_s;
|
||||
LOG_ERROR(_lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
63
src/main.cpp
63
src/main.cpp
|
@ -14,45 +14,72 @@
|
|||
// 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/SaveTool.h"
|
||||
#include <SDL_messagebox.h>
|
||||
|
||||
#ifndef SAVETOOL_DEBUG_BUILD
|
||||
#include <fstream>
|
||||
#endif
|
||||
#include <cpuid.h>
|
||||
|
||||
#include <errhandlingapi.h>
|
||||
#include <synchapi.h>
|
||||
#include <winerror.h>
|
||||
|
||||
#include "Logger/MagnumLogBuffer.h"
|
||||
#include "SaveTool/SaveTool.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifndef SAVETOOL_DEBUG_BUILD
|
||||
std::ofstream output{"SaveToolLog.txt", std::ios::trunc|std::ios::out};
|
||||
MagnumLogBuffer debug_intercept_buffer{EntryType::Info};
|
||||
MagnumLogBuffer warning_intercept_buffer{EntryType::Warning};
|
||||
MagnumLogBuffer error_intercept_buffer{EntryType::Error};
|
||||
std::ostream debug_intercept_stream{&debug_intercept_buffer};
|
||||
std::ostream warning_intercept_stream{&warning_intercept_buffer};
|
||||
std::ostream error_intercept_stream{&error_intercept_buffer};
|
||||
Debug debug_intercept{&debug_intercept_stream};
|
||||
Warning warning_intercept{&warning_intercept_stream};
|
||||
Error error_intercept{&error_intercept_stream};
|
||||
|
||||
Utility::Debug d{&output};
|
||||
Utility::Warning w{&output};
|
||||
Utility::Error e{&output};
|
||||
#else
|
||||
Utility::Warning w{Utility::Debug::defaultOutput()};
|
||||
Utility::Error e{Utility::Debug::defaultOutput()};
|
||||
#endif
|
||||
logger().initialise();
|
||||
|
||||
Utility::Debug{} << "===M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION "===";
|
||||
LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION ".");
|
||||
|
||||
auto str = setlocale(LC_ALL, ".utf-8");
|
||||
if(str) {
|
||||
Containers::StringView locale{str};
|
||||
LOG_INFO_FORMAT("Current locale: {}", locale);
|
||||
|
||||
if(!locale.hasSuffix(".utf8")) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
"Your system doesn't support UTF-8.", nullptr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
auto mutex_handle = CreateMutexW(nullptr, 0, L"MassBuilderSaveTool");
|
||||
|
||||
if(!mutex_handle) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
"There was an error initialising the mutex.",nullptr);
|
||||
LOG_ERROR_FORMAT("Error initialising the instance checker. GetLastError() returned {}.", GetLastError());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
"There was an error initialising the single-instance checker.",nullptr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app",
|
||||
LOG_ERROR("Another instance of the application was detected.");
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
"There can be only one running instance of the application.",nullptr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Utility::Debug{} << "===Initialising OpenGL renderer===";
|
||||
std::uint32_t brand[12];
|
||||
|
||||
if (!__get_cpuid_max(0x80000004, nullptr)) {
|
||||
LOG_WARNING("CPUID features not supported. Can't get the current processor's model.");
|
||||
}
|
||||
else {
|
||||
__get_cpuid(0x80000002, brand+0x0, brand+0x1, brand+0x2, brand+0x3);
|
||||
__get_cpuid(0x80000003, brand+0x4, brand+0x5, brand+0x6, brand+0x7);
|
||||
__get_cpuid(0x80000004, brand+0x8, brand+0x9, brand+0xa, brand+0xb);
|
||||
LOG_INFO_FORMAT("Processor: {}", Containers::arrayCast<const char>(brand).data());
|
||||
}
|
||||
|
||||
SaveTool app({argc, argv});
|
||||
Int result = app.exec();
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 857cc7c0c93f003dbfc5fed7a87de907fffb2a1b
|
||||
Subproject commit 0bfeed061b10ea7dd37c88d9bae1824bad760f3a
|
|
@ -1 +1 @@
|
|||
Subproject commit dc4f2eac6814b37b5257d295c2838bcde95272aa
|
||||
Subproject commit 41b2e250b7e1e09c2ed057f079617e3d77393c47
|
|
@ -1 +1 @@
|
|||
Subproject commit 64db5c575d9c5536bd273a890f50777ad1ca7c13
|
||||
Subproject commit 280cbeee2789749fa9b2267ce7a07813645897de
|
|
@ -1 +1 @@
|
|||
Subproject commit f5f42f4b9a1c34512b779b2c5544ae42fdf97afa
|
||||
Subproject commit f3914e475cc8cec461ac05060470c6510ee81246
|
|
@ -1 +1 @@
|
|||
Subproject commit ddddabdccfdafffd8664fb4e29230dc4f848137e
|
||||
Subproject commit a8df192df022ed6ac447e7b7ada718c4c4824b41
|
|
@ -1 +1 @@
|
|||
Subproject commit e7c81b67ab91d5dc54c2238d9f7d9abab1a0a8c3
|
||||
Subproject commit bdc03ab23b703fcc516436d6ebcbfb6ac4484033
|
|
@ -1 +1 @@
|
|||
Subproject commit 3fc9028b5451aa95973f104d1ef2a1c0df589e64
|
||||
Subproject commit a49602987499379e4a2d155472961d99ddfc75ba
|
|
@ -1 +1 @@
|
|||
Subproject commit 323c23f4e8e7cda9a7848c03401a3ba0a1de0bd4
|
||||
Subproject commit 883bd0cb6e00172b6fca34ff8cc4bc70bc302637
|
Loading…
Reference in New Issue